Reading the controller?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

User avatar
Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch » Tue Apr 08, 2008 1:06 pm

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.

User avatar
electro
Posts: 132
Joined: Tue Jan 29, 2008 11:12 am
Location: New York

Post by electro » Tue Apr 08, 2008 2:17 pm

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:

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
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:

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

User avatar
never-obsolete
Posts: 377
Joined: Wed Sep 07, 2005 9:55 am
Location: Phoenix, AZ

Post by never-obsolete » Tue Apr 08, 2008 2:43 pm

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.

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: 
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.
. That's just like, your opinion, man .

User avatar
electro
Posts: 132
Joined: Tue Jan 29, 2008 11:12 am
Location: New York

Post by electro » Tue Apr 08, 2008 2:59 pm

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:

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: 
Seems to work the way I expected, but something tells me this isn't the best way to do this either.

Thanks again,
T

User avatar
Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch » Tue Apr 08, 2008 4:14 pm

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:

Code: Select all

AND #$03  ; mask out A+B bits
CMP #$03  ; see if it equals A+B
BEQ a_and_b_pressed

User avatar
electro
Posts: 132
Joined: Tue Jan 29, 2008 11:12 am
Location: New York

Post by electro » Tue Apr 08, 2008 4:40 pm

Thanks.

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: 

The code above works in nintendulator.

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: 
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.

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

User avatar
electro
Posts: 132
Joined: Tue Jan 29, 2008 11:12 am
Location: New York

Reading Joy Pad 1 & 2 (cave man way)

Post by electro » Fri Apr 18, 2008 10:27 am

Today I decided to try to read the buttons of joypad #2 along with joypad #1.

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
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

User avatar
Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch » Fri Apr 18, 2008 12:12 pm

Joypad 1 works ok in this code, but joypad #2 doesn't.
Your "loop2" is never being jumped to, so none of that new code is ever being executed.

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.

User avatar
electro
Posts: 132
Joined: Tue Jan 29, 2008 11:12 am
Location: New York

Post by electro » Fri Apr 18, 2008 4:20 pm

I see what you're saying. I removed "loop2". Have it working now, thank you.

Will repost new code.

T

Post Reply