Testing my SNES controllers with two new controller test ROMs
Many weeks ago I was wondering how quickly I could manually read a SNES controller in 65816 assembly.1 This led me to wondering if I could read and display the controller data once per scanline to measure the contact bounce and detect bad button contacts to find my best SNES controller.
I was unable to read and display all 16 bits of controller data in a single scanline. Luckily there's just enough CPU time to read and display 12 bits of data and a Standard Controller has 12 buttons.
Rapid Read Test
This test reads 12 bits of controller data from port 1 every scanline and displays it by writing to
BGnVOFS
(vertical scroll offset register) every horizontal blank.2
Since I do not have super-vision, I used a 60Hz USB HDMI capture device to record the console output while mashing buttons on the controller. I then played back the recording, advancing each frame one at a time to inspect the stability and contact bounce of my controllers.
Here is the output of a controller with bad start and select buttons. On this controller I have to press hard for the console to register a start or select button. Looking at the test output it's clear that the button contacts have gone bad and it's not making a stable connection.
When playing around with the test I found phantom inputs on one of my D-Pads. I was repeatedly pressing the right button and the recording showed the occasional unintended down press that lasted 2 to 5 frames.
I'm fairly certain this is a controller issue and not a technique issue. I've repeated this test on multiple controllers, with the same hand positions and tapping rhythm and 2 of my controllers have no phantom input issues.
Bounce Test
I wanted to look at the contact bounce of all of my controllers but I found recording the rapid-read-test and checking each frame annoying. So I made a new test that can detect button presses and releases.
This test will wait until a controller button is pressed. Then it reads the controller once per scanline, writing the data to RAM and looping until no button presses have been detected for 262 scanlines (1 frame). Finally, it builds multiple HDMA tables to display 214 scanlines of controller reads after key-press and up to 214 scanlines of controller reads before button-release.
To use the joypad-bounce-test, you simply press and release a single button and look at the results. No screen recording is required (unless you are analysing 10 seconds of button mashing like I did below).
Each pair of vertical lines represents a controller button. The left line shows the controller data after key-press, the right line shows the controller data before key-release.
I also added a counter to the test because I was uncertain if the test registered my controller input or not.
The joypad-bounce-test is useful for confirming bad contacts in my controllers. Annoyingly, it is not useful for detecting phantom inputs.
How I hold the controller affects contact bounce
While this is obvious in hindsight, I was unaware that resting my controller on my leg and repeatedly Y tapping with my index finger has different contact bounce than holding the controller in two hands (with both middle fingers underneath the controller) and tapping with my thumb.
On my Super Famicom controller, repeatedly tapping the Y button on my leg has worse contact bounce than holding the controller in two hands and tapping Y with my thumb.
I did not know if this is a sign of a bad controller or not. So I repeated this test with a different controller and got different result. This controller had minor contact bounce when tapping Y on my leg and (surprisingly) large contact bounce when holding the controller in two hands.
I initially did not believe this output and seriously thought there was something wrong with my code. Holding the controller in two hands should be more stable than resting it on my leg. The code looked fine. So I repeated the setup with my PAL controller using the rapid-read test ROM, manually reviewed each frame and got similar results (controller on leg, controller in 2 hands).
Conclusions
I have the following conclusions about my 5 controllers (3 Nintendo and 2 late-2010's clone controllers):
- The Start and Select buttons are horrible and barely work on my 2 clone controllers.
- The D-Pad is bad on 3 of my controllers (1 Nintendo PAL and 2 clone).
- The L and R buttons are good on all of my controllers.
- The face buttons are OK on all of my controllers. I do see significant contact bounce when I button mash and minor or no contact bounce for regular button presses.
Most of the time I did not detect measurable contact bounce on my good controllers. Occasionally I saw contact bounce that lasted less then 20 scanlines and when I was mashing the controller I rarely saw contact bounce that lasted just under half a frame.
These tests confirm that I do not need to add debounce logic to my homebrew SNES game when I am using a good controller.3 The worst contact bounce I saw with my good controllers stabilises in less than half a frame when mashing heavily. Take this with a grain of salt, the sample size is small and I'm not the best button masher.
Downloads
- joypad_rapid_read_test.zip (2.4 KiB)
- joypad_bounce_test.zip (1.8 KiB)
These tests are free to distribute. The source code can be found on GitHub (zlib licensed):
-
Most SNES games do not manually read the controller ports. The SNES contains a joypad auto-read feature that will read 16-bits from the controller ports once per frame.
Manual reading is required for the Multitap and non-standard peripherals that transfer more than 16 bits of data like the Super NES Mouse and the NTT Data Keyboard. ↩ -
I can display 8 bits data of every scanline by writing
value - scanline - 1
to theBGnVOFS
register every horizontal blank with a background pattern that shows the scanline's binary value on every scanline.I'm using mode 0. Where 4 horizonatally offsetted backgrounds can be used to display 32 bits of data every scanline. Mode 0 also gives each background its own independednt palette, allowing to to indivually colourise the on and off colours for all 32 vertical lines. ↩
-
My game does include input buffering for the attack action. ↩