r64 is an abbreviation of Rust 64
which is an abbreviation of Rust Nintendo 64
which loosely translates to Nintendo 64 of Rust
which can be interpreted as Nintendo 64 emulator written using the Rust programming language.
.
I wanted to brush up on my Rust skills, and I figured that a good way to do that would be to start a project that relied heavily on the use of Rust's systems programming concepts. Rather than putting Rust on hardware (which I will do at some point in the near future), I decided to use Rust to make hardware. I've written a 6502 emulator in the context of the NES in the past, so I wanted something a bit more challenging this time around. N64 played a huge role in my childhood, so it seemed like a logical choice.
This emulator has no goals for dynamic recompilation, cycle accuracy, or constraints for timing. Hence, it is a verbose and unoptimized Nintendo 64 emulator. My goal is, quite simply, to boot Super Mario 64 and see a wireframe of the rendered polygons - no textures, shading, or fancyness. If I get to this point, I may throw in audio emulation, and from there work my way towards a functional RCP.
Currently, the VR4300i CPU has been structured and can print the instructions that it is executing. Some of the core opcodes are implemented (special
, load/store
, jump/branch
), others have yet to be looked at. If the CPU encounters an opcode that it can't execute, the unimplemented!()
macro will be called, subsequently panicking the program.
Backends for nop
RCP components are currently being implemented. Step 1 is to get the CPU executing properly, then more work will be done on graphics.
Component | Status |
---|---|
CPU0 | In Progress |
TLB | Not Implemented |
CP0 | In Progress |
CP1/FPU | Not Implemented |
RCP | In Progress |
Audio | Not Implemented |
PIF | Not Implemented |
Build the emulator as you would any other program in Rust.
cargo build
An N64 ROM (.n64) and PIF ROM (.bin) are required to boot the emulator. The PIF ROM image can be obtained by searching for the SHA1 of the IPL 1.0 NTSC PIF ROM image, which is 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd
.
cargo run /path/to/rom.n64 /path/to/pifrom.bin
Once the program is launched, it will enter a REPL. The following commands can be provided after prompt.
Command | Description |
---|---|
"step" / "s" | Performs a step into the next CPU instruction and executes it. |
"print" / "p" | Prints the contents of the GPRs, CP0's registers, as well as the special registers. |
Pressing enter is equivelent to the step
command.
Example output is below.
0x9fc00000: (0x3c093400) lui t1, r0, 0x3400
>
0x9fc00004: (0x40896000) mtc0 t4, t1, a0
>
0x9fc00008: (0x3c090006) lui t1, r0, 0x6
> p
00 (r0): 0x0000000000000000 01 (at): 0x0000000000000000
02 (v0): 0x0000000000000000 03 (v1): 0x0000000000000000
04 (a0): 0x0000000000000000 05 (a1): 0x0000000000000000
06 (a2): 0x0000000000000000 07 (a3): 0x0000000000000000
08 (t0): 0x0000000000000000 09 (t1): 0x0000000000060000
10 (t2): 0x0000000000000000 11 (t3): 0x0000000000000000
12 (t4): 0x0000000000000000 13 (t5): 0x0000000000000000
14 (t6): 0x0000000000000000 15 (t7): 0x0000000000000000
16 (s0): 0x0000000000000000 17 (s1): 0x0000000000000000
18 (s2): 0x0000000000000000 19 (s3): 0x0000000000000000
20 (s4): 0x0000000000000000 21 (s5): 0x0000000000000000
22 (s6): 0x0000000000000000 23 (s7): 0x0000000000000000
24 (t8): 0x0000000000000000 25 (t9): 0x0000000000000000
26 (k0): 0x0000000000000000 27 (k1): 0x0000000000000000
28 (gp): 0x0000000000000000 29 (sp): 0x0000000000000000
30 (s8): 0x0000000000000000 31 (ra): 0x0000000000000000
>
0x9fc0000c: (0x3529e463) ori t1, t1, 0xe463
>
0x9fc00010: (0x40898000) mtc0 s0, t1, a0
>
0x9fc00014: (0x3c08a404) lui t0, r0, 0xa404
>
0x9fc00018: (0x8d080010) lw t0, 16(t0)
> p
00 (r0): 0x0000000000000000 01 (at): 0x0000000000000000
02 (v0): 0x0000000000000000 03 (v1): 0x0000000000000000
04 (a0): 0x0000000000000000 05 (a1): 0x0000000000000000
06 (a2): 0x0000000000000000 07 (a3): 0x0000000000000000
08 (t0): 0x0000000000000000 09 (t1): 0x000000000006E463
10 (t2): 0x0000000000000000 11 (t3): 0x0000000000000000
12 (t4): 0x0000000000000000 13 (t5): 0x0000000000000000
14 (t6): 0x0000000000000000 15 (t7): 0x0000000000000000
16 (s0): 0x0000000000000000 17 (s1): 0x0000000000000000
18 (s2): 0x0000000000000000 19 (s3): 0x0000000000000000
20 (s4): 0x0000000000000000 21 (s5): 0x0000000000000000
22 (s6): 0x0000000000000000 23 (s7): 0x0000000000000000
24 (t8): 0x0000000000000000 25 (t9): 0x0000000000000000
26 (k0): 0x0000000000000000 27 (k1): 0x0000000000000000
28 (gp): 0x0000000000000000 29 (sp): 0x0000000000000000
30 (s8): 0x0000000000000000 31 (ra): 0x0000000000000000
>
0x9fc0001c: (0x31080001) andi t0, t0, 0x1
>
0x9fc00020: (0x5100fffd) beql r0, t0, -3
>
0xffffffff9fc00014: (0x3c08a404) lui t0, r0, 0xa404
>
0xffffffff9fc00018: (0x8d080010) lw t0, 16(t0)
>
0xffffffff9fc0001c: (0x31080001) andi t0, t0, 0x1
>
0xffffffff9fc00020: (0x5100fffd) beql r0, t0, -3
vr4300i - Invaluable information about the main CPU.
cen64 - Great resource for the N64 memory map structure as well as the implementation of RCP behaviors.
rustendo64 - Good resource for the basic structure of an N64 emulator written in Rust.
n64dev - Lots of (not very organized) documentation about various aspects of the N64 hardware.
n64js - Awesome resource for stepping through N64 code and viewing change in system state.
This repo exists under the MIT license.
If you find this project useful, let me know via email or Twitter! (links on my GitHub account page)