Skip to content
Nico edited this page Jan 21, 2021 · 5 revisions

Soon

Info1 Inlined:

--------------------------------------
N64 Protocol and hardware observations
--------------------------------------

The existing resources on the 'net weren't quite enough
to write a successful emulation of an N64 controller.
The following are some random notes from my own reverse-engineering.

Command summary:
  00: identify, returns 05 00 02 with no controller pak,
                05 00 01 with a memory or rumble pak

  01: status, returns a 24-bit button and axis status packet

  02: read from expansion bus, followed by 2 bytes (always 80 01)
      from the N64.

  03: write to expansion bus. Followed by 2 bytes (always 80 01)
      then 32 bytes of data.

Expansion bus pinout, numbered according to the silkscreen markings
on the controller's PCB:

  Pin   Name      Note
-----------------------
  1     Ground
  2     A14       (1)
  3     A12
  4     A7
  5     A6
  6     A5
  7     A4
  8     A3
  9     A2
  10    A1
  11    A0
  12    D0
  13    D1
  14    Detect    (2)
  15    3.3v
  16    D2

  17    Ground
  18    Unknown 1 (3)
  19    A15
  20    /WE
  21    A13
  22    A8
  23    A9
  24    A11
  25    /OE
  26    A10
  27    D7
  28    D6
  29    D5
  30    D4
  31    3.3v
  32    D3

 Note 1:
   With 15 address lines, that gives standard memory paks a 32k capacity.
   The Nyko Hyperpak tested has 128k of SRAM, but includes a switch that
   selects between four 32k banks.

 Note 2:
   There's a 3k resistor between detect and +3.3v in the controller pak.
   When this signal is high, the controller thinks a controller pak is
   present- it will attempt to read and write from it, and it acknowledges
   to the N64 with an 0xE1. When this signal is low, the controller doesn't
   attempt any writes or reads and it acknowledges with 0x1E.

 Note 3:
   Possibly a chip select


Majora's Mask observed startup sequence

	1. N64 sends identify command, 0x00
	   Controller responds with 05 00 02 with no controller pak,
	   05 00 01 with memory pak.

	2. N64 reads controller status (0x01)

	3. Another 0x00 command, with the same response

	4. N64 sends:
	   03 80 01 followed by 32 bytes, all FE
	   Response: 0xE1 with memory pak, 0x1E without.
	   The response has no stop bit! instead, the data line
	   goes low for 2us immediately after the last data bit.

Majora's Mask title screen polling sequence

	1. Status request (0x01)

	2. Identification command (0x00)

	3. Another 0x03 command as described above

	4. N64 sends:
	   02 80 01


Hooked up the controller pak to the logic analyzer. Looks like 0x03 is
indeed a write, and the 32 bytes are all data directed at the controller
pak bus. The two preceeding bytes probably indicate the address. At least
one of the unknown pins in the pinout are probably high bits in the address,
not used by the RAM but maybe used by the rumble pak or other controller paks.

Saved trace "WR1" with rumble pak attached, during game play in Super Smash.
Can't make out a whole lot, but it's clear how the 0x03 packets work.

Recorded an odd packet at the title screen of Majora's Mask- controller slot
was empty, but I was pulling the DETECT pin high to get it to talk anyway.
N64 sent a write (03 80 01) then 32 0x80 bytes. Controller responded with
0xB8 (!). Saved to "no_pak".

The address does auto-increment during a 32-byte write to the controller pak.
With the write command 03 80 01, addresses start at zero.

Memory pak reads, observed in Bomberman 64:
N64 sent: 02 00 35
Read 32 bytes starting at 0x0020.
Trace saved as "RD1".

Command    Start address
-------------------------
02 80 01   0000
02 00 35   0020
02 01 16   0100
02 01 23   0120
02 01 49   0140
02 01 7C   0160
02 01 9D   0180

Rumble pak writes:
To switch on the rumble pak motor, the N64 sends:
03 C0 1B 01 01 01 ...
This writes 01 to addresses starting at 0x4000.
To turn the motor back off, the N64 sends:
03 C0 1B 00 00 00 ...

So, it looks like the rumble pak is a simple memory
mapped latch accessed using the same protocol as the
memory pak.

But how does the N64 know which peripheral is attached?
Initialization, from Super Smash Bros:

N64 sends 03 80 01 then 32 0xFE bytes, controller writes
them starting at address 0x0000.
N64 then starts a read using 02 80 01.
This seems to verify that the address bytes in the read
and write commands use the same format!

The rumble pak returns all 0x80 from this read, while
a memory pak will return 0x00. Why doesn't the memory
pak return 0xFE?

It looks like the status byte returned from a controller
pak write is actually a checksum. This would make a lot of
sense, as when saving a game data integrity is important.
When generating write commands with a constant 80 01 address
word and changing data, the checksum byte changes. Several
messages were tested consisting of 80, 01, 31 zero bytes,
then a different byte. The byte and the message's checksums
are recorded below:

byte   message checksum
-------------------------
00     FF  1111 1111 <-- seems to indicate that the checksum is only
01     7A  0111 1010     over the 32 bytes, not the address word
02     70  0111 0000
03     F5  1111 0101
05     E1  1110 0001
06     14  0001 0100
07     6E  0110 1110
C5     EE  1110 1110
FF     72  0111 0010

01     7A  0111 1010
02     70  0111 0000
04     64  0110 0100
08     4C  0100 1100
10     1C  0001 1100



- Memory map

0x0000 - 0x7FFF :  SRAM, on the memory pak

0x8000          :  Identification/initialization.
                   Write all 0xFEs to it for initialization, then
                   read back 0x00 on the memory pak or 0x80 on the rumble pak.

0xC000          :  Motor control latch (low bit) for the rumble pak


- Figured out a lot more about the detection sequence.. see bus.py for
  an implementation of it, with comments

Poked around in the saved game format a little.. bomberman's saved characters
are easy to modify, it uses a very simple checksum and each character attribute
is just a byte.

The game names used in the controller pak data screen (press start while turning
on the N64) aren't ASCII, but the character set is simple enough. Here's a partial
translation table:

"ABCDEFGHIJKLMNOPQRSTUVWXYZ "
"\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123\x0F"

Each game gets a 32-byte table of contents entry, starting at 0x300. The second
half of each entry is reserved for a 16-byte zero-padded game name.

Info 2 Inlined:

N64 Controller Protocol

​ Jul 26, 2016

Recently Google announced that they were closing their project hosting site Google code. Back in the days before I understood how to properly use version control I thought it'd be a nice place to store the source code and documentation for a N64 controller I was making. There's a tool for exporting to github but the controller source is an old version and not worth keeping, however The documentation is a bit of a different matter. There's not too many sources out there which describe the N64 controller protocol including the memory commands properly and I thought it might be a good idea to rewrite my documentation so it could be accessible.

Physical Layer

N64 controllers connect to the console with a three core cable including 3.3V power, ground, and a bidirectional data line (the internet is lousy with pinouts). The single data line is tied high on the N64 side (and optionally at the controller) and the controller or console can drive the line low. Its important to remember that the data line is never driven high but instead when both the console and controller are in high impedance mode, the line is pulled high. This arrangement is commonly known as an open collector output.

A 0 bit is sent by pulling the data line low for 3µs then letting the line go high for 1µs. Conversely, a 1 bit is sent by pulling the line low for 1µs and letting it go high for 3µs. The console will end communication with a stop bit identical to a 1 bit, however the controller's stop bit has the data line pulled low for 2µs and then let's the bus go high.

Data layer

All communication between the console and controller begins with a command byte sent by the console followed by any required axillary information. The controller then responds within 20ms with the requested data and both sets of communication are terminated with a stop bit. The command bytes are:

0x00

The controller responds with three bytes to identify itself. For a normal controller the first two bytes are always 0x05, 0x00. The last byte is:

  • 0x01 if there is a controller pack plugged in.
  • 0x02 if there is no controller pack.
  • 0x04 if the previous controller read/write address CRC showed an error.

0x01

The controller responds with four bytes of button and joystick data

Byte Data
1 A, B, Z, Start, D up, D down, D left, D right
2 Joy reset, 0, L, R, C up, C down, C left, C right
3 Joystick x ordinate
4 Joystick y ordinate

The joystick ordinates are given as 2s complement values between -0x50 and 0x50. Providing values outside of this range can lead to strange behaviour, such as mario changing directions in super mario 64.

0xFF

From the N64 patents this seems to be a reset command which recalibrates the controller's joystick. The official N64 controller uses optical rotary encoders which give a quadrature encoded output, similar to old mouses. In this arrangement there's no way to know which position the joystick starts in, only how it's changed over time. This is why if you hold the joystick to one side as the console powers on, the joystick will have a constant drift. The 0xFF command resets the controllers internal registers for the joystick ordinates to centred. The controller also responds with the same output as the 0x00 command.

0x02

A read command followed by an additional two bytes from the N64 which give an address and a CRC for that address in the last 5 bits of the second byte. The controller responds with a 32 byte data block beginning at the given address, followed by a CRC of the data block. Details of the data CRC are given below.

0x03

A write command with the same addressing as the previous command. The address bytes are followed by a 32 byte block of data and the controller responds with a data CRC.

Controller memory space

The two bye address from the controller gives an address space between 0x0000 - 0xFFFF, or 64kB of space. The lowest 5 bits for controller read and writes are implicitly all 0 and the 5 bit address CRC calculated by the console is given in their place. All read and writes are therefore aligned on 32 byte boundaries. Memory packs, which all have 32kB of space, are mapped between 0x0000 - 0x7FFF. The rumble packs I've seen simply latch the last data value in a write to any address between 0x8000 and 0xFFFF, though I've read resources which give the rumble pack an address of 0xC000. If the lsb of the latched value is 0 the rumble pack is turned off and vice versa. To test if a rumble or memory pack is attached, some games do a test write to a rumble pack address and read back to see if the value has been latched. If it wasn't, the attached peripheral is likely the memory pack. Other peripherals do exist, one popular example being the transfer pak. From the limited experience I've had with one (borrowing from a friend), and a look at some N64 development manuals, it seems like the transfer pak just allows access to the gameboy cartridge memory which maps ROM and RAM within the address space of the N64 controller. Importantly, the transfer pak only supports MBC1, MBC3, and MBC5 within the functions of the N64 development kit, but there's no reason one can't use the given commands to access an MBC2 game that I'm aware of. You'd need to track down what addresses the transfer pak uses to switch power on and off, which I never managed to find.

CRC

To find the Data CRC I sent a write to a controller with the last bit set, and it was then simple to find the CRC polynomial:

Polynomial length: 8 bits with an implicit 9th 1 bit

Polynomial value: 0x85

Initial Value: 0x00

Post XOR: 0x00 with a pack connected, 0xFF without

As with all CRC calculations, the message is augmented with a 0x00 byte.In summary it's a fairly simple 8 bit CRC that wasn't too difficult to work out however calculation of the CRC using the poly is quite computationally expensive, especially when running on a microcontroller. It requires a XOR, shift and most crucially knowledge of the next 7 bits which means it's difficult to calculate while the message is being received/sent. While I didn't do measurements to determine how quickly the CRC response was needed from the controller, I had some problems with the latency of an implementation that computed the CRC after the message had been received.

For embedded systems with limited power, an alternative approach to CRC calculation is to precompute the effect each byte will have on the CRC to create a 256 entry lookup table. Then, each byte of the CRC only requires a table lookup and a XOR with the CRC. The lookup table was calculated with this short program and resulted in this table. The given table is already formatted as an assembly lookup table to go in the 16 bit program memory space of a pic18 micro, but it can be easily reformatted.

The same approach was reused to find the address CRC:

Polynomial length: 5 bits with an implicit 6th 1 bit

Polynomial value: 0x15

Initial Value: 0x00

Post XOR: 0x00

Hardware

I made two N64 controllers of my own with this challenge. The first was for a contest on the BenHeck forums, and I thought it'd be fun to come out with something so small people would wonder how it was done. I omitted the Dpad and L button and made use of a 4 way tact switch for the C buttons and had just enough space to cram the electronics into the smallest rectangular jiffy box from Jaycar.

A rather blurry shot of the tiny controller I built, around a 16f PIC.

A rather blurry shot of the tiny controller I built, around a 16f PIC.

The second controller replaces the board in an official N64 controller and uses a pot joystick from a gamecube controller. It has a built in memory pack and rumble pack which you can switch between with a button combination. While it's the same form factor, it's amazing the difference having a proper joystick makes. A picture would show you what looked like a stock controller, so here's a video of the innards working before being prettied up and stuffed in the controller shell.

Clone this wiki locally