ASM Resources and Discussion
Hello SMWC. It's apparent that many people on here are quite new to or don't know where to get started with assembly hacking, so this thread is here to serve as a resource for learning and understanding N64 ASM code.
Assembly or ASM is the low level code that the central processing unit reads and executes to make all the magic happen, and it's something that you should be somewhat familiar with in order to make edits to the way a game (namely SM64) works.
An excerpt from the function that prints HUD elements.
The code above is probably rather daunting to those who are new to ASM (it was to me when I started), but with a bit of patience and some memorization, you could be coding like a b0ss in no time. The first thing you should know is that the emulator of choice for viewing and testing ASM code is Nemu64, for its wonderful debugging capabilities. Nemu64 is not fond of VL-Tone's extended rom for one reason or another, so one idea here would be to do your assembly editing/testing with an original SM64 rom and then port your finished code over to your project rom.
I'll be compiling a list of tutorials/references/software that should aid in N64 ASM hacking here. If you have anything that you think should be added to the list, let me know.
Tutorials:
R4300i tutorial by Tarek
Simple debugging demonstration with Nemu64 by yoshielectron
LemASM demonstration by Skelux
MIPS Instruction Reference from University of Idaho
Assemblers:
B64si Beta
Command line assembler for ram/rom, comes with notepad++ syntax highlighter
By shyguyhex - Official thread
CajeASM Stable
Command line assembler, comes with a frontend and notepad++ syntax highlighter
By Tarek701 - Official thread
LemASM - GUI assembler by Lemmy, old but commonly used and gets the job done
Renegade64 - by Viper187
sgASM w2 - by shyguyhex
Other ASM related tools:
jalFinder - by shyguyhex (cmd line utility that scans rom for JALs to provided functions, probably only useful under special circumstances)
ASM address calculator - by shyguyhex
ASM Tips
Tip 1: When coding in ASM, always remember the "negative rule". If an immediate value is greater than 0x7FFF, then the value is negative!
If we want to load 32767 (decimal) to T0, we would first believe that we have to write:
LUI T0, 0x8001
But this is false. As 0x8001 is over 0x7FFF, we actually load -32767 into T0, which we didn't want! To solve this, you simply multiply 0x8001 with 0xFFFF and we get 0x7FFF. This value is not over 0x7FFF and so we now have 32767. So:
LUI T0, 0x7FFF
is correct.
Tip 2: The above rule also applies to load/stores. Remember, that each load/store aligns a 16-bit value to 32-bit! So, if the value is negative, it subtracts one from the upper half!
Let's say we want to load a value from 0x8033B218. It's the current coin amount and a halfword value. Your first thought would be most likely:
However, the above would actually load the value from 0x8032B218 and not the expected 0x8033B218. So, why's that? The instruction LUI simply loads 0x8033 to the upper half of T0; T0 = 0x80330000. LH however loads the lower half, but needs to shift it to the lower half obviously. As 0xB218 is over 0x7FFF, it's negative. Now, doing a shift to left means that MIPS still needs to keep the negative value and this means that the result would be 0xFFFFB218. This negative value is then added to 0x80330000, resulting in: 0x8032B218.
So, to solve this little problem we simply add 1 to the upper half "0x8033". So, we get 0x8034. So, later the game would subtract 1 and successfully loads the value from 0x8033B218.
Tip 3: The delay slot below Branches and Jump instructions is executed BEFORE the jump/branch instruction. Sometimes the delay slot can be useful. If there's nothing important to put in there, always remember to write a NOP!
To show off an example:
JAL 0x802D48D4
ORI A0, R0, 0x000A
This would first do ORI and then a JAL. Sometimes this can save a lot of code;
could be shortened to:
Hello SMWC. It's apparent that many people on here are quite new to or don't know where to get started with assembly hacking, so this thread is here to serve as a resource for learning and understanding N64 ASM code.
Assembly or ASM is the low level code that the central processing unit reads and executes to make all the magic happen, and it's something that you should be somewhat familiar with in order to make edits to the way a game (namely SM64) works.
An excerpt from the function that prints HUD elements.
The code above is probably rather daunting to those who are new to ASM (it was to me when I started), but with a bit of patience and some memorization, you could be coding like a b0ss in no time. The first thing you should know is that the emulator of choice for viewing and testing ASM code is Nemu64, for its wonderful debugging capabilities. Nemu64 is not fond of VL-Tone's extended rom for one reason or another, so one idea here would be to do your assembly editing/testing with an original SM64 rom and then port your finished code over to your project rom.
I'll be compiling a list of tutorials/references/software that should aid in N64 ASM hacking here. If you have anything that you think should be added to the list, let me know.
Tutorials:
R4300i tutorial by Tarek
Simple debugging demonstration with Nemu64 by yoshielectron
LemASM demonstration by Skelux
MIPS Instruction Reference from University of Idaho
Assemblers:
B64si Beta
Command line assembler for ram/rom, comes with notepad++ syntax highlighter
By shyguyhex - Official thread
CajeASM Stable
Command line assembler, comes with a frontend and notepad++ syntax highlighter
By Tarek701 - Official thread
LemASM - GUI assembler by Lemmy, old but commonly used and gets the job done
Renegade64 - by Viper187
sgASM w2 - by shyguyhex
Other ASM related tools:
jalFinder - by shyguyhex (cmd line utility that scans rom for JALs to provided functions, probably only useful under special circumstances)
ASM address calculator - by shyguyhex
ASM Tips
Tip 1: When coding in ASM, always remember the "negative rule". If an immediate value is greater than 0x7FFF, then the value is negative!
If we want to load 32767 (decimal) to T0, we would first believe that we have to write:
LUI T0, 0x8001
But this is false. As 0x8001 is over 0x7FFF, we actually load -32767 into T0, which we didn't want! To solve this, you simply multiply 0x8001 with 0xFFFF and we get 0x7FFF. This value is not over 0x7FFF and so we now have 32767. So:
LUI T0, 0x7FFF
is correct.
Tip 2: The above rule also applies to load/stores. Remember, that each load/store aligns a 16-bit value to 32-bit! So, if the value is negative, it subtracts one from the upper half!
Let's say we want to load a value from 0x8033B218. It's the current coin amount and a halfword value. Your first thought would be most likely:
Code
LUI T0, 0x8033 LH T1, 0xB218(T0)
However, the above would actually load the value from 0x8032B218 and not the expected 0x8033B218. So, why's that? The instruction LUI simply loads 0x8033 to the upper half of T0; T0 = 0x80330000. LH however loads the lower half, but needs to shift it to the lower half obviously. As 0xB218 is over 0x7FFF, it's negative. Now, doing a shift to left means that MIPS still needs to keep the negative value and this means that the result would be 0xFFFFB218. This negative value is then added to 0x80330000, resulting in: 0x8032B218.
So, to solve this little problem we simply add 1 to the upper half "0x8033". So, we get 0x8034. So, later the game would subtract 1 and successfully loads the value from 0x8033B218.
Tip 3: The delay slot below Branches and Jump instructions is executed BEFORE the jump/branch instruction. Sometimes the delay slot can be useful. If there's nothing important to put in there, always remember to write a NOP!
To show off an example:
JAL 0x802D48D4
ORI A0, R0, 0x000A
This would first do ORI and then a JAL. Sometimes this can save a lot of code;
Code
LUI A0, 0x254D ORI A0, 0x44A5 LUI A1, 0x254A ORI A1, 0x222A JAL 0x802D48D4 NOP
could be shortened to:
Code
LUI A0, 0x254D LUI A1, 0x254A ORI A0, 0x44A5 JAL 0x802D48D4 ORI A1, A1, 0x254A