⭅ Previous (System Timing) Next (Audio(APU)) ⭆

How the NES controller works.

Now that we’ve wrapped up a high level model for the graphics system, its time to add interactivity to our emulator. We’ll take a look at how the NES controller port works, and how the standard controller uses the port. After understanding how the port works, we’ll know how to add controller support to our emulator.

Also available on Youtube

The NES controller port.

The NES features an iconic controller. What some people don’t realize is that the system was actually designed to support a wealth of other peripherals. This lead to a fairly generic design for the controller port, to enable controllers besides the standard dpad + buttons.

Here’s what the port looks like on the NES. Note the Famicom has a much larger port, with different wiring. I’ll focus on the NES connector.

nes controller ports

Lets take a look at the electrical pinout for this connector, which will help outline the functionality.

        GND - O
        CLK - O O - 5V
        OUT - O O - D3
        D0  - O O - D4

Reference: https://www.nesdev.org/wiki/Controller_port_pinout

GND and 5V are standard power connections. Most interesting are the CLK, OUT, and D lines. The CLK signal is suggestive of timing. Since the NES wanted to support a wide variety of hardware, this connector is designed to read bits serially(one at a time).

The standard NES controller accomplishes this using a shift register. A shift register essentially allows the storing of a certain number of bits. The register can then be “shifted”, exposing a different one of those bits on its output. After shifting through all 8 positions, the register is empty and will return meaningless data. These single bits are sent over the OUT pin.

The D pins connect to the data bus of the CPU. These enable the controller to receive information from the system, useful for some specialized controllers. These arent used by the standard controller.

The standard NES controller

Lets take a look at the standard NES controller:

     _
   _| |_
  |_   _|  <Select> <Start>  (B) (A)
    |_| 

With the 4 dpad directions and four buttons, the controller has exactly 8 buttons. Given the shift register and the structure of the controller port, this means the software interacts with the controller by saving or latching the state of the controller, then shifting until all the button bits have been read out.

Now that we know what we want to accomplish, lets see how software achieves this using a few more MMIO registers.

Controller MMIO Registers

First, the controller state needs to be stored in the shift register. The shift register has a pin called strobe, which when high stores the current value in the register. While the strobe is high, reading from the shift register will continually read the first buttom.

After setting the strobe low, the shift register will hold its previous state, changing only in response to a shift.

0x4016 (write) : Controller Strobe

The strobe is controlled via a MMIO register at 0x4016. Only one bit is used, and it is the 1s place or least significant bit. So writing 0x01 to 0x4016 sets the strobe, and writing 0x00 releases it. After a set and release the shift register is loaded with the state of all 8 buttons.

0x4016 and 0x4017 (read) : Controller state

Finally controller bits can be read out one at a time. These are communicated via bit 1 of the value read from this address. Player 1 is read from 0x4016, and player 2 from 0x4017. Though each controller is read from a separate address, both controllers share the strobe write at 0x4016.

Standard controller ordering

Bits are read out of the standard controller according to the ordering:

  1. a
  2. b
  3. select
  4. start
  5. up
  6. down
  7. left
  8. right

Controller Test ROM

Now that we know how the controller works, we can build a simple program that just logs the state read from the controller port. It will read the controller during vblank, and use the sprites to display which buttons are pressed.

The screen will always contain sprites for each of the buttons. If the button is pressed, it will light up. Otherwise, it will use a more muted color.

The snippet below demonstrates the key part of the interrupt handler. Like the OAMDMA code we saw for sprites, this keeps a copy of OAM in page 0x0200 then uses dma to copy into OAM.

  ; Constants
  CTRL1 = $4016

  ; now read each bit for the buttons. If set, use palette 1. otherwise palette 0
  ldy #$06    ; OAM offset. 2nd slot(4) + 2 for attribute position
  ldx #$08    ; Sprite&tile num
.btn_update:
  lda CTRL1
  and #$1     ; clear unmapped bits, want 0 or 1
  ; use value as palette directly.
  ; 0 = unpressed should be same as bg
  ; 1 = pressed = active palette
  ; write attr 
  sta (0), y
  ; then add +4 to move to next oam slot
  tya
  clc
  adc #$04
  tay

  dex
  bne .btn_update

When running the code, it should look something like this. The respective buttons light up when the key is pressed, and are grey otherwise.

nes controller test rom output

⭅ Previous (System Timing) Next (Audio(APU)) ⭆

We publish about 1 post a week discussing emulation and retro systems. Join our email list to get notified when a new post is available. You can unsubscribe at any time.