Graphical SpeedBar [Vega]
NOTE: It's recommended to also include this code to eliminate frame flickers -> https://mkwii.com/showthread.php?tid=1530
Shout outs to Star & RiiDefi for Draw Text To Screen Code!
This code will output your vehicle speed view a graphical image depicting an interactive bar. The bar length will vary depending on your vehicle speed. The bar works in an exponential manner, NOT linear. The higher the speeds, the more the bar will change depending on differences at the higher speeds.
NOTE: This code makes use of the following memory addresses...
0x80001660 thru 0x80001689
0x815F0000 thru 0x815F0003
Make sure no other codes in your GCT/Cheat-Manager are using those addresses!
NTSC-U
06001670 00000010
25336420 6B6D2F68
0A000000 00000000
C200A3F0 00000007
9421FF80 BC610008
806DA358 80630044
80630000 3D808002
618C23E0 7D8803A6
4E800021 B8610008
38210080 80010014
60000000 00000000
C2009640 00000016
3D80815F 818C0000
2C0C0000 41820098
9421FF80 BC610008
3D808000 816C1660
C1AB0024 FDA06A10
FDA0681C D9AC1664
806C1668 7C671B78
7C6319D6 38800370
7C6323D6 3BA00000
2C030000 41820020
39E0007C 61901678
9DF00001 3463FFFF
4082FFF8 9BB00001
48000008 9BAC1679
38600009 388000D0
38A00001 61861670
3FE08002 63EC1DF0
7D8803A6 4E800021
63EC1DD0 7D8803A6
4E800021 B8610008
38210080 81830000
60000000 00000000
C27E4C9C 00000007
3D80809C 818CD110
818C0020 540B103A
7D8C582E 818C0010
818C0010 3D608000
918B1660 3D80815F
93EC0000 807FEE20
60000000 00000000
PAL
06001670 00000010
25336420 6B6D2F68
0A000000 00000000
C200A430 00000007
9421FF80 BC610008
806DA360 80630044
80630000 3D808002
618C2480 7D8803A6
4E800021 B8610008
38210080 80010014
60000000 00000000
C2009680 00000016
3D80815F 818C0000
2C0C0000 41820098
9421FF80 BC610008
3D808000 816C1660
C1AB0024 FDA06A10
FDA0681C D9AC1664
806C1668 7C671B78
7C6319D6 38800370
7C6323D6 3BA00000
2C030000 41820020
39E0007C 61901678
9DF00001 3463FFFF
4082FFF8 9BB00001
48000008 9BAC1679
38600009 388000D0
38A00001 61861670
3FE08002 63EC1E90
7D8803A6 4E800021
63EC1E70 7D8803A6
4E800021 B8610008
38210080 81830000
60000000 00000000
C27EEFAC 00000007
3D80809C 818C18F8
818C0020 540B103A
7D8C582E 818C0010
818C0010 3D608000
918B1660 3D80815F
93EC0000 807F3618
60000000 00000000
NTSC-J
06001670 00000010
25336420 6B6D2F68
0A000000 00000000
C200A38C 00000007
9421FF80 BC610008
806DA360 80630044
80630000 3D808002
618C23A0 7D8803A6
4E800021 B8610008
38210080 80010014
60000000 00000000
C20095DC 00000016
3D80815F 818C0000
2C0C0000 41820098
9421FF80 BC610008
3D808000 816C1660
C1AB0024 FDA06A10
FDA0681C D9AC1664
806C1668 7C671B78
7C6319D6 38800370
7C6323D6 3BA00000
2C030000 41820020
39E0007C 61901678
9DF00001 3463FFFF
4082FFF8 9BB00001
48000008 9BAC1679
38600009 388000D0
38A00001 61861670
3FE08002 63EC1DB0
7D8803A6 4E800021
63EC1D90 7D8803A6
4E800021 B8610008
38210080 81830000
60000000 00000000
C27EE618 00000007
3D80809C 818C0958
818C0020 540B103A
7D8C582E 818C0010
818C0010 3D608000
918B1660 3D80815F
93EC0000 807F2678
60000000 00000000
NTSC-K
06001670 00000010
25336420 6B6D2F68
0A000000 00000000
C200A538 00000007
9421FF80 BC610008
806DA380 80630044
80630000 3D808002
618C24E0 7D8803A6
4E800021 B8610008
38210080 80010014
60000000 00000000
C2009788 00000016
3D80815F 818C0000
2C0C0000 41820098
9421FF80 BC610008
3D808000 816C1660
C1AB0024 FDA06A10
FDA0681C D9AC1664
806C1668 7C671B78
7C6319D6 38800370
7C6323D6 3BA00000
2C030000 41820020
39E0007C 61901678
9DF00001 3463FFFF
4082FFF8 9BB00001
48000008 9BAC1679
38600009 388000D0
38A00001 61861670
3FE08002 63EC1EF0
7D8803A6 4E800021
63EC1ED0 7D8803A6
4E800021 B8610008
38210080 81830000
60000000 00000000
C27DD36C 00000007
3D80809B 818CFF38
818C0020 540B103A
7D8C582E 818C0010
818C0010 3D608000
918B1660 3D80815F
93EC0000 807F1C58
60000000 00000000
List of Sources:
1st ASM (When Game loads StaticR.rel, Get Render Mode & Call Direct Print Setup Frame Buffer)
#~~~~~~~~~~~~~~~~#
# START ASSEMBLY #
#~~~~~~~~~~~~~~~~#
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Register Notes: #
# No need to backup r0 or LR #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
#~~~~~~~~~~~~~~~~~~~~~#
# Macros & Statements #
#~~~~~~~~~~~~~~~~~~~~~#
.macro push_stack
stwu r1, -0x80 (r1)
stmw r3, 0x8 (r1)
.endm
.macro pop_stack
lmw r3, 0x8 (r1)
addi r1, r1, 0x80
.endm
.macro call_link address
lis r12, \address@h
ori r12, r12, \address@l
mtlr r12
blrl
.endm
.macro default_instruction
lwz r0, 0x0014 (r1)
.endm
.set region, '' #Must set region value, or else source will not compile
.if (region == 'E' || region == 'e') # RMCE
.set nw4r_db_DirectPrint_SetupFB, 0x800223E0
.elseif (region == 'P' || region == 'p') # RMCP
.set nw4r_db_DirectPrint_SetupFB, 0x80022480
.elseif (region == 'J' || region == 'j') # RMCJ
.set nw4r_db_DirectPrint_SetupFB, 0x800223A0
.elseif (region == 'K' || region == 'k') # RMCK
.set nw4r_db_DirectPrint_SetupFB, 0x800224E0
.else # Invalid Region
.abort
.endif
#~~~~~~~~~~~~~~~~~~~~~~~#
# Start Register Safety #
#~~~~~~~~~~~~~~~~~~~~~~~#
push_stack
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Get Render Mode (RKSystem->mpVideo()->pRenderMode) #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
.if (region == 'E' || region == 'e') # RMCE
lwz r3, -0x5CA8(r13)
.elseif (region == 'P' || region == 'p') # RMCP
lwz r3, -0x5CA0(r13)
.elseif (region == 'J' || region == 'j') # RMCJ
lwz r3, -0x5CA0(r13)
.elseif (region == 'K' || region == 'k') # RMCK
lwz r3, -0x5C80(r13)
.endif
lwz r3, 0x0044(r3)
lwz r3, 0x0 (r3)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Call nw4r::db: DirectPrint_SetupFB #
# r3 = Render Mode #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
call_link nw4r_db_DirectPrint_SetupFB
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# End Register Safety; Default Instruction #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
pop_stack
default_instruction
#
#~~~~~~~~~~~~~~#
# END ASSEMBLY #
#~~~~~~~~~~~~~~#
==========
2nd ASM (Convert Speed Float, Calc Bar Length, Write Bar Amtn to Gecko ASCii String, Draw it to Screen)
#~~~~~~~~~~~~~~~~#
# START ASSEMBLY #
#~~~~~~~~~~~~~~~~#
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Register Notes: #
# No need to backup r0 or LR #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Mem Address Notes: #
# 0x80001660 = Speed Float Word #
# 0x80001664 = Converted Hex 1st Word of Double Word Float #
# 0x80001668 = Converted Hex 2nd Word of Double Word Float (Speed in Hex) #
# 0x80001670 = Gecko String ASCii, r6 Arg for Direct Print #
# 0x80001678 = 0x0A ASCii byte for Entering into new Line for Speed Bar #
# 0x80001679 = Start of Bar #
# 0x80001688 = Last Bar byte at its highest amount possible #
# 0x80001689 = Last Null of Bar byte string at its highest amount possible #
# 0x815F0000 = "Status Word" If not zero, Draw Code will execute, auto clears after every race #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
#~~~~~~~~~~~~~~~~~~~~~#
# Macros & Statements #
#~~~~~~~~~~~~~~~~~~~~~#
.macro push_stack
stwu r1, -0x80 (r1)
stmw r3, 0x8 (r1)
.endm
.macro pop_stack
lmw r3, 0x8 (r1)
addi r1, r1, 0x80
.endm
.macro call_nw4r address
ori r12, r31, \address@l
mtlr r12
blrl
.endm
.macro default_instruction
lwz r12, 0x0 (r3)
.endm
.set region, '' #Must set region value, or else source will not compile
.if (region == 'E' || region == 'e') # RMCE
.set nw4r_db_DirectPrint_Printf, 0x1DF0
.set nw4r_db_DirectPrint_StoreCache, 0x1DD0
.elseif (region == 'P' || region == 'p') # RMCP
.set nw4r_db_DirectPrint_Printf, 0x1E90
.set nw4r_db_DirectPrint_StoreCache, 0x1E70
.elseif (region == 'J' || region == 'j') # RMCJ
.set nw4r_db_DirectPrint_Printf, 0x1DB0
.set nw4r_db_DirectPrint_StoreCache, 0x1D90
.elseif (region == 'K' || region == 'k') # RMCK
.set nw4r_db_DirectPrint_Printf, 0x1EF0
.set nw4r_db_DirectPrint_StoreCache, 0x1ED0
.else # Invalid Region
.err
.endif
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Check to See if a Race is Active: #
# Load Status Word from Mem 81 #
# If not zero, we know to execute the Draw Code #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
lis r12, 0x815F
lwz r12, 0x0 (r12)
cmpwi r12, 0x0
beq- dont_execute
#~~~~~~~~~~~~~~~~~~~~~~~#
# Start Register Safety #
#~~~~~~~~~~~~~~~~~~~~~~~#
push_stack
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Pointer Load, Float Load, Conversion, Storage #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
lis r12, 0x8000 #Set 1st Half Address of Exception Vector Area
lwz r11, 0x1660 (r12) #Load Pointer
lfs f13, 0x0024 (r11) #Load the Speed Float from the Pointer
fabs f13, f13 #Convert Float to handle negative values approriately; aka fix Reverse Speed reading
fctiw f13, f13 #Convert Float to Word Integer, use Standard Rounding; aka fix Funky Kong 96/97 issue
stfd f13, 0x1664 (r12) #Store the whole converted float to 0x80001664 (using this instead of a li then stfiwx will save us one instruction total)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Calculate Bar Amount to Write to Memory #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
lwz r3, 0x1668 (r12) #Load Vehicle Hex Speed Amnt into r3
mr r7, r3 #Copy hex value to r7 for DirectPrintf's 1st format Arg
mullw r3, r3, r3 #Raise Vehicle Hex Speed to the Power of 2
li r4, 0x370 #Load 0x370 into r4 for upcoming divw instruction
divw r3, r3, r4 #Divide Vehicle Hex Speed by 0x370
li r29, 0x0 #Clear out r29 for Bar End or else Bar will never decrease when speed decreases
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Check if Bar Amnt is Zero. If so, we don't need to Write the Bar, skip upcoming Loop #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
cmpwi r3, 0x0
beq- skip_loop
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Set ASCii Symbol (divider), Setup Loop Store Address #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
li r15, 0x7C
ori r16, r12, 0x1678
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Loop #
# Write Approriate Bar Amount Based on finalized Hex Speed Amnt #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
the_loop:
stbu r15, 0x1 (r16)
subic. r3, r3, 1
bne+ the_loop
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Store NULL (Bar End) after last Byte of Bar #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
stb r29, 0x1 (r16)
b draw_toscreen
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Store NULL to what would be first Bar Byte #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
skip_loop:
stb r29, 0x1679 (r12)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# DirectPrint Printf & Store Cache #
# Purpose: Draw on Screen #
# r3 = X coordinate (starts far left) #
# r4 = Y coordinate (starts at very top) #
# r5 = 0 No Wrap; 1 Wrap #
# r6 = Address Pointer to String that will be Drawn on Screen #
# r7 thru r10 printf format args #
# f1 thru f13 printf float format args #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
draw_toscreen:
li r3, 0x9
li r4, 0xD0
li r5, 0x1
ori r6, r12, 0x1670 #Gecko String Write Address
lis r31, 0x8002 #For nw4r macro
call_nw4r nw4r_db_DirectPrint_Printf #r7 already set from earlier
call_nw4r nw4r_db_DirectPrint_StoreCache
#~~~~~~~~~~~~~~~~~~~~~#
# End Register Safety #
#~~~~~~~~~~~~~~~~~~~~~#
pop_stack
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# dont_execute label; Default Instruction #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
dont_execute:
default_instruction
#
#~~~~~~~~~~~~~~#
# END ASSEMBLY #
#~~~~~~~~~~~~~~#
==========
3rd ASM (Store Memory Pointer)
#~~~~~~~~~~~~~~~~#
# START ASSEMBLY #
#~~~~~~~~~~~~~~~~#
#~~~~~~~~~~~~~~~~~~~~~#
# Macros & Statements #
#~~~~~~~~~~~~~~~~~~~~~#
.set region, '' #Must set region value, or else source will not compile
.if (region == 'E' || region == 'e') # RMCE
.macro default_instruction
lwz r3, -0x11E0 (r31)
.endm
.macro set_playerbase
lis r12, 0x809C
lwz r12, 0xFFFFD110 (r12)
.endm
.elseif (region == 'P' || region == 'p') # RMCP
.macro default_instruction
lwz r3, 0x3618 (r31)
.endm
.macro set_playerbase
lis r12, 0x809C
lwz r12, 0x18F8 (r12)
.endm
.elseif (region == 'J' || region == 'j') # RMCJ
.macro default_instruction
lwz r3, 0x2678 (r31)
.endm
.macro set_playerbase
lis r12, 0x809C
lwz r12, 0x0958 (r12)
.endm
.elseif (region == 'K' || region == 'k') # RMCK
.macro default_instruction
lwz r3, 0x1C58 (r31)
.endm
.macro set_playerbase
lis r12, 0x809B
lwz r12, 0xFFFFFF38 (r12)
.endm
.else # Invalid Region
.abort
.endif
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Set Region-Specific Player-Base #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
set_playerbase
#~~~~~~~~~~~~~~~~~~~~~~~#
# Pointer Level Loading #
#~~~~~~~~~~~~~~~~~~~~~~~#
lwz r12, 0x0020 (r12) #Load Word from 1st Level Pointer
slwi r11, r0, 2 #Shift the bits of r0 by 2 to the left, result in r11. This is the same as multiplying by 0x4. r0 is current player slot.
lwzx r12, r12, r11 #Load into 2nd Level Pointer
lwz r12, 0x0010(r12) #Load into 3rd Level Pointer
lwz r12, 0x0010(r12) #Load into 4th Level Pointer
#~~~~~~~~~~~~~~~~~~~~~#
# Store Final Pointer #
#~~~~~~~~~~~~~~~~~~~~~#
lis r11, 0x8000 #Set up 1st Half Address of Exception Vector Area
stw r12, 0x1660 (r11) #Store Pointer Address to 0x80001660
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Set Status Word in Mem81 for Draw Text ASM #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
lis r12, 0x815F #Set up 1st Half Address of dynmaic Mem81
stw r31, 0x0 (r12) #Store r31 to 0x815F0000, we simply need a value not zero to be stored here. This will make the Speed Bar ASM only trigger during races.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Default Instruction; Region-Specific #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
default_instruction
#
#~~~~~~~~~~~~~~#
# END ASSEMBLY #
#~~~~~~~~~~~~~~#
Code creator: Vega
Code credits: Star & RiiDefi (Draw Text To Screen Code); Chadderz (source fixes to Mdmwii's OG Speed source), Mdmwii (original Speed-O-Meter)
NOTE: It's recommended to also include this code to eliminate frame flickers -> https://mkwii.com/showthread.php?tid=1530
Shout outs to Star & RiiDefi for Draw Text To Screen Code!
This code will output your vehicle speed view a graphical image depicting an interactive bar. The bar length will vary depending on your vehicle speed. The bar works in an exponential manner, NOT linear. The higher the speeds, the more the bar will change depending on differences at the higher speeds.
NOTE: This code makes use of the following memory addresses...
0x80001660 thru 0x80001689
0x815F0000 thru 0x815F0003
Make sure no other codes in your GCT/Cheat-Manager are using those addresses!
NTSC-U
06001670 00000010
25336420 6B6D2F68
0A000000 00000000
C200A3F0 00000007
9421FF80 BC610008
806DA358 80630044
80630000 3D808002
618C23E0 7D8803A6
4E800021 B8610008
38210080 80010014
60000000 00000000
C2009640 00000016
3D80815F 818C0000
2C0C0000 41820098
9421FF80 BC610008
3D808000 816C1660
C1AB0024 FDA06A10
FDA0681C D9AC1664
806C1668 7C671B78
7C6319D6 38800370
7C6323D6 3BA00000
2C030000 41820020
39E0007C 61901678
9DF00001 3463FFFF
4082FFF8 9BB00001
48000008 9BAC1679
38600009 388000D0
38A00001 61861670
3FE08002 63EC1DF0
7D8803A6 4E800021
63EC1DD0 7D8803A6
4E800021 B8610008
38210080 81830000
60000000 00000000
C27E4C9C 00000007
3D80809C 818CD110
818C0020 540B103A
7D8C582E 818C0010
818C0010 3D608000
918B1660 3D80815F
93EC0000 807FEE20
60000000 00000000
PAL
06001670 00000010
25336420 6B6D2F68
0A000000 00000000
C200A430 00000007
9421FF80 BC610008
806DA360 80630044
80630000 3D808002
618C2480 7D8803A6
4E800021 B8610008
38210080 80010014
60000000 00000000
C2009680 00000016
3D80815F 818C0000
2C0C0000 41820098
9421FF80 BC610008
3D808000 816C1660
C1AB0024 FDA06A10
FDA0681C D9AC1664
806C1668 7C671B78
7C6319D6 38800370
7C6323D6 3BA00000
2C030000 41820020
39E0007C 61901678
9DF00001 3463FFFF
4082FFF8 9BB00001
48000008 9BAC1679
38600009 388000D0
38A00001 61861670
3FE08002 63EC1E90
7D8803A6 4E800021
63EC1E70 7D8803A6
4E800021 B8610008
38210080 81830000
60000000 00000000
C27EEFAC 00000007
3D80809C 818C18F8
818C0020 540B103A
7D8C582E 818C0010
818C0010 3D608000
918B1660 3D80815F
93EC0000 807F3618
60000000 00000000
NTSC-J
06001670 00000010
25336420 6B6D2F68
0A000000 00000000
C200A38C 00000007
9421FF80 BC610008
806DA360 80630044
80630000 3D808002
618C23A0 7D8803A6
4E800021 B8610008
38210080 80010014
60000000 00000000
C20095DC 00000016
3D80815F 818C0000
2C0C0000 41820098
9421FF80 BC610008
3D808000 816C1660
C1AB0024 FDA06A10
FDA0681C D9AC1664
806C1668 7C671B78
7C6319D6 38800370
7C6323D6 3BA00000
2C030000 41820020
39E0007C 61901678
9DF00001 3463FFFF
4082FFF8 9BB00001
48000008 9BAC1679
38600009 388000D0
38A00001 61861670
3FE08002 63EC1DB0
7D8803A6 4E800021
63EC1D90 7D8803A6
4E800021 B8610008
38210080 81830000
60000000 00000000
C27EE618 00000007
3D80809C 818C0958
818C0020 540B103A
7D8C582E 818C0010
818C0010 3D608000
918B1660 3D80815F
93EC0000 807F2678
60000000 00000000
NTSC-K
06001670 00000010
25336420 6B6D2F68
0A000000 00000000
C200A538 00000007
9421FF80 BC610008
806DA380 80630044
80630000 3D808002
618C24E0 7D8803A6
4E800021 B8610008
38210080 80010014
60000000 00000000
C2009788 00000016
3D80815F 818C0000
2C0C0000 41820098
9421FF80 BC610008
3D808000 816C1660
C1AB0024 FDA06A10
FDA0681C D9AC1664
806C1668 7C671B78
7C6319D6 38800370
7C6323D6 3BA00000
2C030000 41820020
39E0007C 61901678
9DF00001 3463FFFF
4082FFF8 9BB00001
48000008 9BAC1679
38600009 388000D0
38A00001 61861670
3FE08002 63EC1EF0
7D8803A6 4E800021
63EC1ED0 7D8803A6
4E800021 B8610008
38210080 81830000
60000000 00000000
C27DD36C 00000007
3D80809B 818CFF38
818C0020 540B103A
7D8C582E 818C0010
818C0010 3D608000
918B1660 3D80815F
93EC0000 807F1C58
60000000 00000000
List of Sources:
1st ASM (When Game loads StaticR.rel, Get Render Mode & Call Direct Print Setup Frame Buffer)
#~~~~~~~~~~~~~~~~#
# START ASSEMBLY #
#~~~~~~~~~~~~~~~~#
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Register Notes: #
# No need to backup r0 or LR #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
#~~~~~~~~~~~~~~~~~~~~~#
# Macros & Statements #
#~~~~~~~~~~~~~~~~~~~~~#
.macro push_stack
stwu r1, -0x80 (r1)
stmw r3, 0x8 (r1)
.endm
.macro pop_stack
lmw r3, 0x8 (r1)
addi r1, r1, 0x80
.endm
.macro call_link address
lis r12, \address@h
ori r12, r12, \address@l
mtlr r12
blrl
.endm
.macro default_instruction
lwz r0, 0x0014 (r1)
.endm
.set region, '' #Must set region value, or else source will not compile
.if (region == 'E' || region == 'e') # RMCE
.set nw4r_db_DirectPrint_SetupFB, 0x800223E0
.elseif (region == 'P' || region == 'p') # RMCP
.set nw4r_db_DirectPrint_SetupFB, 0x80022480
.elseif (region == 'J' || region == 'j') # RMCJ
.set nw4r_db_DirectPrint_SetupFB, 0x800223A0
.elseif (region == 'K' || region == 'k') # RMCK
.set nw4r_db_DirectPrint_SetupFB, 0x800224E0
.else # Invalid Region
.abort
.endif
#~~~~~~~~~~~~~~~~~~~~~~~#
# Start Register Safety #
#~~~~~~~~~~~~~~~~~~~~~~~#
push_stack
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Get Render Mode (RKSystem->mpVideo()->pRenderMode) #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
.if (region == 'E' || region == 'e') # RMCE
lwz r3, -0x5CA8(r13)
.elseif (region == 'P' || region == 'p') # RMCP
lwz r3, -0x5CA0(r13)
.elseif (region == 'J' || region == 'j') # RMCJ
lwz r3, -0x5CA0(r13)
.elseif (region == 'K' || region == 'k') # RMCK
lwz r3, -0x5C80(r13)
.endif
lwz r3, 0x0044(r3)
lwz r3, 0x0 (r3)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Call nw4r::db: DirectPrint_SetupFB #
# r3 = Render Mode #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
call_link nw4r_db_DirectPrint_SetupFB
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# End Register Safety; Default Instruction #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
pop_stack
default_instruction
#
#~~~~~~~~~~~~~~#
# END ASSEMBLY #
#~~~~~~~~~~~~~~#
==========
2nd ASM (Convert Speed Float, Calc Bar Length, Write Bar Amtn to Gecko ASCii String, Draw it to Screen)
#~~~~~~~~~~~~~~~~#
# START ASSEMBLY #
#~~~~~~~~~~~~~~~~#
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Register Notes: #
# No need to backup r0 or LR #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Mem Address Notes: #
# 0x80001660 = Speed Float Word #
# 0x80001664 = Converted Hex 1st Word of Double Word Float #
# 0x80001668 = Converted Hex 2nd Word of Double Word Float (Speed in Hex) #
# 0x80001670 = Gecko String ASCii, r6 Arg for Direct Print #
# 0x80001678 = 0x0A ASCii byte for Entering into new Line for Speed Bar #
# 0x80001679 = Start of Bar #
# 0x80001688 = Last Bar byte at its highest amount possible #
# 0x80001689 = Last Null of Bar byte string at its highest amount possible #
# 0x815F0000 = "Status Word" If not zero, Draw Code will execute, auto clears after every race #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
#~~~~~~~~~~~~~~~~~~~~~#
# Macros & Statements #
#~~~~~~~~~~~~~~~~~~~~~#
.macro push_stack
stwu r1, -0x80 (r1)
stmw r3, 0x8 (r1)
.endm
.macro pop_stack
lmw r3, 0x8 (r1)
addi r1, r1, 0x80
.endm
.macro call_nw4r address
ori r12, r31, \address@l
mtlr r12
blrl
.endm
.macro default_instruction
lwz r12, 0x0 (r3)
.endm
.set region, '' #Must set region value, or else source will not compile
.if (region == 'E' || region == 'e') # RMCE
.set nw4r_db_DirectPrint_Printf, 0x1DF0
.set nw4r_db_DirectPrint_StoreCache, 0x1DD0
.elseif (region == 'P' || region == 'p') # RMCP
.set nw4r_db_DirectPrint_Printf, 0x1E90
.set nw4r_db_DirectPrint_StoreCache, 0x1E70
.elseif (region == 'J' || region == 'j') # RMCJ
.set nw4r_db_DirectPrint_Printf, 0x1DB0
.set nw4r_db_DirectPrint_StoreCache, 0x1D90
.elseif (region == 'K' || region == 'k') # RMCK
.set nw4r_db_DirectPrint_Printf, 0x1EF0
.set nw4r_db_DirectPrint_StoreCache, 0x1ED0
.else # Invalid Region
.err
.endif
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Check to See if a Race is Active: #
# Load Status Word from Mem 81 #
# If not zero, we know to execute the Draw Code #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
lis r12, 0x815F
lwz r12, 0x0 (r12)
cmpwi r12, 0x0
beq- dont_execute
#~~~~~~~~~~~~~~~~~~~~~~~#
# Start Register Safety #
#~~~~~~~~~~~~~~~~~~~~~~~#
push_stack
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Pointer Load, Float Load, Conversion, Storage #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
lis r12, 0x8000 #Set 1st Half Address of Exception Vector Area
lwz r11, 0x1660 (r12) #Load Pointer
lfs f13, 0x0024 (r11) #Load the Speed Float from the Pointer
fabs f13, f13 #Convert Float to handle negative values approriately; aka fix Reverse Speed reading
fctiw f13, f13 #Convert Float to Word Integer, use Standard Rounding; aka fix Funky Kong 96/97 issue
stfd f13, 0x1664 (r12) #Store the whole converted float to 0x80001664 (using this instead of a li then stfiwx will save us one instruction total)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Calculate Bar Amount to Write to Memory #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
lwz r3, 0x1668 (r12) #Load Vehicle Hex Speed Amnt into r3
mr r7, r3 #Copy hex value to r7 for DirectPrintf's 1st format Arg
mullw r3, r3, r3 #Raise Vehicle Hex Speed to the Power of 2
li r4, 0x370 #Load 0x370 into r4 for upcoming divw instruction
divw r3, r3, r4 #Divide Vehicle Hex Speed by 0x370
li r29, 0x0 #Clear out r29 for Bar End or else Bar will never decrease when speed decreases
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Check if Bar Amnt is Zero. If so, we don't need to Write the Bar, skip upcoming Loop #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
cmpwi r3, 0x0
beq- skip_loop
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Set ASCii Symbol (divider), Setup Loop Store Address #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
li r15, 0x7C
ori r16, r12, 0x1678
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Loop #
# Write Approriate Bar Amount Based on finalized Hex Speed Amnt #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
the_loop:
stbu r15, 0x1 (r16)
subic. r3, r3, 1
bne+ the_loop
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Store NULL (Bar End) after last Byte of Bar #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
stb r29, 0x1 (r16)
b draw_toscreen
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Store NULL to what would be first Bar Byte #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
skip_loop:
stb r29, 0x1679 (r12)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# DirectPrint Printf & Store Cache #
# Purpose: Draw on Screen #
# r3 = X coordinate (starts far left) #
# r4 = Y coordinate (starts at very top) #
# r5 = 0 No Wrap; 1 Wrap #
# r6 = Address Pointer to String that will be Drawn on Screen #
# r7 thru r10 printf format args #
# f1 thru f13 printf float format args #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
draw_toscreen:
li r3, 0x9
li r4, 0xD0
li r5, 0x1
ori r6, r12, 0x1670 #Gecko String Write Address
lis r31, 0x8002 #For nw4r macro
call_nw4r nw4r_db_DirectPrint_Printf #r7 already set from earlier
call_nw4r nw4r_db_DirectPrint_StoreCache
#~~~~~~~~~~~~~~~~~~~~~#
# End Register Safety #
#~~~~~~~~~~~~~~~~~~~~~#
pop_stack
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# dont_execute label; Default Instruction #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
dont_execute:
default_instruction
#
#~~~~~~~~~~~~~~#
# END ASSEMBLY #
#~~~~~~~~~~~~~~#
==========
3rd ASM (Store Memory Pointer)
#~~~~~~~~~~~~~~~~#
# START ASSEMBLY #
#~~~~~~~~~~~~~~~~#
#~~~~~~~~~~~~~~~~~~~~~#
# Macros & Statements #
#~~~~~~~~~~~~~~~~~~~~~#
.set region, '' #Must set region value, or else source will not compile
.if (region == 'E' || region == 'e') # RMCE
.macro default_instruction
lwz r3, -0x11E0 (r31)
.endm
.macro set_playerbase
lis r12, 0x809C
lwz r12, 0xFFFFD110 (r12)
.endm
.elseif (region == 'P' || region == 'p') # RMCP
.macro default_instruction
lwz r3, 0x3618 (r31)
.endm
.macro set_playerbase
lis r12, 0x809C
lwz r12, 0x18F8 (r12)
.endm
.elseif (region == 'J' || region == 'j') # RMCJ
.macro default_instruction
lwz r3, 0x2678 (r31)
.endm
.macro set_playerbase
lis r12, 0x809C
lwz r12, 0x0958 (r12)
.endm
.elseif (region == 'K' || region == 'k') # RMCK
.macro default_instruction
lwz r3, 0x1C58 (r31)
.endm
.macro set_playerbase
lis r12, 0x809B
lwz r12, 0xFFFFFF38 (r12)
.endm
.else # Invalid Region
.abort
.endif
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Set Region-Specific Player-Base #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
set_playerbase
#~~~~~~~~~~~~~~~~~~~~~~~#
# Pointer Level Loading #
#~~~~~~~~~~~~~~~~~~~~~~~#
lwz r12, 0x0020 (r12) #Load Word from 1st Level Pointer
slwi r11, r0, 2 #Shift the bits of r0 by 2 to the left, result in r11. This is the same as multiplying by 0x4. r0 is current player slot.
lwzx r12, r12, r11 #Load into 2nd Level Pointer
lwz r12, 0x0010(r12) #Load into 3rd Level Pointer
lwz r12, 0x0010(r12) #Load into 4th Level Pointer
#~~~~~~~~~~~~~~~~~~~~~#
# Store Final Pointer #
#~~~~~~~~~~~~~~~~~~~~~#
lis r11, 0x8000 #Set up 1st Half Address of Exception Vector Area
stw r12, 0x1660 (r11) #Store Pointer Address to 0x80001660
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Set Status Word in Mem81 for Draw Text ASM #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
lis r12, 0x815F #Set up 1st Half Address of dynmaic Mem81
stw r31, 0x0 (r12) #Store r31 to 0x815F0000, we simply need a value not zero to be stored here. This will make the Speed Bar ASM only trigger during races.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
# Default Instruction; Region-Specific #
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
default_instruction
#
#~~~~~~~~~~~~~~#
# END ASSEMBLY #
#~~~~~~~~~~~~~~#
Code creator: Vega
Code credits: Star & RiiDefi (Draw Text To Screen Code); Chadderz (source fixes to Mdmwii's OG Speed source), Mdmwii (original Speed-O-Meter)