NES and SNES controllers support 8 to 12 buttons with only three data pins (plus VCC/GND). Let’s attach them to a C64 – or any 6502-based system!

NES Connector

The NES controller needs to be connected to +5V, GND and three GPIOs.

---------- | 5 6 7 \ | 4 3 2 1 | ------------

Pin Description 1 GND 2 CLK 3 LATCH 4 DATA 5 – 6 – 7 +5V

SNES Connector

The SNES controller’s pins are just like the NES controller’s, but with a different connector.

/--------------------- | 7 6 5 | 4 3 2 1 | \---------------------

Pin Description 1 +5V 2 CLK 3 LATCH 4 DATA 5 – 6 – 7 GND

User Port

The C64 User Port exposes, among other lines, +5V, GND and 8 GPIOs (CIA#2 Port B):

1 | 2 3 4 5 6 7 8 9 10 | 11 12 --- ---------------------------- ------- A | B C D E F H J K L | M N

(viewed towards the C64 edge connector)

Pin Description 1 GND 2 +5V C PB0 D PB1 E PB2 F PB3 H PB4 J PB5 K PB6 L PB7

Connection

Let’s semi-arbitrarily map the signals like this:

GPIO Description PB3 LATCH (for both controllers) PB4 DATA (controller 1) PB5 CLK (for both controllers) PB6 DATA (controller 2)

The latch and clock outputs go to both controllers. There is a data line for each controller.

So the connection diagram for two NES controllers looks like this:

Description User Port Pin NES #1 Pin NES #2 Pin Color GND 1 1 1 black +5V 2 7 7 red LATCH F 3 3 blue DATA#1 H 4 – green CLK J 2 2 white DATA#2 K – 4 yellow

And this is the same diagram for two SNES controllers:

Description User Port Pin NES #1 Pin NES #2 Pin Color GND 1 7 7 black +5V 2 1 1 red LATCH F 3 3 blue DATA#1 H 4 – green CLK J 2 2 white DATA#2 K – 4 yellow

In fact, you can attach an NES and an SNES controller in parallel for each of the two slots, as long as you ever only connect one controller per slot.

This is the user port connector with wires attached for two controllers, using the color scheme above:

This is an NES connector attached as the first controller (green data line):

And this is an SNES connector attached as the second controller (yellow data line):

The Code

The code to read both controllers at a time is pretty simple:

; C64 CIA#2 PB nes_data = $dd01 nes_ddr = $dd03 ; bit_latch = $08 ; PB3 (user port pin F): LATCH (both controllers) bit_data1 = $10 ; PB4 (user port pin H): DATA (controller #1) bit_clk = $20 ; PB5 (user port pin J): CLK (both controllers) bit_data2 = $40 ; PB6 (user port pin K): DATA (controller #2) ; zero page controller1 = $e0 ; 3 bytes controller2 = $f0 ; 3 bytes query_controllers: lda #$ff-bit_data1-bit_data2 sta nes_ddr lda #$00 sta nes_data ; pulse latch lda #bit_latch sta nes_data lda #0 sta nes_data ; read 3x 8 bits ldx #0 l2: ldy #8 l1: lda nes_data cmp #bit_data2 rol controller2,x and #bit_data1 cmp #bit_data1 rol controller1,x lda #bit_clk sta nes_data lda #0 sta nes_data dey bne l1 inx cpx #3 bne l2 rts

After calling query_controllers , three bytes each at controller1 and controller2 will contain the state:

; byte 0: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ; NES | A | B |SEL|STA|UP |DN |LT |RT | ; SNES | B | Y |SEL|STA|UP |DN |LT |RT | ; ; byte 1: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ; NES | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ; SNES | A | X | L | R | 1 | 1 | 1 | 1 | ; byte 2: ; $00 = controller present ; $FF = controller not present

A 0 bit means the button is pressed, 1 means it is released.

The code pulses LATCH once, which makes the controllers sample their button states and transmit the first bit through their respective DATA lines. Pulsing CLK 15 more times will make the controllers send the remaining bits. Since SNES controllers send 16 bits of data, but NES controllers only send 8 bits, the type of controller can be detected through the lowermost nybble of byte 1. Similarly, the presense of a controller is detected by continuing to read bits: If a controller is attached, it will send 0 bits.

Repository

The driver code, together with demo code for the C64 can be found at https://github.com/mist64/nes_snes_controller_6502.

More?

For each additional controller, only a single extra GPIO is needed. This way, six controllers would be possible on a single 8 bit I/O port, with only slightly modified code.