Gamepad code

From NESdev Wiki
Revision as of 18:52, 28 February 2016 by Rainwarrior (talk | contribs) (→‎For Nesasm: removing this code block because is very strange and of dubious use for beginners or as reference (e.g. a jump table entry for each bit read, instead of just storing them? really??))
Jump to navigationJump to search

For ca65

I am a big fan of structured code, so this coding style will be a bit different from the nesasm version above. This describes the nameless loops used in the code you see below.

Reading pressed buttons

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. It uses a 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
  sta JOYPAD1
  sta buttons
  lsr a        ; now A is 0
  sta JOYPAD1
:
  lda JOYPAD1
  lsr a	       ; bit0 -> Carry
  rol buttons  ; Carry -> bit0; bit 7 -> Carry
  bcc :-
  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
:
  lda JOYPAD1
  and #$03      ; ignore bits other than controller
  cmp #$01      ; Set carry if and only if nonzero
  rol buttons1  ; Carry -> bit0; bit 7 -> Carry
  lda JOYPAD2   ; Repeat 
  and #$03
  cmp #$01
  rol buttons2  ; Carry -> bit0; bit 7 -> Carry
  bcc :-
  rts

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.