Monday, May 5, 2014

NES Dual Port RAM Interface

UPDATE : Thanks for the great response - I'll be posting more videos throughout the week @batslyadams - I'd love to hear your ideas for ways to enhance games! Looking to livestream some development / testing this Thursday, 5/6/14.


I've been writing a game recently and was looking for a way to live debug on real hardware. The original solution was to fire variable information out the second controller port to serial every NMI but I decided to to take a shot at a new PCB for the console I've been dreaming about for a while.


The board routes the left port of the dual port RAM (Cypress CY7C136) to the DIP footprint on the NES and the right port to an AVR (ATMEGA164), this allows me to read and write any location at runtime without bus conflicts. Control is provided through the UART and two additional pins are soldered directly to the 2A03 to control /NMI and /RESET. I took a console and desoldered the 2KB RAM and replaced it with a socket. I'd like to permanently solder this in now that they work but having them removable for debugging was a huge help. AVR control code was written mainly in C with some assembly sprinkled across for the memory control portions.

The AVR sits and wait for serial commands and can perform several functions including :
* Read / write of any memory location
* Quick dump of an entire 256-byte page
* Freezing of memory addresses (rewriting a single value constantly in the busy loop)
* Single frame stepping by controlling the NMI
* Remote reset of console
* Applying auto increment to tables to a single variable (fun for sine waves on x/y positions!)

I had pretty great results with using 250000 baud with the Genesis flasher project which is plenty fast for what I'm trying to do here. Cautionary tale about using this dual port RAM part, be sure to operate the other side normally even though it appears to be decoupled. As far as I can tell from the datasheet leaving CE low shouldn't have an effect on the opposite port but it most certainly does. Next revision could definitely use a few pullups on the AVR side, other than that I'm pretty happy with the layout.

Before wiring up the additional control lines :



Pretty excited to finally use this guy - your usual USB FTDI cable except with a 3.5mm jack. Routed the UART to a standard panel mount TRS on the back of the console.

It has been a blast playing with this thing - stuff I've done so far :

Debug Information
The original intention of this was be able to manipulate values in real time on a console for a game I've been developing, FCEUX is pretty incredible for emulation but I try to do a lot of testing on real hardware.





Scoreboards
Blades of Steel used to be my favorite, but I'm starting to come around on Ice Hockey. Heavily using the read function to pull any relevant data from a game then deciding what to do with it.
Would probably be great for automatic splits for speedruns.



Relay Control
Contra has a particular way of allocating shots, there are five slots for one player that a bullet can accommodate. I didn't want to cheap out and just trigger on a button press so all five values are pulled and checked for a new rising edge.



Hex editor
Wrote a live hex editor inspired by FCEUX, lots of fun goofing with variables on the fly. Great fun to have a "co-pilot" looking for ways to help you cheat, like a manual game genie.




Single Frame Step
The /NMI pin is the 60hz interrupt generated from the PPU, this is one of the trickiest parts of the NES since this is the only safe window in which to make writes to VRAM. Due to the limited video bandwidth, the game is typically laid out the following way :

Busy loop / Wait for NMI
Falling edge of NMI / Perform VRAM updates then set flag that NMI has occured
Return from NMI / Perform logic updates / queue VRAM updates
(repeat)

On a "step" command sent from the host the AVR waits until the natural falling edge /NMI then asserts low until it receives the command for another step. This safely inhibits the interrupt and keeps everything in sync. Could also set up a frame level breakpoint here!


 


Enhanced Rules / Displays
Every game needs a place to store the controller reads after a $4016/$4017 fetch. Typically these routines will still run in a pause menu (so you'll know when to exit). The idea is that the host can read that we are in a pause state AND the controller data you can control a menu offscreen.

Remote Reset
I provided two pins for the NMI and RESET on the console, these are jumpered from the 2A03 directly to GPIO on the AVR. Both of these inputs to the CPU are open drain.

The /RESET pin allows you to command a console reset the host so you wouldn't have to fuss with the console. You can provide an argument for a short reset (10ms) to jump to reset vector within the game and a long reset (~1s) which will bring you back to the powerpak menu.



Glitch Roulette
This was a lot of fun. The idea that every time the player jumps (memory location / method of determining jumping will vary from game to game) the host will write a random value to a random location. The sound engine is usually the first to go since they typically require a good amount of RAM and are overwriting is fairly harmless. A hit on an indirect memory address/stack is usually catastrophic.

Biggest ups to Todd Bailey for the help with assembling these things and Adam Stankiewicz and Nick Gargiulo for tirelessly testing blades of steel.

5 comments:

  1. It's amazing, you can now write a kinect based game for nes ;)

    ReplyDelete
  2. I've been wanting to do this for a pinball scoreboard, using the same dual port ram chip. Do you happen to have a schematic/KiCAD files online anywhere? If not it's great to see that the idea is at least sound.

    Thanks

    ReplyDelete
  3. This is incredible! We've been looking for something like this. We play a TON of Blades at our office, and have even built a scorekeeper app:

    http://blades-app.herokuapp.com/

    It relies on hand-fed data though, and doesn't have any of the more advanced stats direct data would provide.

    ReplyDelete
  4. Awesome! I've been wanting to do something like this for the last 6 months or so. I was borrowing an fpga from a friend, that I was going to program to emulate the memory chip, but this is so much easier. Any chance you'll open source the AVR code?

    ReplyDelete