It is currently Sat Dec 15, 2018 11:26 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 54 posts ]  Go to page 1, 2, 3, 4  Next
Author Message
 Post subject:
PostPosted: Mon Feb 04, 2008 7:11 am 
Offline
User avatar

Joined: Tue Jan 29, 2008 11:12 am
Posts: 132
Location: New York
I have some code that was mostly given to me in another post, which I learned tremendously from. However now having some problems actually putting the pieces together correctly.

If using the emulators, would the arrow keys on my PC automatically replace the arrow keys from a real nes gamepad? Should I be using a PC joystick?

I compiled with nesasm and using Nintendulator. (But also tried Jnes, Nestopia.)

I wanted to use the code to test the reading of game pad keys and assumed the emulator made use of the arrow keys on the PC.

In the code I just wanted to press the right arrow and make some noise.

I left in the subroutine lables for each of the buttons, however I just wanted to test one button (the right arrow). That's why the other key_press subroutines are empty.

Code:
;Nes Gamepad demo program
;-------------------

   .inesprg 1
   .ineschr 0 

   .bank 1        ; don't know if I need this.
   .org $FFFA
   .dw 0
   .dw updatejoy ; should my code start here?
   .dw 0

   .bank 0
   .org $8000

;;Equates for masking

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
         
button_state          EQU       $00 ; my button_state variable

   
updatejoy:

  LDA #1         ; first, strobe the joypad
  STA $4016
  LDA #0
  STA $4016

  ; now we're going to loop 8 times (once for each button), and
  ; rotate each button's status (in the carry flag) into our button_state variable

    LDX #$08              ; set X to 8 (the number of times we want to loop)
  joyloop:
    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 joyloop          ; jump back to our loop until X is zero

  RTS

  LDA button_state
  AND key_right  ; masking using AND. Check right arrow press
  BNE right_is_pressed ; branch to subroutine

  LDA button_state
  AND key_left  ; check left arrow press
  BNE left_is_pressed

  LDA button_state
  AND key_down  ; check down arrow press
  BNE down_is_pressed

  LDA button_state
  AND key_up  ; check up arrow press
  BNE up_is_pressed

  LDA button_state
  AND key_start  ; check start button press
  BNE start_is_pressed

  LDA button_state
  AND key_select  ; check select button
  BNE select_is_pressed

  LDA button_state
  AND key_b  ; check b button press
  BNE b_is_pressed

  LDA button_state
  AND key_a  ; check a button press
  BNE a_is_pressed

right_is_pressed: ; play sound when right arrow is pressed.

   lda #$FF   ;
   sta $4000  ;

   lda #%11011011  ;
   sta $4001  ;

   lda #$A5
   sta $4002

   lda #$AB
   sta $4003

   lda #%00000001
   sta $4015



   jmp updatejoy ; is this jmp right?

left_is_pressed:

rts

down_is_pressed:

rts

up_is_pressed:

rts

start_is_pressed:

rts

select_is_pressed:

rts

b_is_pressed:

rts

a_is_pressed:

rts


Thanks,
T


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 04, 2008 7:59 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20877
Location: NE Indiana, USA (NTSC)
electro wrote:
If using the emulators, would the arrow keys on my PC automatically replace the arrow keys from a real nes gamepad?

Yes. You might want to configure the controls with a known-working rom to make sure that the directions work before you try it with your own creation.

Quote:
Should I be using a PC joystick?

Yes, but watch the brand. Some gamepads like to press diagonal too easily. To start with, I would recommend using a PlayStation controller through a PS1 to USB adapter. I get the authentic Nintendo feel by using an N64 controller through an N64 to USB adapter, but it's harder to find those adapters than it was in 1999 when I bought mine.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 04, 2008 12:03 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11012
Location: Rio de Janeiro - Brazil
The RTS's are pretty much fucking everything up...

If you start at updatejoy, which is a subroutine, when the RTS is executed, where do you thing the code will return to? That's right, there is no place to return to, because the subroutine was not called (code execution just started there), thus a return address was not placed on the stack. The RTS instruction does not care about this though, it just fetches from the stack what it assumes to be a return address and jumps to it. Since there wasn't a return address on the stack, a junk value was fetched and the program crashed, because the CPU tried to execute random data instead of an actual program.

You also have RTS's after the code that handles each button, but again you didn't JSR there, you just branched with BNE. So, again, no return address was placed on the stack. You can only use RTS if you JSR'ed to the location where the RTS instruction is.

Another serious problem is that you don't do any sort of initialization, you simply try to read the joypad as soon as the program starts. Actually, all the logic is pretty much wrong. The following is an example of a more correct program structure:

Code:
start:
   ;INITIALIZATION CODE HERE

;The following is the main loop

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:

   jmp loop ;Go back and keep reading the joypad forever

;Now come all the subroutines used by the main code:

updatejoy:
   (...)
   rts

right_is_pressed:
   (...)
   rts

left_is_pressed:
   (...)
   rts

down_is_pressed:
   (...)
   rts

up_is_pressed:
   (...)
   rts


Of course, the above will not compile, it's just to show what the correct program structure to achieve what you want would be.

First of all, you need to perform some basic initialization: disable interrups, turn rendering off (since you're not using the PPU), initialize the stack, and so on. The wiki has some basic initialization code, IIRC, so you can take a look there.

Then you need a loop. The player pressing buttons is something that happens over time, so you must constantly read the joypad. A part of the code that repeats is called a loop. Inside this loop, you must perform the actions that repeat, in your case, that's reading the joypad and acting accordingly to the buttons that are pressed, calling the different routines.

Now the subroutines are proper subroutines, called with JSR and finished with RTS. The part that checks for each individual button was modified to skip over the call to the subroutine in case the button was NOT pressed. This is a common assembly "trick".

Programs are usually composed by the main code, in this case starting at "start", and by supporting subroutines, which are pieces of code that can be called by the main program. You'll eventually get this.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 04, 2008 12:16 pm 
Offline
User avatar

Joined: Tue Jan 29, 2008 11:12 am
Posts: 132
Location: New York
I think I see what you're saying. (I haven't really seen good examples of code that demonstrate these things as well as it's being demonstrated here.) Very cool.

I'll check out the "initialization" info on wiki. That was something I have to get straight.

I appreciate the layout you provided. I'll digest it and repost.

Thanks again,
T


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 04, 2008 7:11 pm 
Offline
User avatar

Joined: Tue Jan 29, 2008 11:12 am
Posts: 132
Location: New York
I see what you've done to fix the code. It makes sense to me.

I'm trying now to find good examples of setting up the "initialization".

Can anyone point me to an example somehwere of the initialization, which I can study? (I would prefer to study some examples, then come back with some semi-intelligent questions to post here.)

I use nesasm. Would the initialization be different for each assembler?

Right now I'm just interested in keypad and sound, no video (yet).

Thanks again,
T

P.S. I really did learn a great deal from the previous posts here and in my other thread. KNowing what's happening with the instructions, and then putting together the correct "structure", 2 different things!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 04, 2008 9:13 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11012
Location: Rio de Janeiro - Brazil
electro wrote:
Can anyone point me to an example somehwere of the initialization, which I can study?

Didn't you find the wiki page? Here it is, in case you didn't. You can probably just copy it all, although you should try to understand what the coe is doing.

Quote:
I use nesasm. Would the initialization be different for each assembler?

Simple code like that doesn't change much from assembler to assembler... You may find some diference regarding the labels, because some assemblers support local labels (I believe that in the wiki code these are the labels starting with "@"), temporary labels, and so on, while others don't. If something goes wrong, changing the names of the labels to simple ones (no special characters or anything) and keeping them unique should solve the problem.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Feb 04, 2008 9:36 pm 
Offline
User avatar

Joined: Tue Jan 29, 2008 11:12 am
Posts: 132
Location: New York
Thanks for the link.

I have never seen any of this "init" stuff in any of the example code I've looked at. No wonder they didn't work correctly.

What kind of surprises me is that this "init" stuff is extremely important part of an nes code, yet it seemed to be the most difficult thing to find clear examples of.

I love nesdev wiki. (Have you ever wondered what the word "wiki" is? "Wiki" ?! New words for the 21st century. Like "23 skadoo" was in 1924.

I'll really be studying it.

Thank you again for your great help, it's very much appreciated.

T


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 07, 2008 6:10 am 
Offline
User avatar

Joined: Tue Jan 29, 2008 11:12 am
Posts: 132
Location: New York
Here's my latest mess:

Code:
;Nes Gamepad demo program
;-------------------

   .inesprg 1
   .ineschr 0 

   .bank 1
   .org $FFFA
   .dw 0
   .dw start
   .dw 0

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

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

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:

   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)
    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 #$FF   ;
   sta $4000  ;

   lda #%11011011  ;
   sta $4001  ;

   lda #$A5
   sta $4002

   lda #$AB
   sta $4003

   lda #%00000001
   sta $4015

   rts

left_is_pressed:

              lda #$FF   ;
   sta $4000  ;

   lda #%11011011  ;
   sta $4001  ;

   lda #$A5
   sta $4002

   lda #$AB
   sta $4003

   lda #%00000001
   sta $4015

   rts


It complies but does not work correctly. (Suppose to play a sound when the right or left keypad button is pressed.

(I left the other keypad buttons out for testing purposes).

Thanks for any feedback.

T


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 07, 2008 6:29 am 
Offline
User avatar

Joined: Wed Aug 03, 2005 3:15 pm
Posts: 394
Can I recommend splitting this thread from this post...

http://nesdev.com/bbs/viewtopi ... ght=#30612

... and making it a new topic, with an appropriate name about controller coding? There is some great info here, and it would be alot easier for any newer people to find.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 07, 2008 6:35 am 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
Best way to debug things is start small and expand. When you have a bug, it could be anywhere, so you have to narrow it down systematically. Insert this debug code at various points, first just after NES initialization, then in a controller handler routine. If it doesn't beep, then it's not getting there.

Best way to debug things is start small and expand. Insert this debug code at various points, first just after NES initialization, then in a controller handler routine. If it doesn't beep, then it's not getting there.

Code:
beep:
   lda #$01   ; Enable square 1
   sta $4015
   lda #$9F   ; Envelope
   sta $4000
   lda #$21   ; Start tone
   sta $4003


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 07, 2008 6:44 am 
Offline
User avatar

Joined: Tue Jan 29, 2008 11:12 am
Posts: 132
Location: New York
blargg wrote:
Best way to debug things is start small and expand. When you have a bug, it could be anywhere, so you have to narrow it down systematically. Insert this debug code at various points, first just after NES initialization, then in a controller handler routine. If it doesn't beep, then it's not getting there.

Best way to debug things is start small and expand. Insert this debug code at various points, first just after NES initialization, then in a controller handler routine. If it doesn't beep, then it's not getting there.

Code:
beep:
   lda #$01   ; Enable square 1
   sta $4015
   lda #$9F   ; Envelope
   sta $4000
   lda #$21   ; Start tone
   sta $4003


Great idea. I'll try that.

I'll also post as the other guy suggested above.

Before I do, I was wondering if I have to clear the sound registers, or reset the frame counter/clock divider at the init of my code?

(I have been reading a little over at nullsleep's nes page).

Thanks again,
T


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 07, 2008 6:56 am 
Offline
User avatar

Joined: Tue Jan 29, 2008 11:12 am
Posts: 132
Location: New York
Tried what you suggested, inserting the beep debugger at various places. It works if placed directly after my init stuff, and also in my main loop.

It doesn't work anywhere in my subroutines.

This is a great thing to have learned (the debugger thing).

Thanks again,
T


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 07, 2008 9:24 am 
Offline
User avatar

Joined: Tue Jan 29, 2008 11:12 am
Posts: 132
Location: New York
Here is the latest code. This was all pieced together from code examples by other members here. Although it was all cut and pasted, etc. I still learned what each piece of code is doing, thanks to their help.

The init stuff is something I still have to go over more. Anyway with the help from blargg pointing out that in my masks I was missing the "#". So instead of doing immediate addressing (loading a number), it was reading;

Code:
AND keypress ; absolute adressing. Looks for the "keypress" address in memory, that's not good!


Should be;
Code:
AND #keypress ; immediate addressing. Masks with the designated binary number for the particular keypress.


Here's the code which compiled and was loaded into Jnes (I still will also test in Nintendulator an others).

Code:
;Nes Gamepad demo program
;-------------------

   .inesprg 1
   .ineschr 0 

   .bank 1
   .org $FFFA
   .dw 0
   .dw start
   .dw 0

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

   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)
    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 #$01   ; Enable square 1
   sta $4015  ; this register gets written to first to enable sound channel. Then come the other registers.

   lda #$9F   ; Envelope
   sta $4000
   lda #$21   ; Start tone
   sta $4003
   
   rts
   

   
left_is_pressed:

                lda #$01   ; Enable square 1
   sta $4015  ; this register gets written to first to enable sound channel. Then come the other registers.
   lda #$9F   ; Envelope
   sta $4000
   lda #$21   ; Start tone
   sta $4003

   rts


It doesn't work in Nintenduator, but does work in Nestopia and Jnes.

Now I have to figure out how to stop the sound immediately when the button is up. Now the way it is the sound plays for a set duration.

I guess the "pseudo" code would be something like;

If button_press = #0
then goto "stop sound"

Or would it be some kind of timing thing? (Like play sound for T=X?)

Maybe BEQ to a subroutine that clears the $4015 register?
(In other words clear the $4015 register when the button is up?)

Thanks,
T


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 07, 2008 11:05 am 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
Stop a channel by clearing its length counter. One way is to clear that bit of $4015. Another way is to enable the length counter and load it with the appropriate duration when starting the note, then let it count down to 0 normally. Set bit 4 of $4000 to enable the length counter, then write the appropriate value to $4003 to load the length counter (as well as set the high 3 bits of the period). Yet another is to use the volume envelope by clearing bit 4 of $4000 and setting the decay rate in the low 4 bits. Also, you should write $08 to $4001 (and $4005) to disable the sweep, otherwise they'll silence low notes (really, any value with bit 3 set and bit 7 clear).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 07, 2008 5:10 pm 
Offline
User avatar

Joined: Tue Jan 29, 2008 11:12 am
Posts: 132
Location: New York
blargg wrote:
Stop a channel by clearing its length counter. One way is to clear that bit of $4015. Another way is to enable the length counter and load it with the appropriate duration when starting the note, then let it count down to 0 normally. Set bit 4 of $4000 to enable the length counter, then write the appropriate value to $4003 to load the length counter (as well as set the high 3 bits of the period). Yet another is to use the volume envelope by clearing bit 4 of $4000 and setting the decay rate in the low 4 bits. Also, you should write $08 to $4001 (and $4005) to disable the sweep, otherwise they'll silence low notes (really, any value with bit 3 set and bit 7 clear).


Thank you. I'll digest this some more and re-post.

T


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 54 posts ]  Go to page 1, 2, 3, 4  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group