Difference between revisions of "Controller reading code"

From Nesdev wiki
Jump to: navigation, search
(flat merge from Gamepad code)
m (Basic Example: moved to Controller reading)
 
(17 intermediate revisions by 4 users not shown)
Line 1: Line 1:
This article provides example code for reading the [[standard controller]].
+
This page contains example code for reading the [[NES controller]].
  
== Example 1 ==
+
See also: [[Controller reading]]
  
To read the first player's [[standard controller]], write an odd value and then an even value to $4016, then read $4016 eight times in a row. For each read, if either of the low two bits are set, that means that the corresponding button is pressed. (On a Famicom, bit 0 returns hardwired controllers and bit 1 expansion controllers.) The buttons come in the order of A, B, Select, Start, Up, Down, Left, Right.
+
=== Basic Example ===
  
<pre>
+
This describes an efficient method of reading the [[standard controller]] using [http://cc65.github.io/cc65/ ca65] syntax.
; Reads controller
+
; Out: A=buttons pressed, where bit 0 is A button
+
read_joy:
+
      ; Strobe controller
+
      lda #1
+
      sta $4016
+
      lda #0
+
      sta $4016
+
     
+
      ; Read all 8 buttons
+
      ldx #8
+
loop:
+
      pha
+
     
+
      ; Read next button state and mask off low 2 bits.
+
      ; Compare with $01, which will set carry flag if
+
      ; either or both bits are set.
+
      lda $4016
+
      and #$03
+
      cmp #$01
+
     
+
      ; Now, rotate the carry flag into the top of A,
+
      ; land shift all the other buttons to the right
+
      pla
+
      ror a
+
     
+
      dex
+
      bne loop
+
     
+
      rts
+
</pre>
+
 
+
=== DPCM Safety ===
+
 
+
If a DMC sample is playing, it can corrupt the above sequence by inserting an extra $4016 read somewhere between yours if it happens to try to read a sample just as you're reading $4016. The simplest way to compensate is to repeatedly read the controller twice, and see if the two readings match. If they don't, read the controller again and compare with the previous read. Repeat until they match.
+
 
+
<pre>
+
; temp is a zero-page variable
+
 
+
; Reads controller. Reliable when DMC is playing.
+
; Out: A=buttons held, A button in bit 0
+
read_joy_safe:
+
      ; Get first reading
+
      jsr read_joy
+
     
+
mismatch:
+
      ; Save previous reading
+
      sta temp
+
     
+
      ; Read again and compare. If they differ,
+
      ; read again.
+
      jsr read_joy
+
      cmp temp
+
      bne mismatch
+
     
+
      rts
+
</pre>
+
 
+
Note that if the player presses or releases a button between two reads, this will interpret that as a corrupt read and read again. Unfortunately, there's no way for it to tell the difference.
+
 
+
One problem with this approach is that it can read the controller more times than necessary, since for example after reading it three times (due to the first two not matching), it only compares the second and third reads; it doesn't compare the first and third, which might match. If the second read was the corrupt one, the second and third won't match, causing a fourth read.
+
 
+
A faster controller reading approach has been discussed, which assumes that the upper bits of $4016 will be the same each time it's read. This can fail on the Famicom, which has a microphone that toggles bit 2.
+
 
+
== Example 2 ==
+
 
+
This describes an efficient method of reading the [[standard controller]].
+
It is designed for [[ca65]], using a few [https://cc65.github.io/doc/ca65.html#ss6.6 unnamed labels] for short loops.
+
  
 
The result byte ''buttons'' should be placed in zero page to save a cycle each time through the loop.
 
The result byte ''buttons'' should be placed in zero page to save a cycle each time through the loop.
Line 82: Line 14:
 
</pre>
 
</pre>
  
When reading from ''JOYPAD*'' what is read might be different from $01/$00 for various reasons. (See [[Controller port registers]].) In this code the only concern is bit 0 read from ''JOYPAD*.''.
+
When reading from ''JOYPAD*'' what is read might be different from $01/$00 for various reasons. (See [[Controller reading]].) In this code the only concern is bit 0 read from ''JOYPAD*.''.
 
<pre>
 
<pre>
 
JOYPAD1 = $4016
 
JOYPAD1 = $4016
Line 89: Line 21:
  
 
This is the end result that will be stored in ''buttons''. '''1''' if the button was pressed, '''0''' otherwise.
 
This is the end result that will be stored in ''buttons''. '''1''' if the button was pressed, '''0''' otherwise.
<pre>
+
 
bit:   7     6     5     4     3     2     1     0
+
{| class="wikitable" style="text-align:center;"
button: A     B Select Start Up   Down Left Right
+
|-
</pre>
+
! bit
 +
| &nbsp;&nbsp;&nbsp;&nbsp;7&nbsp;&nbsp;&nbsp;&nbsp;
 +
| &nbsp;&nbsp;&nbsp;&nbsp;6&nbsp;&nbsp;&nbsp;&nbsp;
 +
| &nbsp;&nbsp;&nbsp;&nbsp;5&nbsp;&nbsp;&nbsp;&nbsp;
 +
| &nbsp;&nbsp;&nbsp;&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;
 +
| &nbsp;&nbsp;&nbsp;&nbsp;3&nbsp;&nbsp;&nbsp;&nbsp;
 +
| &nbsp;&nbsp;&nbsp;&nbsp;2&nbsp;&nbsp;&nbsp;&nbsp;
 +
| &nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;
 +
| &nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;
 +
|-
 +
! button
 +
| A
 +
| B
 +
| Select
 +
| Start
 +
| Up
 +
| Down
 +
| Left
 +
| Right
 +
|}
  
 
This subroutine takes 132 cycles to execute but ignores the Famicom expansion controller.
 
This subroutine takes 132 cycles to execute but ignores the Famicom expansion controller.
Line 101: Line 52:
 
; so we're hitting two birds with one stone here
 
; so we're hitting two birds with one stone here
 
readjoy:
 
readjoy:
  lda #$01
+
    lda #$01
  sta JOYPAD1
+
    ; While the strobe bit is set, buttons will be continuously reloaded.
  sta buttons
+
    ; This means that reading from JOYPAD1 will only return the state of the
  lsr a        ; now A is 0
+
    ; first button: button A.
  sta JOYPAD1
+
    sta JOYPAD1
:
+
    sta buttons
  lda JOYPAD1
+
    lsr a        ; now A is 0
  lsr a       ; bit0 -> Carry
+
    ; By storing 0 into JOYPAD1, the strobe bit is cleared and the reloading stops.
  rol buttons  ; Carry -> bit0; bit 7 -> Carry
+
    ; This allows all 8 buttons (newly reloaded) to be read from JOYPAD1.
  bcc :-
+
    sta JOYPAD1
  rts
+
loop:
 +
    lda JOYPAD1
 +
    lsr a       ; bit 0 -> Carry
 +
    rol buttons  ; Carry -> bit 0; bit 7 -> Carry
 +
    bcc loop
 +
    rts
 
</pre>
 
</pre>
  
Line 122: Line 78:
 
.code
 
.code
 
readjoy:
 
readjoy:
  lda #$01
+
    lda #$01
  sta JOYPAD1
+
    sta JOYPAD1
  sta buttons2  ; player 2's buttons double as a ring counter
+
    sta buttons2  ; player 2's buttons double as a ring counter
  lsr a        ; now A is 0
+
    lsr a        ; now A is 0
  sta JOYPAD1
+
    sta JOYPAD1
:
+
loop:
  lda JOYPAD1
+
    lda JOYPAD1
  and #$03      ; ignore bits other than controller
+
    and #%00000011  ; ignore bits other than controller
  cmp #$01     ; Set carry if and only if nonzero
+
    cmp #$01       ; Set carry if and only if nonzero
  rol buttons1 ; Carry -> bit0; bit 7 -> Carry
+
    rol buttons1   ; Carry -> bit 0; bit 7 -> Carry
  lda JOYPAD2   ; Repeat  
+
    lda JOYPAD2     ; Repeat
  and #$03
+
    and #%00000011
  cmp #$01
+
    cmp #$01
  rol buttons2 ; Carry -> bit0; bit 7 -> Carry
+
    rol buttons2   ; Carry -> bit 0; bit 7 -> Carry
  bcc :-
+
    bcc loop
  rts
+
    rts
 
</pre>
 
</pre>
  
=== DPCM Safety ===
+
=== DPCM Safety using Repeated Reads ===
  
If your code is intended to be used with [[APU DMC]] playback, this code will need to be altered. The NES occasionally glitches the controller port twice in a row if sample playback is enabled, and games using samples need to work around this. For example, ''Super Mario Bros. 3'' reads each controller's data at least two times each frame. First it reads it as normal, then it reads it again. If the two results differ, it does the procedure all over. Because repeated rereads can take a long time, another way is to just use the previous frame's button press data if the results differ:
+
If your code is intended to be used with [[APU DMC]] playback, this code will need to be altered. The NES occasionally glitches the controller port twice in a row if sample playback is enabled, and games using samples need to work around this. For example, ''Super Mario Bros. 3'' reads each controller's data at least two times each frame. First it reads it as normal, then it reads it again. If the two results differ, it does the procedure all over.
  
 +
One controller example:<ref>[//forums.nesdev.com/viewtopic.php?p=151720#p151720 Super Mario Bros. 3 controller reread method]</ref>
 
<pre>
 
<pre>
last_frame_buttons1 = $00
 
last_frame_buttons2 = $01
 
first_read_buttons1 = $02
 
first_read_buttons2 = $03
 
 
 
readjoy_safe:
 
readjoy_safe:
  lda buttons2
+
    jsr readjoy
  sta last_frame_buttons2
+
reread:
  lda buttons1
+
    lda buttons
  sta last_frame_buttons1
+
    pha
 +
    jsr readjoy
 +
    pla
 +
    cmp buttons
 +
    bne reread
 +
    rts
 +
</pre>
  
  ; Read the controllers once and stash the result
+
Because repeated rereads can take a long time, another way is to just use the previous frame's button press data if the results differ. Note that this is a compromise: if the player presses or releases a button between the two reads, this will interpret that as a corrupt read. Unfortunately, there's no way for it to tell the difference.
  jsr readjoy
+
  lda buttons2
+
  sta first_read_buttons2
+
  lda buttons1
+
  sta first_read_buttons1
+
  
  ; Read the controllers again and compare
+
=== DPCM Safety using OAM DMA ===
  jsr readjoy
+
  ldx #1
+
cleanup_loop:
+
  ; Ignore read values if a bit deletion occurred
+
  lda buttons1,x
+
  cmp first_read_buttons1,x
+
  beq not_glitched
+
    lda last_frame_buttons,x
+
    sta buttons1,x
+
  not_glitched:
+
  
  ; Other filters, as described below, can go here
+
Because DPCM reads may only occur on an odd cycle, it is possible to get glitch-free controller reads by timing all '''$4016'''/'''$4017''' reads to fall on even cycles. This is made possible by the behavior of OAM DMA: the first cycle after an OAM DMA is guaranteed to be an even cycle.<ref>[http://forums.nesdev.com/viewtopic.php?p=171971 Forum post:] Rahsennor's OAM-synchronized controller read</ref>
  
  dex
+
This is a relatively new technique, and is not entirely supported by emulators.<ref>[http://forums.nesdev.com/viewtopic.php?f=2&t=14319&start=15#p172099 Forum post:] as of May 2016, Nintendulator and Nestopia do not accurately emulate OAM-synchronized controller reading.</ref>
  bpl cleanup_loop
+
  
  rts
+
<pre>
 +
    lda #OAM
 +
    sta $4014          ; ------ DMA ------
 +
    ldx #1            ; even odd          <- strobe code must take an odd number of cycles total
 +
    stx controller1    ; even odd even
 +
    stx $4016          ; odd even odd even
 +
    dex                ; odd even
 +
    stx $4016          ; odd even odd even
 +
read_loop:
 +
    lda $4017          ; odd even odd EVEN <- loop code must take an even number of cycles total
 +
    and #3            ; odd even
 +
    cmp #1            ; odd even
 +
    rol controller2, x ; odd even odd even odd even (X = 0; waste 1 cycle and 0 bytes for alignment)
 +
    lda $4016          ; odd even odd EVEN
 +
    and #3            ; odd even
 +
    cmp #1            ; odd even
 +
    rol controller1    ; odd even odd even odd
 +
    bcc read_loop      ; even odd [even]
 
</pre>
 
</pre>
  
Line 187: Line 146:
 
To reject opposing presses (Up+Down and Left+Right), which are possible on a worn Control Pad:
 
To reject opposing presses (Up+Down and Left+Right), which are possible on a worn Control Pad:
 
<pre>
 
<pre>
  lda buttons1,x
 
  and #$0A          ; Compare Up and Left...
 
  lsr a
 
  and buttons1,x    ; to Down and Right
 
  beq not_updown
 
    ; Use previous frame's directions
 
 
     lda buttons1,x
 
     lda buttons1,x
     eor last_frame_buttons1,x
+
     and #%00001010    ; Compare Up and Left...
    and #$F0
+
    lsr a
    eor last_frame_buttons1,x
+
    and buttons1,x    ; to Down and Right
    sta buttons1,x
+
    beq not_updown
  not_updown:
+
        ; Use previous frame's directions
 +
        lda buttons1,x
 +
        eor last_frame_buttons1,x
 +
        and #%11110000
 +
        eor last_frame_buttons1,x
 +
        sta buttons1,x
 +
    not_updown:
 
</pre>
 
</pre>
  
 
To instead reject all diagonal presses, simulating a 4-way joystick:
 
To instead reject all diagonal presses, simulating a 4-way joystick:
 
<pre>
 
<pre>
  lda buttons1,x
 
  and #$0F  ; If A & (A - 1) is nonzero, A has more than one bit set
 
  beq not_diagonal
 
  sec
 
  sbc #1
 
  and buttons1,x  ; to Down and Right
 
  beq not_updown1
 
    ; Use previous frame's directions
 
 
     lda buttons1,x
 
     lda buttons1,x
     eor last_frame_buttons1,x
+
     and #%00001111    ; If A & (A - 1) is nonzero, A has more than one bit set
    and #$F0
+
    beq not_diagonal
    eor last_frame_buttons1,x
+
    sec
    sta buttons1,x
+
    sbc #1
  not_diagonal:
+
    and buttons1,x
 +
    beq not_diagonal
 +
        ; Use previous frame's directions
 +
        lda buttons1,x
 +
        eor last_frame_buttons1,x
 +
        and #%11110000
 +
        eor last_frame_buttons1,x
 +
        sta buttons1,x
 +
    not_diagonal:
 
</pre>
 
</pre>
  
=== Misc ===
+
=== Calculating Presses and Releases ===
  
 
To calculate newly pressed and newly released buttons:
 
To calculate newly pressed and newly released buttons:
 
<pre>
 
<pre>
  lda buttons1,x
+
    lda buttons1,x
  eor #$FF
+
    eor #%11111111
  and last_frame_buttons1,x
+
    and last_frame_buttons1,x
  sta released_buttons1,x
+
    sta released_buttons1,x
  lda last_frame_buttons1,x
+
    lda last_frame_buttons1,x
  eor #$FF
+
    eor #%11111111
  and buttons1,x
+
    and buttons1,x
  sta pressed_buttons1,x
+
    sta pressed_buttons1,x
 
</pre>
 
</pre>
 +
 +
=== Button Flags ===
 +
 +
It is helpful to define the buttons as a series of bit flags:
 +
<pre>
 +
BUTTON_A      = 1 << 7
 +
BUTTON_B      = 1 << 6
 +
BUTTON_SELECT = 1 << 5
 +
BUTTON_START  = 1 << 4
 +
BUTTON_UP    = 1 << 3
 +
BUTTON_DOWN  = 1 << 2
 +
BUTTON_LEFT  = 1 << 1
 +
BUTTON_RIGHT  = 1 << 0
 +
</pre>
 +
 +
And then buttons can be checked as follows:
 +
<pre>
 +
    lda buttons
 +
    and #BUTTON_A | BUTTON_B
 +
    beq notPressingAorB
 +
    ; Handle presses.
 +
notPressingAorB:
 +
</pre>
 +
 +
== External Examples ==
 +
 +
* [http://forums.nesdev.com/viewtopic.php?t=4124 Forum post:] Blargg's DMC-fortified controller read routine
 +
* [http://forums.nesdev.com/viewtopic.php?p=171971 Forum post:] Rahsennor's OAM-synchronized controller read
 +
* [http://forums.nesdev.com/viewtopic.php?f=2&t=14197 Forum post:] Drag's bitwise DMC-safe controller reading
 +
 +
== References ==
 +
<references/>

Latest revision as of 17:27, 20 April 2019

This page contains example code for reading the NES controller.

See also: Controller reading

Basic Example

This describes an efficient method of reading the standard controller using ca65 syntax.

The result byte buttons should be placed in zero page to save a cycle each time through the loop.

; we reserve one byte for storing the data that is read from controller
.zeropage
buttons .res 1

When reading from JOYPAD* what is read might be different from $01/$00 for various reasons. (See Controller reading.) In this code the only concern is bit 0 read from JOYPAD*..

JOYPAD1 = $4016
JOYPAD2 = $4017

This is the end result that will be stored in buttons. 1 if the button was pressed, 0 otherwise.

bit     7         6         5         4         3         2         1         0    
button A B Select Start Up Down Left Right

This subroutine takes 132 cycles to execute but ignores the Famicom expansion controller. Many controller reading subroutines use the X or Y register to count 8 times through the loop. But this one uses a more clever ring counter technique: $01 is loaded into the result first, and once eight bits are shifted in, the 1 bit will be shifted out, terminating the loop.

; At the same time that we strobe bit 0, we initialize the ring counter
; so we're hitting two birds with one stone here
readjoy:
    lda #$01
    ; While the strobe bit is set, buttons will be continuously reloaded.
    ; This means that reading from JOYPAD1 will only return the state of the
    ; first button: button A.
    sta JOYPAD1
    sta buttons
    lsr a        ; now A is 0
    ; By storing 0 into JOYPAD1, the strobe bit is cleared and the reloading stops.
    ; This allows all 8 buttons (newly reloaded) to be read from JOYPAD1.
    sta JOYPAD1
loop:
    lda JOYPAD1
    lsr a	       ; bit 0 -> Carry
    rol buttons  ; Carry -> bit 0; bit 7 -> Carry
    bcc loop
    rts

Adding support for controllers on the Famicom's DA15 expansion port and for player 2's controller is straightforward.

.zeropage
buttons1: .res 1
buttons2: .res 1

.code
readjoy:
    lda #$01
    sta JOYPAD1
    sta buttons2  ; player 2's buttons double as a ring counter
    lsr a         ; now A is 0
    sta JOYPAD1
loop:
    lda JOYPAD1
    and #%00000011  ; ignore bits other than controller
    cmp #$01        ; Set carry if and only if nonzero
    rol buttons1    ; Carry -> bit 0; bit 7 -> Carry
    lda JOYPAD2     ; Repeat
    and #%00000011
    cmp #$01
    rol buttons2    ; Carry -> bit 0; bit 7 -> Carry
    bcc loop
    rts

DPCM Safety using Repeated Reads

If your code is intended to be used with APU DMC playback, this code will need to be altered. The NES occasionally glitches the controller port twice in a row if sample playback is enabled, and games using samples need to work around this. For example, Super Mario Bros. 3 reads each controller's data at least two times each frame. First it reads it as normal, then it reads it again. If the two results differ, it does the procedure all over.

One controller example:[1]

readjoy_safe:
    jsr readjoy
reread:
    lda buttons
    pha
    jsr readjoy
    pla
    cmp buttons
    bne reread
    rts

Because repeated rereads can take a long time, another way is to just use the previous frame's button press data if the results differ. Note that this is a compromise: if the player presses or releases a button between the two reads, this will interpret that as a corrupt read. Unfortunately, there's no way for it to tell the difference.

DPCM Safety using OAM DMA

Because DPCM reads may only occur on an odd cycle, it is possible to get glitch-free controller reads by timing all $4016/$4017 reads to fall on even cycles. This is made possible by the behavior of OAM DMA: the first cycle after an OAM DMA is guaranteed to be an even cycle.[2]

This is a relatively new technique, and is not entirely supported by emulators.[3]

    lda #OAM
    sta $4014          ; ------ DMA ------
    ldx #1             ; even odd          <- strobe code must take an odd number of cycles total
    stx controller1    ; even odd even
    stx $4016          ; odd even odd even
    dex                ; odd even
    stx $4016          ; odd even odd even
read_loop:
    lda $4017          ; odd even odd EVEN <- loop code must take an even number of cycles total
    and #3             ; odd even
    cmp #1             ; odd even
    rol controller2, x ; odd even odd even odd even (X = 0; waste 1 cycle and 0 bytes for alignment)
    lda $4016          ; odd even odd EVEN
    and #3             ; odd even
    cmp #1             ; odd even
    rol controller1    ; odd even odd even odd
    bcc read_loop      ; even odd [even]

Directional Safety

To reject opposing presses (Up+Down and Left+Right), which are possible on a worn Control Pad:

    lda buttons1,x
    and #%00001010    ; Compare Up and Left...
    lsr a
    and buttons1,x    ; to Down and Right
    beq not_updown
        ; Use previous frame's directions
        lda buttons1,x
        eor last_frame_buttons1,x
        and #%11110000
        eor last_frame_buttons1,x
        sta buttons1,x
    not_updown:

To instead reject all diagonal presses, simulating a 4-way joystick:

    lda buttons1,x
    and #%00001111    ; If A & (A - 1) is nonzero, A has more than one bit set
    beq not_diagonal
    sec
    sbc #1
    and buttons1,x
    beq not_diagonal
        ; Use previous frame's directions
        lda buttons1,x
        eor last_frame_buttons1,x
        and #%11110000
        eor last_frame_buttons1,x
        sta buttons1,x
    not_diagonal:

Calculating Presses and Releases

To calculate newly pressed and newly released buttons:

    lda buttons1,x
    eor #%11111111
    and last_frame_buttons1,x
    sta released_buttons1,x
    lda last_frame_buttons1,x
    eor #%11111111
    and buttons1,x
    sta pressed_buttons1,x

Button Flags

It is helpful to define the buttons as a series of bit flags:

BUTTON_A      = 1 << 7
BUTTON_B      = 1 << 6
BUTTON_SELECT = 1 << 5
BUTTON_START  = 1 << 4
BUTTON_UP     = 1 << 3
BUTTON_DOWN   = 1 << 2
BUTTON_LEFT   = 1 << 1
BUTTON_RIGHT  = 1 << 0

And then buttons can be checked as follows:

    lda buttons
    and #BUTTON_A | BUTTON_B
    beq notPressingAorB
    ; Handle presses.
notPressingAorB:

External Examples

  • Forum post: Blargg's DMC-fortified controller read routine
  • Forum post: Rahsennor's OAM-synchronized controller read
  • Forum post: Drag's bitwise DMC-safe controller reading

References

  1. Super Mario Bros. 3 controller reread method
  2. Forum post: Rahsennor's OAM-synchronized controller read
  3. Forum post: as of May 2016, Nintendulator and Nestopia do not accurately emulate OAM-synchronized controller reading.