Ok, so what am I working on. Part two of my N64 modernisation project
is to arrange it so that USB controllers can be used with the Nintendo 64.
The reasoning here is pretty simple. N64 controllers have a design flaw. I
don't fully understand it, but the gist is that there's a magic powder inside
the analog sticks that gives them their "springiness" and makes them return to
centre. As the controllers wear, the powder escapes and it gets to the point
where the sticks won't return to centre anymore as well as losing their
sensitivity. Mine have held up pretty well, mostly because I've gone to great
pains to take care of them, but they're fifteen years old now and they're
starting to show it.
Obviously these controllers aren't manufactured anymore. Its not enough to buy
used ones, for obvious reasons, and new ones are difficult to come by. Not
impossible - I've seen them on eBay and in shops like Gametraders and Cash
Converters, and I guess I wouldn't need to buy many of them, but still, they
aren't exactly cheap or plentiful.
A better option is USB gamepads. As you'd expect from any PC peripheral,
they're common as dirt and the good ones (eg Logitech or XBox) are
comfortable, sturdy and responsive. So my thought has been to arrange it so
that these sticks can be used with the N64.
First stop: Google. There's lots of projects where people have interfaced N64
controllers to something else
interfaced similar controllers
the N64. As far as I've been able to tell though, nobody has ever got a USB
stick going on a N64. Great, new territory - seems I can't avoid it.
I've spent the last couple of weeks researching and thinking and while I
haven't yet done any testing with real hardware, I think I have a rough
concept for how an interface might work. There's three aspects to it:
- Acting as a USB host controller and HID class implementation.
- Translating USB gamepad data into equivalent N64 button/position data
- Speaking the proprietary N64 controller protocol.
The translation is fairly straight forward. The gamepads I'm interested in
(I'll be using a Logitech Dual Action
pad for my testing) have (at least) two sticks, a direction pad, four buttons
and two shoulder buttons. There's enough here to map to the N64 layout, which
is one stick, a direction pad, two buttons (A and B), a second directional pad
(C) and two shoulder buttons. The left shoulder maps to the Z trigger or the
real left shoulder, as they were never used together on the N64 due to the
structure of the controller. The second stick on the Logitech pad will map to
the C buttons, with some threshold to determine if the analog stick is
considered "pushed" or not.
The USB side is interesting. Its pretty easy to build a AVR-based USB device.
V-USB is a very good software stack to
turn an AVR into a USB device controller, or you can use one of the numerous
chips from FTDI. For a USB host however, the
options are far less compelling. As far as I'm able to tell, V-USB does not
implement a USB host controller at all. I had intended to use the FTDI Vinculum VDIP1,
but as I mentioned previously, the cost
of shipping is prohibitive. After some more searching yesterday I found
a software USB host controller implemented by a student project team at
Cornell. I think some combination of their code and the V-USB code should be
enough to implement a minimal host controller and HID class, which is all I
On the other side is the N64 controller protocol. Although its proprietary,
its long been studied and is pretty well understood. The most useful sources
of information have been tzanger's n64dev page
and Micah Dowty's
Its a command based protocol. The N64 sends a command, and the controller
sends the response. There's no provision for the controller to initiate a
data send - the N64 regularly polls the controller by sending a "get status"
command, to which the controller response by sending a data packet containing
the current state of the buttons and stick. There's also commands in there ask
the controller if its there and what peripherals it has attached (such as a
rumble or memory pak), as well as reading and writing to the memory card. Its
pretty simple really, which is good - I like simple.
The connection to the N64 has three lines - +3.3V, ground and data. Obviously
the power lines play no part in the communication. All that happens on the
The most difficult thing about the data protocol itself is its strict timing
requirements, as it typical of a serial protocol without a seperate clock. The
line begins in a high (logical 1) state, held there by a pull-up resistor in
the controller itself (the line is never explicitly driven high by either
A single byte consists of eight data bits and one stop bit. A single bit is 4
microseconds wide. To start the bit, the sender pulls the line low for 1us.
The next two microseconds are either high or low, depending on the value of
the bit. The final microsecond is high, then it goes again. After all eight
bits are sent, the final 4us are all high to signal the end of the byte. See
tzanger's page - it has some diagrams that
make it easier to follow.
What this means is that however I implement this I need to be able to sample
or transition the data line every microsecond. At 16MHz, that means I need to
do something every sixteen cycles. Most instructions on the AVR take a single
cycle to execute, so there's plenty of time to do things in between, but
because I need to be able to respond to the N64 sending data within 2us, its
pretty much impossible to run the USB host out of the same AVR.
So my interface has two AVRs - one doing the N64 comms, the other managing
USB. This complicates things as now some mechanism is required for the two
AVRs to communicate with each other.
This is the bit I'm not quite sure about. I originally thought to have the AVR
ports tied together such that the USB AVR could just chuck the current state
on the port and the N64 AVR could read it whenever it wanted. This is no good
though because the entire controller state is 32 bits wide - sixteen for the
button state, eight for the analog stick X axis and eight more for the Y axis.
I don't really have the bandwidth available to do it that way, not even with
the larger AVRs, which would be overkill in every other way anyway.
I've been thinking about perhaps using eight lines and sending the data a byte
at a time, but at that point I've now got the two AVRs needing to coordinate
communication when they could both be interrupted at any moment, breaking the
whole thing. It might work if I allowed the transfer to be interrupted and in
that case the N64 AVR will just use the last button state, but then this means
that the USB AVR would have to be constantly streaming the current state
rather than just sending updates when transitions occur. If it didn't then a
state transition could be lost if the transfer is interrupted.
There's always the option of putting four eight-bit latches in between the two
AVRs and storing the state there, as they can effectively be thought of as
memories with seperate read and write channels. This however means slower
access (external memories access instructions take two cycles in most cases
instead of one) which might present timing problems, as well as requiring more
I need to study the AVR datasheets to figure out if any of the peripherals it
comes with can help me out. I'm sure a simple solution will present itself, I
just have to find it. Fortunately the need for it is quite a way off. The USB
and N64 comms need to be developed first, and they need to be done in
isolation to ensure they work correctly.
So that's where I'm at. So far I'm just getting my development environment
setup. This week I've built myself an AVR programmer which is working nicely,
so next I need to write a few basic programs and make sure my laptop is setup
properly and I know what I'm doing. Then the real work can begin :)