Due to recent demand on Discord and DMs, here is a customizable XYZ/XZ configuration of Version 1.1
tt = Text Config (00 = omit km/h text, 20 = km/h text to right, 0A = km/h text underneath)
X,x, Y, and W values are left-half single precision float values. (i.e. 3F80 = 3F800000 = 1)
Here's the updated source for the Last ASM. Other ASM source's can be found in the OP.
Code:
#Address Ports
#Inject at 80597820 (PAL)
#Inject at 80590FFC (NTSC-U)
#Inject at 805971A0 (NTSC-J)
#Inject at 80585878 (NTSC-K)
#Region Compilation Setting & Assembler Directives
.set region, ''
.if (region == 'p')
.set ptr_raceData, 0x809bd728
.set ptr_menuData, 0x809c1e38
.set swprintf, 0x80017814
.set ptr_unk, 0x80386000
.set ScreenElement_setTextSrc, 0x805cdd00
.elseif (region == 'e')
.set ptr_raceData, 0x809b8f68
.set ptr_menuData, 0x809bd508
.set swprintf, 0x80016cb4
.set ptr_unk, 0x80381c80
.set ScreenElement_setTextSrc, 0x805c11e0
.elseif (region == 'j')
.set ptr_raceData, 0x809bc788
.set ptr_menuData, 0x809c0e98
.set swprintf, 0x80017738
.set ptr_unk, 0x80385980
.set ScreenElement_setTextSrc, 0x805cd5dc
.elseif (region == 'k')
.set ptr_raceData, 0x809abd68
.set ptr_menuData, 0x809b0478
.set swprintf, 0x8001787c
.set ptr_unk, 0x80374020
.set ScreenElement_setTextSrc, 0x805bbcc0
.else
.err
.endif
#Register Safety Notes
#r0, r3 thru r12 safe, r26 thru r31 safe
#f0 thru 13, and f29 safe
#LR and CTR safe
#Set User config registers
li r12, 0 #0 = XYZ Speed, #1 = XY Speed
lis r26, 0x42F0 #Set widescreen X position
lis r27, 0x41F0 #Set 4:3 X position
lis r30, 0xC35C #Set Y position
lis r31, 0x3F80 #Set Scale
#Get Slot that code is currently hooked on. Then get your Slot value. Make sure they are equal
lwz r3, 0 (r28)
lwz r3, 0 (r3)
lbz r3, 0x10 (r3)
lis r4, ptr_raceData@ha
lwz r4, ptr_raceData@l (r4)
lbz r4, 0x0B84 (r4)
cmpw r3, r4 #If slots don't match, skip code
bne+ end
#Check if a single player gameplay screen is active, keep pointer in r29 for later use
lis r3, ptr_menuData@ha
lwz r3, ptr_menuData@l (r3)
lwz r3, 0 (r3)
lwz r29, 0x38 (r3) # grand prix
cmpwi r29, 0
bne- found
lwz r29, 0x3C (r3) # time trial
cmpwi r29, 0
bne- found
lwz r29, 0x40 (r3) # 1 player offline vs
cmpwi r29, 0
bne- found
lwz r29, 0x50 (r3) # 1 player battle
cmpwi r29, 0
bne- found
lwz r29, 0x108 (r3) # 1 player ww/regional vs
cmpwi r29, 0
bne- found
lwz r29, 0x110 (r3) # 1 player friend room vs
cmpwi r29, 0
beq- end
#Single Player screen is active!
#Create the string via BL Trick
found:
bl format
.short 0x001A, 0x0800, 0x0001, 0x0030 #Bmg escape sequence for yellow text
.short 0x0025, 0x002E, 0x0031, 0x0066 #16-bit ascii for %.1f
.short 0x000A, 0x006B, 0x006D, 0x002F #16-bit ascii for \nkm/h
.short 0x0068 #16-bit ascii for \nkm/h
.short 0
.align 2
format:
#Load current and previous XYZ values
lwz r3, 0 (r28)
lwz r3, 0x8 (r3)
lwz r3, 0x90 (r3) #r3 is base pointer nearby prev XYZ
lwz r4, 0x4 (r3) #r4 is base pointer nearby current XYZ
psq_l f3, 0x18 (r3), 0, 0 #old XY
lfs f4, 0x20 (r3) #old Z
psq_l f1, 0x68 (r4), 0, 0 #new XY
lfs f2, 0x70 (r4) #new Z
#Check for XYZ vs XZ
cmpwi r12, 0
beq- skip_xz_adjustments
#Null out both Y Values
#Merge old X with new Y values back together in their respective original FPRs
fsubs f0, f0, f0 #Subtract FPR by itself to make it Null
ps_merge01 f1, f1, f0 #Merge in nulled out Y value f1 ps1
ps_merge01 f3, f3, f0 #Merge in nulled out Y value to f3 ps1
#Do the formula: sqrt{[(x2 - x1)^2] + [(y2 - y1)^2] + [(z2 - z1)^2]}
#X2 = f1 ps0
#X1 = f3 ps0
#Y2 = f1 ps1
#Y1 = f3 ps1
#Z2 = f2 ps0
#Z1 = f2 ps0
skip_xz_adjustments:
ps_sub f1, f1, f3
fsubs f2, f2, f4
ps_mul f1, f1, f1
ps_sum0 f1, f1, f1, f1
fmadds f1, f2, f2, f1
frsqrte f1, f1
fres f1, f1
#Allocate stack memory, 0x100 should be enough, lol
#0x8 needed by swprintf
#rest for the formatted string
#Set non-float args of swprintf, call it
#f1 speed
#r3 where to dump (dest addr)
#r4 length
#r5 source addr (already set)
addi sp, sp, -0x100
crset 4*cr1+eq #Makes swprintf execute faster by providing an accurate branch route regarding float processing
addi r3, sp, 0x8
li r4, 0x100
mflr r5
lis r12, swprintf@h
ori r12, r12, swprintf@l
mtctr r12
bctrl
#Set r5 arg for last func call near end, and preset r4 for ascii conversion loop
addi r5, sp, 0x8 #Beginning of the formatted string
addi r4, sp, 0xE #We do not want to process the bmg escape sequence
#The Timer uses special 16-bit ascii symbols in place of typical 16-bit ascii symbols for digits, dashes, and dots. Adjust the symbols.
loop:
lhzu r3, 0x2 (r4)
cmpwi r3, 0
beq- out
cmpwi r3, 0x2D
beq- dash
cmpwi r3, 0x2E
beq- dot
cmplwi r3, 0x30
blt- loop
cmplwi r3, 0x39
bgt- loop
addi r3, r3, 0x2430 #Update digits (0 thru 9)
b write_new_ascii
dash:
li r3, 0x246D
b write_new_ascii
dot:
li r3, 0x246B
write_new_ascii:
sth r3, 0 (r4)
b loop
#Ascii symbol reformatting complete
out:
lwz r3, 0x5C (r29)
#Find out if widescreen or not, apply X and Y screen pos
lis r6, ptr_unk@ha
lwz r6, ptr_unk@l (r6)
lwz r6, 0x58 (r6)
cmpwi r6, 1
stw r30, 0x50 (r3) #Write Y pos
stw r31, 0x58 (r3) #Write Scale, fyi stmw is slow
beq+ widescreen
stw r27, 0x4C (r3) #Write 4:3 X pos
b make_visible
widescreen:
stw r26, 0x4C (r3) #Write 16:9 X pos
#Make the element visible, align text to right, call func to display it on screen
make_visible:
li r4, 0
stb r4, 0x80 (r3)
li r6, 2 #Side note: Use 0 to align left, be sure to update your X measurements correctly (neg/inverse them)
lwz r3, 0x114 (r3)
lwz r7, 0 (r3)
stb r6, 0x100 (r7)
lis r12, ScreenElement_setTextSrc@h
ori r12, r12, ScreenElement_setTextSrc@l
mtctr r12
bctrl
#Recover sp
addi sp, sp, 0x100
#The end (original instruction)
end:
addi r11, sp, 0x100