Gamepad code

From NESdev Wiki
Jump to navigationJump to search

For Nesasm

<source lang="6502tasm">

Uncomment the next line if you want to have all your button callbacks to
execute upon "has gone from up to down" rather than "is currently down".
EDGE_BUTTONS = 1
   .org 0x00

.ifdef EDGE_BUTTONS buttons_down .ds 8 .endifdef current_button_sub .ds 2

    .org 0x8000
Need quite a bit of stuff here. See
Init code

get_buttons:

   lda #0x01 ; strobe
   sta 0x4016
   lda #0x00 ; MOAR strobe
   sta 0x4016
   ldx #0

.get_button:

   lda 0x4016
   and #0x01 ; Only want information about the first bit
bne means branch on the zero register being false, which it
will be if the number in the accumulator is non-zero.
   bne .pressed
   .ifdef EDGE_BUTTONS
   lda #0x00
   sta buttons_down, x
   .endifdef
   jmp .end

.pressed:

   .ifdef EDGE_BUTTONS
   lda buttons_down, x
   bne .end
   lda #0x01
   sta buttons_down, x
   .endifdef
   txa
   pha
   asl A
   tax
   lda button_subs + 0x00, x ; Equivalent to non-existent jsr (button_subs, x)
   sta current_button_sub + 0x00
   lda button_subs + 0x01, x
   sta current_button_sub + 0x01
   lda #HIGH(.after_button_sub + 0x01)
   pha
   lda #LOW(.after_button_sub + 0x01)
   pha
   jmp [current_button_sub]

.after_button_sub:

   pla
   tax

.end:

   txa
   inx
   cmp #8
   bmi .get_button
   jmp get_buttons

button_subs: ; Vector table

   .dw do_nothing ; a
   .dw do_nothing ; b
   .dw do_nothing ; select
   .dw do_nothing ; start
   .dw do_nothing ; up
   .dw do_nothing ; down
   .dw do_nothing ; left
   .dw do_nothing ; right

do_nothing:

   rts

</source>

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

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.

The buttons byte buttons should be placed in zero page.

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

When reading from JOYPAD* what is read migh be different from $01/$00. See Controller Port for more information on that subject. 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	slct	strt	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