Setting up n64chain and armips
Follow these steps to install n64chain and armips and prepare them for command line usage.
- Download n64chain from https://github.com/tj90241/n64chain/releases/ and extract all the files from the 'tools' folder to to C:\n64chain\ (Your directory should look like this)
- Download the latest armips build from https://buildbot.orphis.net/armips/ and extract armips.exe to C:\n64chain\bin\armips.exe
- Download n64crc from https://shygoo.net/storage/n64crc.exe and move it to C:\n64chain\bin\n64crc.exe
- Add n64chain, n64crc, and armips to the system path by doing the following:
- Click the Windows start button, type "environment variables" in the search bar and open "Edit environment variables for your account"
- You should see a variable called "PATH" under user variables, click it and click "Edit..."
- (Win7) Add
;C:\n64chain\bin
to the end of the variable value and click OK
(Win10) Click "New", addC:\n64chain\bin
and click OK
- Click the Windows start button, type "environment variables" in the search bar and open "Edit environment variables for your account"
After the path variable has been updated, you may need to restart your computer for the change to take effect.
Simple Hello World
Here is a minimalist Hello World example that overwrites one of the game's useless functions with code of our own. Create a folder containing the following files, and also drop an original SM64 ROM named "Super Mario 64 (U) [!].z64" in the folder.
You can download this archive which contains everything except the ROM.
/*
Here we create declarations for a few functions and variables in SM64 so they can be referenced from our C code.
Later we will tell armips their addresses by defining labels for them in sm64.asm.
*/
extern void PrintStr(int x, int y, const char* text); // 0x802D6554
extern short g_MarioCoins; // 0x8033B218
extern short g_MarioHealth; // 0x8033B21E
sm64.asm
// Tell armips' linker where the assets we declared in sm64.h are located
.definelabel PrintStr, 0x802D6554
.definelabel g_MarioCoins, 0x8033B218
.definelabel g_MarioHealth, 0x8033B21E
hello_world.c
#include "sm64.h"
// This hello_world function will overwrite one of SM64's useless functions, which happens to be called once every frame
void hello_world(void)
{
// Print HELLO WORLD to the screen
PrintStr(20, 20, "HELLO WORLD");
// Also kill Mario if he gets 5 or more coins, for science
if(g_MarioCoins >= 5)
{
g_MarioHealth = 0;
}
}
Note: armips' linker exposes symbols both ways, so if we wanted to make our own call to this function from custom assembly code we could just use jal hello_world
.
.n64 // Let armips know we're coding for the N64 architecture
.open "Super Mario 64 (U) [!].z64", "Super Mario 64 (U) [!].mod.z64", 0 // Open the ROM file
.include "sm64.asm" // Include sm64.asm to tell armips' linker where to find the game's function(s)
.headersize 0x80245000 // Set the displacement between ROM and RAM addresses
.org 0x802CB1C0 // Set the origin to the RAM address of the useless debug function
.area 0xA4, 0 // Define an area the size of the useless function to ensure we can't overwrite anything else
.importobj "hello_world.o" // Import and link the compiled C object, overwriting the useless function with our new code
.endarea
.close // Close the ROM file
build.bat
mips64-elf-gcc -Wall -O1 -mtune=vr4300 -march=vr4300 -mabi=32 -fomit-frame-pointer -G0 -c hello_world.c
armips main.asm
n64crc "Super Mario 64 (U) [!].mod.z64"
After the files are prepared, run build.bat. This should build a new ROM file called "Super Mario 64 (U) [!].mod.z64" which contains the Hello World program. If you run the ROM and go to the castle grounds, you should see this:
Spoiler alert: Mario does die if he grabs 5 coins.
For reference, here's the assembly and data n64chain generates. Note that armips' linker will put the data sections (in this case, the "HELLO WORLD" text) immediately after the code.