Reading the controller?
Moderator: Moderators
CMP checks the whole value. AND only checks one bit.
When you read key states from $4016/4017, the returned value is actually $40 (button released), or $41 (button pressed)*. Not $00,$01 like you might expect, so if you do CMP #$01, you will never detect any button presses (since the value is never $01), whereas AND #$01 will work properly.
* usually, anyway. What's really going on is that $4016/7 leave several of the high bits as open bus, so it is possible for other values to be read back as well -- but that's a hypertechnicality. The point is, don't use CMP this way.
When you read key states from $4016/4017, the returned value is actually $40 (button released), or $41 (button pressed)*. Not $00,$01 like you might expect, so if you do CMP #$01, you will never detect any button presses (since the value is never $01), whereas AND #$01 will work properly.
* usually, anyway. What's really going on is that $4016/7 leave several of the high bits as open bus, so it is possible for other values to be read back as well -- but that's a hypertechnicality. The point is, don't use CMP this way.
I understand what you're saying about $40 = no press and $41 = press. But in the original code that I'm referring to earlier in this thread we did this before masking the bits:
So we stripped off the low bit of $40 (button not pressed) and $41 (button press). So now we're comparing the new values stored in the "button_state"? Wouldn't the CMP work in this case?
Here's the whole code:
Thanks for the help,
T
Code: Select all
updatejoy:
LDA #1 ; first, strobe the joypad
STA $4016
LDA #0
STA $4016
LDX #$08 ; set X to 8 (the number of times we want to loop.
joybuttons:
LDA $4016 ; get button state
LSR A ; shift it into the C flag
ROR button_state ; rotate C flag into our button_state variable
DEX ; decrement X (our loop counter)
BNE joybuttons ; jump back to our loop until X is zero
rts
Here's the whole code:
Code: Select all
;Nes Gamepad demo program
;-------------------
; INES header setup
.inesprg 1
.ineschr 0 ; zero because we don't have character data
.inesmir 1
.inesmap 0
.bank 1
.org $FFFA
.dw 0 ; NMI routine
.dw start ; Reset routine
.dw 0 ; IRQ routine
.bank 0
.org $8000
start:
key_a EQU %00000001 ; A button press
key_b EQU %00000010 ; B
key_select EQU %00000100 ; select
key_start EQU %00001000 ; start
key_up EQU %00010000 ; up arrow
key_down EQU %00100000 ; down
key_left EQU %01000000 ; left arrow
key_right EQU %10000000 ; right
ab_pressed EQU %00000011 ; A and B are pressed
button_state EQU $00
sei ; ignore IRQs
cld ; disable decimal mode
ldx #$40
stx $4017 ; disable APU frame IRQ
ldx #$ff
txs ; Set up stack
inx ; now X = 0
stx $2000 ; disable NMI
stx $2001 ; disable rendering
stx $4010 ; disable DMC IRQs
vblankwait1:
bit $2002
bpl vblankwait1
; We now have about 30,000 cycles to burn before the PPU stabilizes.
; Use it to clear RAM. X is still 0...
txa
clrmem: ; this is for the video stuff
sta $000,x
sta $100,x
sta $200,x
sta $300,x
sta $400,x
sta $500,x
sta $600,x
sta $700,x ; Remove this if you're storing reset-persistent data
inx
bne clrmem
vblankwait2:
bit $2002
bpl vblankwait2
; *** CLEAR SOUND REGISTERS ***
lda #$00 ; clear all the sound registers by setting
ldx #$00 ; everything to 0 in the Clear_Sound loop
Clear_Sound:
sta $4000,x ; store accumulator at $4000 offset by x
inx ; increment x
cpx #$0F ; compare x to $0F
bne Clear_Sound ; branch back to Clear_Sound if x != $0F
lda #$10 ; load accumulator with $10
sta $4010 ; store accumulator in $4010
lda #$00 ; load accumulator with 0
sta $4011 ; clear these 3 registers that are
sta $4012 ; associated with the delta modulation
sta $4013 ; channel of the NES
loop:
jsr updatejoy
;Check the state of the right key
lda button_state
cmp #key_right
bne right_not_pressed
jsr right_is_pressed
right_not_pressed:
;Check the state of the left button
lda button_state
cmp #key_left
bne left_not_pressed
jsr left_is_pressed
left_not_pressed:
;Check the state of the up arrow
lda button_state
cmp #key_up
bne up_not_pressed
jsr up_is_pressed
up_not_pressed:
;Check if A and B are pressed
lda button_state
cmp #ab_pressed
bne ab_not_pressed
jsr ab_is_pressed
ab_not_pressed:
jmp loop ;Go back and keep reading the joypad forever
updatejoy:
LDA #1 ; first, strobe the joypad
STA $4016
LDA #0
STA $4016
LDX #$08 ; set X to 8 (the number of times we want to loop, once fo each button)
joybuttons:
LDA $4016 ; get button state
LSR A ; shift it into the C flag
ROR button_state ; rotate C flag into our button_state variable
DEX ; decrement X (our loop counter)
BNE joybuttons ; jump back to our loop until X is zero
rts
right_is_pressed: ; play sound when right arrow is pressed.
lda #$03 ; Enable square 1
sta $4015
lda #$9F ; Envelope
sta $4000
lda #$21 ; Start tone
sta $4003
rts
left_is_pressed:
lda #$03 ; Enable square 1
sta $4015
lda #$9F ; Envelope
sta $4000
lda #$21 ; Start tone
sta $4003
rts
up_is_pressed:
lda #$03 ; Enable square 1
sta $4015
lda #$9F ; Envelope
sta $4000
lda #$21 ; Start tone
sta $4003
rts
ab_is_pressed:
lda #$0F ; Enable all sound channels bits 0-3 of register 4015
sta $4015
lda #%00000111 ; Envelope
sta $400C ; noise channel
lda #%00000001 ; Start tone
sta $400F
rts
Thanks for the help,
T
- never-obsolete
- Posts: 403
- Joined: Wed Sep 07, 2005 9:55 am
- Location: Phoenix, AZ
- Contact:
since all 8 buttons have their state held in button_state, comparing against a bit mask of each of the individual buttons might not work as expected if you don't mask out the others.
what happens if both right and some other button is pushed? button_state will not equal #key_right, even though right is pushed. unless you want "right_is_pressed" to be called when right and only right is pushed.
Code: Select all
;Check the state of the right key
lda button_state
cmp #key_right ; compare
bne right_not_pressed ; branch if not equal to zero
jsr right_is_pressed
right_not_pressed:
Yeah, I see what you guys are saying now... Thanks for the help.
I was experimenting with detecting 2 simultaneous button presses, so I did this:
Seems to work the way I expected, but something tells me this isn't the best way to do this either.
Thanks again,
T
I was experimenting with detecting 2 simultaneous button presses, so I did this:
Code: Select all
ab_pressed EQU %00000011 ; a and b button press
;Check if A and B are pressed
lda button_state
cmp #ab_pressed
bne ab_not_pressed
jsr ab_is_pressed
ab_not_pressed:
Thanks again,
T
Blah that's what I get for taking things out of context XD
Anyway yeah... what I stay still stands kinda. The thing is AND only looks at the bits you mask, whereas CMP looks at the whole byte.
so
AND #$01
BNE blah
will branch if A is pressed... whereas
CMP #$01
BEQ blah
will branch if A is pressed and all other buttons are released. Holding left then pressing A, for example, will fail this check.
Similarly:
AND #$03
BNE blah
branches if A or B (or both) is pressed. Whereas CMP+BEQ will only branch if both are pressed and all other buttons are released.
Basically if you're only interested in a single bit -- then mask out the bit your interested in.
If you want to do A+B pressed while ignoring status of other buttons, then you can use a combination of the two:
Anyway yeah... what I stay still stands kinda. The thing is AND only looks at the bits you mask, whereas CMP looks at the whole byte.
so
AND #$01
BNE blah
will branch if A is pressed... whereas
CMP #$01
BEQ blah
will branch if A is pressed and all other buttons are released. Holding left then pressing A, for example, will fail this check.
Similarly:
AND #$03
BNE blah
branches if A or B (or both) is pressed. Whereas CMP+BEQ will only branch if both are pressed and all other buttons are released.
Basically if you're only interested in a single bit -- then mask out the bit your interested in.
If you want to do A+B pressed while ignoring status of other buttons, then you can use a combination of the two:
Code: Select all
AND #$03 ; mask out A+B bits
CMP #$03 ; see if it equals A+B
BEQ a_and_b_pressed
Thanks.
What about this:
The code above works in nintendulator.
When I did this (below) previously, it also worked in nintendulator but not on the NES console:
I understand what you're saying now.
With this piece of code above, using only CMP + BNE, it worked the way I expected in nintendulator, When I pressed a and b it did "jsr ab_is_pressed" and the other buttons did not effect it. But this did not work on the console.
UPDATE: This (code below) did work nicely on the console.
Thanks again for the great help on that!
T
What about this:
Code: Select all
;Check if A and B are pressed
LDA button_state
AND #$03 ; mask out A+B bits
CMP #$03 ; see if it equals A+B
BNE ab_not_pressed
jsr ab_is_pressed
ab_not_pressed:
When I did this (below) previously, it also worked in nintendulator but not on the NES console:
Code: Select all
;Check if A and B are pressed
LDA button_state
CMP #$03 ; see if it equals A+B
BNE ab_not_pressed
jsr ab_is_pressed
ab_not_pressed:
With this piece of code above, using only CMP + BNE, it worked the way I expected in nintendulator, When I pressed a and b it did "jsr ab_is_pressed" and the other buttons did not effect it. But this did not work on the console.
UPDATE: This (code below) did work nicely on the console.
Code: Select all
;Check if A and B are pressed
LDA button_state
AND #$03 ; mask out A+B bits
CMP #$03 ; see if it equals A+B
BNE ab_not_pressed
jsr ab_is_pressed
ab_not_pressed:
Thanks again for the great help on that!
T
Reading Joy Pad 1 & 2 (cave man way)
Today I decided to try to read the buttons of joypad #2 along with joypad #1.
I just did the same thing for joypad #1, made separate subroutines for reading joypad #2.
Don't know if this is the right way to do this. Joypad 1 works ok in this code, but joypad #2 doesn't.
Maybe I could have just included (consolidated) the code for joypad #2 in the same subroutines used for reading joypad #1, rather than creating separate subroutines the way I did?
Thanks again,
T
Code: Select all
;Nes Gamepad demo program
;-------------------
; INES header setup
.inesprg 1
.ineschr 0 ; zero because we don't have character data
.inesmir 1
.inesmap 0
.bank 1
.org $FFFA
.dw 0 ; NMI routine
.dw start ; Reset routine
.dw 0 ; IRQ routine
.bank 0
.org $8000
start:
key_a EQU %00000001 ; A button press
key_b EQU %00000010 ; B
key_select EQU %00000100 ; select
key_start EQU %00001000 ; start
key_up EQU %00010000 ; up arrow
key_down EQU %00100000 ; down
key_left EQU %01000000 ; left arrow
key_right EQU %10000000 ; right
ab_pressed EQU %00000011 ; A and B are pressed
button_state EQU $00
button_state2 EQU $0F
sei ; ignore IRQs
cld ; disable decimal mode
ldx #$40
stx $4017 ; disable APU frame IRQ
ldx #$ff
txs ; Set up stack
inx ; now X = 0
stx $2000 ; disable NMI
stx $2001 ; disable rendering
stx $4010 ; disable DMC IRQs
vblankwait1:
bit $2002
bpl vblankwait1
; We now have about 30,000 cycles to burn before the PPU stabilizes.
; Use it to clear RAM. X is still 0...
txa
clrmem: ; this is for the video stuff
sta $000,x
sta $100,x
sta $200,x
sta $300,x
sta $400,x
sta $500,x
sta $600,x
sta $700,x ; Remove this if you're storing reset-persistent data
inx
bne clrmem
vblankwait2:
bit $2002
bpl vblankwait2
; *** CLEAR SOUND REGISTERS ***
lda #$00 ; clear all the sound registers by setting
ldx #$00 ; everything to 0 in the Clear_Sound loop
Clear_Sound:
sta $4000,x ; store accumulator at $4000 offset by x
inx ; increment x
cpx #$0F ; compare x to $0F
bne Clear_Sound ; branch back to Clear_Sound if x != $0F
lda #$10 ; load accumulator with $10
sta $4010 ; store accumulator in $4010
lda #$00 ; load accumulator with 0
sta $4011 ; clear these 3 registers that are
sta $4012 ; associated with the delta modulation
sta $4013 ; channel of the NES
loop:
jsr updatejoy
;Check the state of the right key
lda button_state
and #key_right
beq right_not_pressed
jsr right_is_pressed
right_not_pressed:
;Check the state of the left button
lda button_state
and #key_left
beq left_not_pressed
jsr left_is_pressed
left_not_pressed:
;Check the state of the up arrow
lda button_state
and #key_up
beq up_not_pressed
jsr up_is_pressed
up_not_pressed:
;Check the state of the up arrow
lda button_state
and #key_down
beq down_not_pressed
jsr down_is_pressed
down_not_pressed:
;Check if A and B are pressed
lda button_state
AND #$03 ; mask out A+B bits
CMP #$03 ; see if it equals A+B
BNE ab_not_pressed
jsr ab_is_pressed
ab_not_pressed:
;Check if A is pressed
lda button_state
AND #key_a ; mask out bits
BEQ a_not_pressed
jsr a_is_pressed
a_not_pressed:
;Check if b button is pressed
lda button_state
and #key_b
beq b_not_pressed
jsr b_is_pressed
b_not_pressed:
;Check if select button is pressed
lda button_state
and #key_select
beq select_not_pressed
jsr select_is_pressed
select_not_pressed:
jmp loop ;Go back and keep reading the joypad forever
loop2:
jsr updatejoy2
;Check State of Right Button of Joypad #2
lda button_state2
and #key_right
beq right_not_pressed_2
jsr right_is_pressed_2
right_not_pressed_2:
;check state of A button of joypad #2
lda button_state2
AND #key_a ; mask out bits
BEQ a_not_pressed_2
jsr a_is_pressed_2
a_not_pressed_2:
;Check state of b button on joypad #2
lda button_state2
and #key_b
beq b_not_pressed_2
jsr b_is_pressed_2
b_not_pressed_2:
;Check the state of the left button of joypad #2
lda button_state2
and #key_left
beq left_not_pressed_2
jsr left_is_pressed_2
left_not_pressed_2:
;Check the state of the up arrow of joypad #2
lda button_state2
and #key_up
beq up_not_pressed_2
jsr up_is_pressed_2
up_not_pressed_2:
;Check the state of the up arrow of joypad #3
lda button_state2
and #key_down
beq down_not_pressed_2
jsr down_is_pressed_2
down_not_pressed_2:
;Check if select button is pressed
lda button_state2
and #key_select
beq select_not_pressed_2
jsr select_is_pressed_2
select_not_pressed_2:
jmp loop2 ;Go back and keep reading the joypad forever
updatejoy:
LDA #1 ; first, strobe the joypad
STA $4016
LDA #0
STA $4016
LDX #$08 ; set X to 8 (the number of times we want to loop, once fo each button)
joybuttons:
LDA $4016 ; get button state
LSR A ; shift it into the C flag
ROR button_state ; rotate C flag into our button_state variable
DEX ; decrement X (our loop counter)
BNE joybuttons ; jump back to our loop until X is zero
rts
updatejoy2:
LDA #1 ; first, strobe the joypad
STA $4016
LDA #0
STA $4016
LDY #$08 ; set Y to 8 (the number of times we want to loop, once for each button)
joybuttons2:
LDA $4017 ; get button state of joypad #2
LSR A ; shift it into the C flag
ROR button_state2 ; rotate C flag into our button_state2 variable
DEY ; decrement X (our loop counter)
BNE joybuttons2 ; jump back to our loop until Y is zero
rts
right_is_pressed: ; play sound when right arrow is pressed.
lda #$0F ; Enable channels
sta $4015
;lda #%10011101
;sta $400C
lda #%00111101 ; mode, period
sta $400E
lda #%0000000 ; duration
sta $400F
rts
left_is_pressed:
lda #$0F ; Enable channels
sta $4015
;lda #%10011111
;sta $400C
lda #%00010111 ; mode, period
sta $400E
lda #%10000000 ; duration
sta $400F
rts
up_is_pressed:
lda #$0F ; Enable channels
sta $4015
lda #%00111111 ; mode, period low rumble
sta $400E
lda #%0000000 ; duration
sta $400F
rts
down_is_pressed:
lda #$0F ; Enable channels
sta $4015
lda #%00111110 ; mode, period
sta $400E
lda #%11111000 ; duration
sta $400F
rts
a_is_pressed:
rts
b_is_pressed:
rts
select_is_pressed:
rts
ab_is_pressed:
rts
right_is_pressed_2:
rts
left_is_pressed_2:
rts
up_is_pressed_2:
lda #$0F ; Enable channels
sta $4015
lda #%00111111 ; mode, period low rumble
sta $400E
lda #%0000000 ; duration
sta $400F
rts
down_is_pressed_2:
rts
a_is_pressed_2:
lda #$0F ; Enable channels
sta $4015
lda #%00100111 ; enable medium looping with a 1 at bit 5
sta $400C
rts
b_is_pressed_2:
lda #$0F ; Enable channels
sta $4015
lda #%00101111 ; enable slow looping
sta $400C
rts
start_is_pressed_2:
rts
select_is_pressed_2:
jsr start ; Resets sound to the beginning, clears sound registers.
rts
Don't know if this is the right way to do this. Joypad 1 works ok in this code, but joypad #2 doesn't.
Maybe I could have just included (consolidated) the code for joypad #2 in the same subroutines used for reading joypad #1, rather than creating separate subroutines the way I did?
Thanks again,
T
Your "loop2" is never being jumped to, so none of that new code is ever being executed.Joypad 1 works ok in this code, but joypad #2 doesn't.
Be mindful of your program's flow. You probably don't even want a loop2... since your "loop" is the main program loop, you'd want it to look at both P1 and P2.