# Difference between revisions of "Controller reading code"

(→Basic Example: table) |
m (use binary immediate values where appropriate; use consistent indentation) |
||

Line 60: | Line 60: | ||

loop: | loop: | ||

lda JOYPAD1 | lda JOYPAD1 | ||

− | lsr a ; | + | lsr a ; bit 0 -> Carry |

− | rol buttons ; Carry -> | + | rol buttons ; Carry -> bit 0; bit 7 -> Carry |

bcc loop | bcc loop | ||

rts | rts | ||

Line 81: | Line 81: | ||

loop: | loop: | ||

lda JOYPAD1 | lda JOYPAD1 | ||

− | and # | + | and #%00000011 ; ignore bits other than controller |

− | cmp #$01 | + | cmp #$01 ; Set carry if and only if nonzero |

− | rol buttons1 | + | rol buttons1 ; Carry -> bit 0; bit 7 -> Carry |

− | lda JOYPAD2 | + | lda JOYPAD2 ; Repeat |

− | and # | + | and #%00000011 |

cmp #$01 | cmp #$01 | ||

− | rol buttons2 | + | rol buttons2 ; Carry -> bit 0; bit 7 -> Carry |

bcc loop | bcc loop | ||

rts | rts | ||

Line 142: | Line 142: | ||

<pre> | <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 166: | Line 166: | ||

<pre> | <pre> | ||

lda buttons1,x | lda buttons1,x | ||

− | and # | + | and #%00001010 ; Compare Up and Left... |

lsr a | lsr a | ||

and buttons1,x ; to Down and Right | and buttons1,x ; to Down and Right | ||

Line 173: | Line 173: | ||

lda buttons1,x | lda buttons1,x | ||

eor last_frame_buttons1,x | eor last_frame_buttons1,x | ||

− | and # | + | and #%11110000 |

eor last_frame_buttons1,x | eor last_frame_buttons1,x | ||

sta buttons1,x | sta buttons1,x | ||

Line 182: | Line 182: | ||

<pre> | <pre> | ||

lda buttons1,x | lda buttons1,x | ||

− | and # | + | and #%00001111 ; If A & (A - 1) is nonzero, A has more than one bit set |

beq not_diagonal | beq not_diagonal | ||

sec | sec | ||

Line 191: | Line 191: | ||

lda buttons1,x | lda buttons1,x | ||

eor last_frame_buttons1,x | eor last_frame_buttons1,x | ||

− | and # | + | and #%11110000 |

eor last_frame_buttons1,x | eor last_frame_buttons1,x | ||

sta buttons1,x | sta buttons1,x | ||

Line 202: | Line 202: | ||

<pre> | <pre> | ||

lda buttons1,x | lda buttons1,x | ||

− | eor # | + | 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 # | + | eor #%11111111 |

and buttons1,x | and buttons1,x | ||

sta pressed_buttons1,x | sta pressed_buttons1,x |

## Revision as of 20:44, 12 November 2018

## Contents

### 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 port registers.) 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. 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:

last_frame_buttons1 = $00 last_frame_buttons2 = $01 first_read_buttons1 = $02 first_read_buttons2 = $03 readjoy_safe: lda buttons2 sta last_frame_buttons2 lda buttons1 sta last_frame_buttons1 ; Read the controllers once and stash the result jsr readjoy lda buttons2 sta first_read_buttons2 lda buttons1 sta first_read_buttons1 ; Read the controllers again and compare 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: dex bpl cleanup_loop rts

Note that 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.^{[1]}

This is a relatively new technique, and is not entirely supported by emulators.^{[2]}

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 ; to Down and Right beq not_updown1 ; 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

- ↑ Forum post: Rahsennor's OAM-synchronized controller read
- ↑ Forum post: as of May 2016, Nintendulator and Nestopia do not accurately emulate OAM-synchronized controller reading.