Famicom Party

16. Controller Input

Table of Contents:

With the basics of NES graphics covered, let's turn our attention to reading player input from the controller(s). In this chapter, we'll look at the history of input devices for the system, how to read the state of controller buttons, and how to use controller input to create actual gameplay.

A History of Controllers

The most successful home console prior to the NES was the Atari 2600 (or "Video Computer System" / VCS).
The Atari 2600 joystick.
The 2600's controller was a joystick with a single button, which meant players only had a limited ability to provide input.

In place of a joystick, the Famicom/NES controllers use a directional pad (or "D-pad"), shaped like a plus sign. This setup, invented by Gunpei Yokoi,
The "Game & Watch Multiscreen" edition of Donkey Kong.

Yokoi would also go on to produce Kid Icarus and Metroid for the NES, as well as designing the Virtual Boy. His practice of "lateral thinking with withered technology" continues to be the foundation of Nintendo's designs.
first made an appearance in Nintendo's "Game & Watch Multiscreen" Donkey Kong system in 1982.

The Famicom controllers, released only one year later in 1983, were an evolution of the Multiscreen design. The two controllers each feature a D-pad, two action buttons (labelled "A" and "B"), and two supplemental buttons ("Select" and "Start"). On the Famicom, the controllers are clearly labelled "I" and "II", and player two's controller features a microphone with its own volume slider. Why did the Player 2 controller feature a microphone? At the time of the Famicom's release, karaoke had become very popular in Japan. The microphone would allow developers to create karaoke games for the Famicom, with the player's voice projected through their TV speakers. I'm not aware of any Famicom games that actually used that functionality, and the microphone was dropped when the controllers were revised for the NES. Some Famicom games do use the microphone as a sort of additional input by having the player scream into it at certain points. This functionality has been preserved in Nintaco, which has a "Scream into Microphone" action in the "Machine" menu.

A "player 2" controller from the Famicom, featuring microphone controls and a lack of Select and Start buttons.

On the Famicom, the two controllers were hard-wired into the console. To facilitate the addition of future input methods, the Famicom console also had a front-facing, 15-pin expansion port, normally hidden behind a red plastic cover. This expansion port was where the Famicom's version of the "Zapper" light gun connected, as well as multiplayer accessories.

A close-up picture of the Famicom expansion port. Photo from NintendoWorldReport.
A schematic showing how the Famicom light gun attaches to the console, via the expansion port. The Famicom light gun has a more realistic, "Wild West" revolver style than the comparitively futuristic (and bright orange) US "Zapper" light gun.

When the Famicom was re-designed for its US release as the NES, the controllers underwent some minor, but significant, changes. The microphone was dropped, making the "Player 1" and "Player 2" controllers identical. Additionally, the controllers were now detachable, and connected to the console via a specially-designed plug. This made using alternative controllers (such as the Zapper) much easier, since players could simply disconnect a standard controller and plug in something else in its place.

The original NES controller.
Photo by Evan Amos.

Nintendo (and its hardware partners) created several alternative controllers over the lifespan of the system. The NES Advantage was an arcade-style joystick, with the ability to set the A or B buttons to "Turbo" (automatic rapid button presses when held down), as well as a "slow motion" feature (in reality, a "Turbo" setting for the start button). The NES Max controller resembled more modern controller designs, with "wings" for better ergonomics and a sliding directional pad that looks similar to an analog stick, but is still digital input. Later controller releases included the NES Satellite and NES Four Score, each of which allowed four controllers to connect to the system at the same time and enabled simultaneous four-player gameplay in games that were coded to take advantage of it.

A collection of non-standard NES controllers. Clockwise, from top left: NES Advantage, NES Max, NES Satellite, NES Four Score.

Perhaps the most unusual alternative controller, and the one which likely did more to cement Nintendo's success in the US than anything else, was R.O.B., the "Robotic Operating Buddy". Launched as part of the NES "Deluxe Set" in 1985, R.O.B. was a battery-powered "robot" with photodiodes for "eyes", which could watch the TV for specially-coded flashes of light and move in response to them. Only two licensed games were ever released to take advantage of R.O.B.: Gyromite, a pack-in game in the Deluxe Set, and Stack-Up, also released in 1985. While R.O.B. was a failure in terms of driving development of new games, it was a cool-looking (for the time) robot that enticed many players/parents to buy the Deluxe Set.

Controller Hardware

An NES controller has eight inputs: four on the directional pad (Up, Down, Left, Right), and four actual buttons (Select, Start, A, B). Each input can be in one of two states: pressed, or not pressed. This sounds like the perfect opportunity to store controller state in a byte of memory, with one bit for the state of each button. From an electrical standpoint, though, sending eight bits of data at a time would require eight wires. The NES controller connector only has seven pins, making this strategy impossible.

An annotated photo of an NES controller plug. There are seven pins, only three of which are used for data. In general, only one data line is used at a time, with "Data 1" used for standard NES controllers and Data 2 / Data 3 being used by the Zapper.

Instead of sending all eight button states in parallel, the NES controller uses a "parallel-in, serial-out shift register". The controller has one eight-bit register inside of itself. When the controller receives a signal from the console on the "Latch" wire, it begins to continuously fill the register with the current state of the buttons. Turning off the Latch signal causes the shift register to go back to serial mode, saving the last button states before the signal was turned off. Then, when the controller receives a signal on the "Clock" wire, it outputs the state of a single button at a time (in a specific order) on the appropriate Data wire. So, to read the state of all buttons on the controller, your code must:

There are, as you might expect, special memory-mapped I/O addresses for performing these actions. Memory address $4016 sets the latch state for both Controller 1 and Controller 2. Once the controllers' shift registers are filled, reading from $4016 will return one bit of data (one button state) from Controller 1, and reading from $4017 will return one bit of data from Controller 2. On the NES Satellite or NES Four Score, the first eight reads of $4016 return the button states for Controller 1, and the next eight reads return the button states for Controller 3. A final set of eight reads returns a "signature" pattern of bits, which allows the game to determine whether or not a four-player adapter is connected to the console. A similar process is used with $4017 to read the states of Controller 2 and Controller 4.

To convert the flow above to assembly code, we first write a "1" to $4016, then write a "0" to the same address.

  LDA #$01
  STA $4016
  LDA #$00
  STA $4016

(Note: as with other MMIO addresses, it's common to set constant names for $4016 and $4017. Later, I will use CONTROLLER1 and CONTROLLER2.)

Reads from $4016 each return a single button state, with bit 0 set if the button is pressed and cleared if it is not (all other bits are cleared). The button states are returned in this order: A, B, Select, Start, Up, Down, Left, Right. Storing these eight button states to eight separate zero-page addresses would be very inefficient, but thankfully, there is a simple technique that can store those eight button states in a single byte. In order to use it, though, we will need to learn a few more 6502 opcodes.

Bit Shifts and Rotations

A bit shift moves the bits within a byte left or right. The 6502 has two opcodes that can shift bits: ASL (Arithmetic Shift Left) and LSR (Logical Shift Right). ASL moves all bits in a byte one position to the left, dropping the leftmost bit (bit 7) into the carry flag and adding a zero to replace the now-empty rightmost bit (bit 0). LSR does the opposite, moving all bits in a byte one position to the right, dropping the rightmost bit into the carry flag and setting the leftmost bit to zero. Because of how binary numbers work, performing a left shift is the same as multiplying by two (so long as the result fits within a single byte), and a right shift is the same as dividing by two and rounding down.

The rotation opcodes (ROL "ROtate Left" and ROR "ROtate Right") shift bits just like ASL and LSR, but rather than filling empty bits with zeroes, they move whatever is stored in the carry flag into the empty bit position. When using a rotation opcode, the contents of the carry flag are shifted into the byte before one of the byte's bits are shifted into the carry flag.

Let's look at a few examples.

  ; Our starting byte - equivalent to decimal 15
  LDA #%00001111
  STA $8000

  ; Shift left.
  ASL $8000
  ; Memory address $8000 now contains 00011110,
  ; equivalent to decimal 30.
  ; The carry flag contains 0, because
  ; that was the left-most bit.

  ; Shift back to the right.
  LSR $8000
  ; Memory address $8000 is now back to 00001111.
  ; The carry flag still contains 0.

  ; Shift right again.
  LSR $8000
  ; Memory address $8000 now contains 00000111,
  ; equivalent to decimal 7.
  ; Note that the carry flag is now 1 - when
  ; the rightmost bit was shifted right, it went
  ; into the carry flag.

  ; This time, let's rotate right.
  ROR $8000
  ; Memory address $8000 now contains 10000011,
  ; and the carry flag contains 1 again.
  ; What happened?
  ; The "1" from the carry flag moved into the
  ; leftmost bit position, and the "1" in the
  ; rightmost bit position dropped off into the
  ; carry flag.

  ; Let's do that a few more times:
  ROR $8000
  ; Memory address $8000: 11000001, carry flag: 1
  ROR $8000
  ; Memory address $8000: 11100000, carry flag: 1
  ROR $8000
  ; Memory address $8000: 11110000, carry flag: 0
  ROR $8000
  ; Memory address $8000: 01111000, carry flag: 0

  ; We can also shift or rotate the accumulator directly:
  LDA $8000
  ; The results of the rotate and shift are only in
  ; the accumulator, not stored back into $8000.

Ring Counters

Now that we've looked at shifts and rotations, let's put them to use to store controller data in a single byte. Remember, asking for a button state (reading from $4016) returns the state of a button in bit 0, with the bit set if the button is pressed or cleared if the button is not pressed. A ring counter makes use of rotations to run a loop exactly eight times, transferring the results of eight reads from $4016 into a single byte.

To set up the ring counter, we'll first need a byte of memory to store our controller data. Since controller data is updated frequently, a byte of zero-page RAM is ideal.

pad1: .res 1

Next, we will set the initial state of pad1 to the byte 00000001.

  LDA #%00000001
  STA pad1

Each time we read a button state from the controller, we will use shift and rotation opcodes to first transfer the bit that represents the button state into the carry flag, and then rotate it onto pad1. When the "1" from bit 0 rotates all the way left and falls off into the carry flag, we know that we have transferred eight button states and we can end the loop (by checking at the end of each loop iteration against BCC, Branch if Carry Clear).

Here's a look at the full code:

  ; write a "1", then a "0", to CONTROLLER1 ($4016)
  ; in order to lock in button states
  LDA #$01
  LDA #$00

  ; initialize pad1 to 00000001
  LDA #%00000001
  STA pad1

  LDA CONTROLLER1       ; Get the next button state
  LSR A                 ; Shift the accumulator right one bit,
                        ; dropping the button state from bit 0
                        ; into the carry flag
  ROL pad1              ; Shift everything in pad1 left one bit,
                        ; moving the carry flag into bit 0
                        ; (because rotation) and bit 7
                        ; of pad1 into the carry flag
  BCC get_button_states ; If the carry flag is still 0,
                        ; continue the loop. If the "1"
                        ; that we started with drops into
                        ; the carry flag, we are done.

At the end of this loop, the eight bits of pad1 will contain the state of all eight buttons on the controller, as follows: To capture the state of player 2's controller buttons, use the same ring counter, but substitute CONTROLLER2 ($4017) for CONTROLLER1, and designate a second byte of zero-page RAM for storing button states (pad2).


Using Controller Data

Once you have captured the state of the controller's buttons, the next step is making use of that data in your game code. To do so, we can use the logical filters introduced in the last chapter to test whether or not the buttons we care about are set, and then branch based on the zero flag. To make that testing easier, I like to set constants for each button's position in pad1:

BTN_RIGHT   = %00000001
BTN_LEFT    = %00000010
BTN_DOWN    = %00000100
BTN_UP      = %00001000
BTN_START   = %00010000
BTN_SELECT  = %00100000
BTN_B       = %01000000
BTN_A       = %10000000

Once you have these constants, the checks themselves are fairly simple. Here is how we would test whether or not the start button is pressed:

  LDA pad1      ; Load button states into accumulator
  AND #BTN_START ; Must use "#"! Not a memory address!
  BNE start_pressed ; Branch to code you want to
                    ; run when start is pressed

As a quick refresher, AND lets you selectively filter out bits from the accumulator. Any bits that are "0" in AND's operand will be set to zero in the accumulator; any bits that are "1" in AND's operand will stay as they are in the accumulator. By using 00010000 as our operand for AND, we ensure that all bits except the bit that represents the state of the start button will be zero. That way, if the start button is not pressed, the result of our AND will be zero, regardless of how many other buttons on the controller are pressed.

Let's apply this to the game we've been building. Instead of having the player's ship automatically move left and right, we will instead read the controller and move the player's ship appropriately. We already isolated our player-updating code into its own subroutine (.proc update_player), which should make things a bit simpler. We will need to make the following changes from the last chapter:

Let's start with reading the controller. We will make a new subroutine for the ring counter and call it from NMI. First, though, we will need some new constants in our constants.inc:

10CONTROLLER1 = $4016
11CONTROLLER2 = $4017
13BTN_RIGHT = %00000001
14BTN_LEFT = %00000010
15BTN_DOWN = %00000100
16BTN_UP = %00001000
17BTN_START = %00010000
18BTN_SELECT = %00100000
19BTN_B = %01000000
20BTN_A = %10000000

With that out of the way, let's create a new file for controller-related subroutines. This will make it possible to re-use the same controller code in multiple projects. Inside the file (I'm calling it controllers.asm), we'll write a subroutine that uses the ring counter technique we saw earlier:

1.include "constants.inc"
3.segment "ZEROPAGE"
4.importzp pad1
6.segment "CODE"
7.export read_controller1
8.proc read_controller1
10 TXA
11 PHA
12 PHP
14 ; write a 1, then a 0, to CONTROLLER1
15 ; to latch button states
16 LDA #$01
18 LDA #$00
21 LDA #%00000001
22 STA pad1
25 LDA CONTROLLER1 ; Read next button's state
26 LSR A ; Shift button state right, into carry flag
27 ROL pad1 ; Rotate button state from carry flag
28 ; onto right side of pad1
29 ; and leftmost 0 of pad1 into carry flag
30 BCC get_buttons ; Continue until original "1" is in carry flag
32 PLP
33 PLA
34 TAX
35 PLA
36 RTS

One thing to note is the .importzp pad1 on line 4 - we will need to make sure that we reserve a byte in zero-page with that name and export it with .exportzp. Here is the updated ZEROPAGE segment in our main file (now called input.asm): I've also removed the player_dir byte; it was only needed because we were continually moving the player's ship either left or right. Since we don't need to track that anymore, we've freed up another byte of zero-page RAM.

4.segment "ZEROPAGE"
5player_x: .res 1
6player_y: .res 1
7scroll: .res 1
8ppuctrl_settings: .res 1
9pad1: .res 1
10.exportzp player_x, player_y, pad1

We have a fair number of things that we are keeping track of, but we are only using five of the 256 zero-page addresses available to us. Even so, it's still important to use zero-page only for things that will be updating frequently, since later additions like audio or tracking a large number of enemies can take up a big chunk of zero-page addresses.

Next, let's update the NMI handler to read controller state once per frame:

18.import read_controller1
20.proc nmi_handler
21 LDA #$00
23 LDA #$02
25 LDA #$00
27 ; read controller
28 JSR read_controller1

Remember that you need to import any subroutines you want to use that are exported in a different file (line 18).

Everything is in place to work with controller data. Now, it's time to update the update_player subroutine. While virtually all of this subroutine will be replaced, a portion of it that might still be useful is the code to detect collisions with the edge of the screen. To keep the logic simpler, this chapter will only focus on using controller input, but it's a good homework exercise to re-implement bounds checking on your own, essentially ignoring controller presses that would put the ship outside of a certain area. Our logic for updating the player position will work as follows (the exact order of checking directions is arbitrary):

This setup gives us a one-to-one mapping between controller presses and movement on screen, which will have a "stiff" feel. In a later chapter, we'll add rudimentary physics, but this simpler approach will let us focus on the controller.

The updated update_player subroutine is as follows:

102.proc update_player
103 PHP ; Start by saving registers,
104 PHA ; as usual.
105 TXA
106 PHA
107 TYA
108 PHA
110 LDA pad1 ; Load button presses
111 AND #BTN_LEFT ; Filter out all but Left
112 BEQ check_right ; If result is zero, left not pressed
113 DEC player_x ; If the branch is not taken, move player left
115 LDA pad1
117 BEQ check_up
118 INC player_x
120 LDA pad1
122 BEQ check_down
123 DEC player_y
125 LDA pad1
127 BEQ done_checking
128 INC player_y
130 PLA ; Done with updates, restore registers
131 TAY ; and return to where we called this
132 PLA
133 TAX
134 PLA
135 PLP
136 RTS

That rounds out the updates to the code. Let's build everything and verify that it works as expected, by running the following commands in the top-level directory for the project.

ca65 src/backgrounds.asm
ca65 src/controllers.asm
ca65 src/reset.asm
ca65 src/input.asm
ld65 src/backgrounds.o src/controllers.o src/input.o src/reset.o -C nes.cfg -o input.nes

The resulting .nes file should let you move the player ship freely using whatever your emulator has mapped to the D-pad inputs. Here it is running in an emulator in your browser! Note that since we are now using the controller, the emulator has an on-screen controller. There is also support for keyboard shortcuts, using the following keys (for QWERTY keyboards). You will need to first click on the emulator area or otherwise focus it in order for keypresses to be registered.



Here are some exercises to try now that we have covered the basics of controller input. You can download the code from this chapter as a starting point.