It is currently Sun May 26, 2019 2:41 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sun Dec 16, 2018 11:54 pm 
Offline

Joined: Tue Dec 11, 2018 9:47 am
Posts: 8
I've finished going through some basic tutorials and am trying to move beyond them. I've been following this tutorial for reading user input:
http://thevirtualmountain.com/nes/2017/ ... input.html

The tutorial demonstrates how to read d-pad input to move some sprites one pixel at a time. Holding down the d-pad results in continuous movement. I'm trying to make a simple puzzle game where I want the player to move their sprite 8 pixels at a time. However this causes the sprites to move way too fast using the tutorial code as-is.

I took a search through the forums and found a similar question:
viewtopic.php?f=10&t=15124&p=183044&hilit=Controller+delay#p183044

The suggestion was to check if the buttons were pressed in the previous frame, and it links to a project with some existing controller code with a "delay auto shift" mechanism. Rather than trying to copy in all this code that I don't understand, I'm trying to do some simple changes. The functionality I want is that if the user presses the D-Pad, the sprite should move exactly 8 pixels in the desired direction, but it should move no further unless the user releases the d-pad and presses it again.

The way I tried to implement this was by allocating some memory in the zero page to store a bit mask for keeping track of previous controller state. I also try to initialize it to #$00. In the subroutine that reads controller button state one by one, I add some logic to check whether the bit representing the button is set. If it is set, that means the button was already pressed and the sprite presumably moved, so I have it proceed without moving the sprite. If the bit was not set, then I set the bit and move the sprite. I haven't added any code to clear the bit once the button is released, so I would expect this code to allow me to move the sprite once in each direction, and then no longer allow me to move after that. However when I build and run, the game allows the ship to continue zooming along as I press and hold the d-pad.

I forked the original project and added a commit with my changes here:
https://github.com/buschi8282/nes-tutor ... 1b62d83bdd

It should be possible to clone the repo and run the project (relevant commit is in branch tutorial-5)

I'm pretty new to this so I'm not sure what the error is, can anybody spot if there is anything wrong with my approach?

Also, are there any tools available for debugging, preferably on Mac (or compatible with WINE)? It would be awesome if there was a way to step through the code and see exactly what is in all the registers and in memory along the way. I feel like I'd be able to figure this out by myself with some basic debugging tools. Are there any tutorials that cover how to debug?


Top
 Profile  
 
PostPosted: Mon Dec 17, 2018 4:36 am 
Offline
User avatar

Joined: Thu Mar 31, 2016 11:15 am
Posts: 514
That tutorial has some helpful stuff but the code is pretty iffy. It's not implemented in a clean or effective way, and it's not how most people code NES games. I wouldn't recommend you copy it.

Most people implement controller reading as described on the wiki: https://wiki.nesdev.com/w/index.php/Controller_Reading

In particular, you read all of buttons into a single byte of memory first, then use bit masks to check if buttons are held down. You don't need to understand the code to set this up - all you need to know is how to use it. The page explains how to detect button presses.

To use that code, at the start of your game loop you'd call the controller reading subroutine:
Code:
    jsr readjoy

Then checking buttons is done with an AND instruction:
Code:
    lda buttons_pressed
    and #BUTTON_LEFT
    beq notPressingLeft
    lda ship_x
    sec
    sbc #8
    sta ship_x
notPressingLeft:

Pretty clean, huh? Also note that this code doesn't involve sprites. It's cleaner to keep sprite code and game logic code separate.

Quote:
Also, are there any tools available for debugging

The Windows version of FCEUX has a debugger, and it works well with WINE. The Mesen emulator has a good debugger too, though I don't know if it works on Mac. Both have documentation on their websites.


Top
 Profile  
 
PostPosted: Mon Dec 17, 2018 5:54 pm 
Offline

Joined: Sun Jan 31, 2016 9:55 pm
Posts: 54
You're on the right track with keeping the previous frame's button state. There's slightly different logic you'll need though. Here's some pseudo-code to help:
Code:
if (leftButton is pressed AND leftButton was not pressed last frame):
    move left


In code, following pubby's example, that looks like this:

Code:
; Move the buttons pressed into the variable for last frame
lda buttons_pressed
sta buttons_pressed_last_frame
; Now get the new buttons pressed
jsr readjoy


Code:
    lda buttons_pressed
    and #BUTTON_LEFT
    beq notPressingLeft
    ; Check that buttons were not pressed last frame
    lda buttons_pressed_last_frame
    and #BUTTON_LEFT
    bne stillPressingLeft
    lda ship_x
    sec
    sbc #8
    sta ship_x
notPressingLeft:
stillPressingLeft:


Top
 Profile  
 
PostPosted: Mon Dec 17, 2018 8:04 pm 
Offline
User avatar

Joined: Thu Mar 31, 2016 11:15 am
Posts: 514
I didn't explain this well at all, but I was assuming two variables would be used:

Code:
buttons_held ; tests buttons that are held down
buttons_pressed ; tests buttons newly pressed this frame

'buttons_pressed' would be set in the readjoy subroutine, like this:

Code:
    ldx buttons_held ; Preserve the last frame's buttons_held in register X

     ; Calculate new buttons_held value (not shown)

    txa ; Load register A with last frame's buttons_held
    eor #$FF
    and buttons_held
    sta buttons_pressed

And then it would be used as in my original code - only 1 AND and 1 branch needed. It's the same idea as your code, russellsprouts, just pre-computed.

Here's the code I use: https://github.com/pubby/nesbase/blob/m ... /gamepad.s


Top
 Profile  
 
PostPosted: Mon Dec 17, 2018 8:11 pm 
Offline

Joined: Sun Mar 27, 2016 7:56 pm
Posts: 216
I use the same approach as pubby.

The wiki also suggests having a buttons_released variable, which contains the buttons released on this frame, but I haven't really found it useful in practice. I generally don't care if a button was released on this frame in particular, I just want to know that it's released, which buttons_held is sufficient for.


Top
 Profile  
 
PostPosted: Mon Dec 17, 2018 8:38 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21401
Location: NE Indiana, USA (NTSC)
If you want to have, say, separate actions for B, A, and B+A, you may need to trigger the B and A actions on release if and only if the B+A action wasn't triggered.

_________________
Pin Eight | Twitter | GitHub | Patreon


Top
 Profile  
 
PostPosted: Mon Dec 17, 2018 8:52 pm 
Offline

Joined: Sun Mar 27, 2016 7:56 pm
Posts: 216
Maybe, though I don't really like putting actions on button releases (unless it's, say, charging up a shot before firing it), since it can feel a little unnatural. Personally, I might implement such a thing by having the B and A actions have a few frames leading into them, where pressing the other button in that timeframe triggers the B+A action instead.


Top
 Profile  
 
PostPosted: Tue Dec 18, 2018 12:08 am 
Offline

Joined: Tue Dec 11, 2018 9:47 am
Posts: 8
Thanks everyone for the feedback and info. I feel like I'm learning a lot but not quite there yet. I'm attempting to rework the controller logic to be consistent with the first basic example in the controller reading wiki article. The work in progress I have can be found here:
https://github.com/buschi8282/nes-tutor ... torial.asm

In the zero page I defined a byte in memory to store current controller state:
Code:
current_controller_state .rs 1


I created a subroutine that reads controller input and saves it to memory:
Code:
ReadController1:
  LDA #$01
  STA controller1
  STA current_controller_state
  LSR A
  STA controller1
.loop:
  LDA controller1
  LSR A
  ROL current_controller_state
  BCC .loop
  RTS


I then have a move ship subroutine that checks bits in current_controller_state to see which buttons are pressed. When I use hardcoded binary numbers to AND with current_controller_state, everything seems to work fine:
Code:
ReadDown:
  LDA current_controller_state
  AND #%00000100
  BEQ EndReadDown

  LDA shipTile1Y
  CLC
  ADC #$08
  STA shipTile1Y
  STA shipTile2Y
  STA shipTile3Y

  LDA shipTile4Y
  CLC
  ADC #$08
  STA shipTile4Y
  STA shipTile5Y
  STA shipTile6Y
EndReadDown:


I want to replace those hardcoded binary values with something similar to the examples in this thread. I defined some data up in the zero page like this:
Code:
a_button .rs 1
b_button .rs 1
select_button .rs 1
start_button .rs 1
up_button .rs 1
down_button .rs 1
left_button .rs 1
right_button .rs 1

  LDA #%10000000
  STA a_button
  LDA #%01000000
  STA b_button
  LDA #%00100000
  STA select_button
  LDA #%00010000
  STA start_button
  LDA #%00001000
  STA up_button
  LDA #%00000100
  STA down_button
  LDA #%00000010
  STA left_button
  LDA #%00000001
  STA right_button


However if I try to use these for the AND operation, I get crazy results. Some buttons will become disabled, others will cause the ship to move around diagonally when it should be moving horizontally or vertically. In the code in git I have comments describing the unexpected side effect for each button handler, but here is a single example:
Code:
ReadDown:
  LDA current_controller_state
  AND #down_button ; enabling this causes up and down to do nothing, right
                   ;button moves ship diagonal down/right, left button works as
                   ;expected
  BEQ EndReadDown

  LDA shipTile1Y
  CLC
  ADC #$08
  STA shipTile1Y
  STA shipTile2Y
  STA shipTile3Y

  LDA shipTile4Y
  CLC
  ADC #$08
  STA shipTile4Y
  STA shipTile5Y
  STA shipTile6Y
EndReadDown:


Since using hard coded binary values in the AND operation gives me expected results, I believe the ReadController1 subroutine is working as expected. I suspect the problem is with the way I am defining values like down_button. I'm a little shaky on some of the different addressing modes, but I believe prefixing it with # with a call like
Code:
AND #down_button
would use immediate addressing mode and use the value stored in memory for the AND operation to compare stored value of #%00000100 with the contents of accumulator A. I'm not sure what kind of problem would cause the strange behavior I'm seeing.

As far as debugging, I've gotten FCEUX and something else called nintaco to run on my machine. I'm able to open some debug tools which I'll need to spend some time on to learn, but for some reason I'm not able to get controller input working on either emulator. I'll spend some time to try troubleshooting that.


Top
 Profile  
 
PostPosted: Tue Dec 18, 2018 12:43 am 
Offline
User avatar

Joined: Thu Mar 31, 2016 11:15 am
Posts: 514
Since the values are constants, you wouldn't store them in RAM. Instead, you'd define them like this:
Code:
A_BUTTON = %10000000


And you'd use them like:
Code:
AND #A_BUTTON


The way you were doing it, you were ANDing the address of the variables. So for example, if 'button_a' was located at $0039 in memory, your code would be equivalent to AND #$39.

Your code would have worked had you used zeropage or absolute addressing (no # symbol). 'AND button_a' would have worked. Still, you should do it the other way using constants.


Top
 Profile  
 
PostPosted: Tue Dec 18, 2018 1:23 am 
Offline

Joined: Tue Dec 11, 2018 9:47 am
Posts: 8
Thanks, changing to constants solved the problem. I had thought I was using the right addressing mode, I guess I'll need more hands on practice to really understand them though. I'll keep plugging away tomorrow :)


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 12:30 am 
Offline

Joined: Tue Dec 11, 2018 9:47 am
Posts: 8
I made some good progress tonight. I set it up so the up button has the kind of movement I want. I'm trying to do something similar to Tetris where the piece will move one square when pressing direction key, then pause slightly, then continue a sustained movement if you keep holding the button down. I've implemented this for the up button, while leaving other buttons as-is for now. The code changes are here:
https://github.com/buschi8282/nes-tutor ... 6a6ad50d20

I believe the NMI interrupt where the controller read subroutine gets called occurs once per frame, and that on NES we have 60 frames per second. I define some counters and delay fields here:
Code:
;counter to keep track of how many frames have elapsed since initial press of up
up_button_counter .rs 1
;counter to keep track of how many frames since last movement while in sustained
;motion
up_button_active_counter .rs 1

;16 frame delay from initial movement to sustained movement
BUTTON_ACTIVE_DELAY1 = $10
;4 frame delay between sustained movement to throttle the speed of the sprites
BUTTON_ACTIVE_DELAY2 = $04



Here is the code I use to determine whether to move the ship based on current and previous controller input. with this implementation I think I would need to have separate sets of counters for each of the directional buttons and duplicate a lot of this logic. Is there a smarter way of doing this in assembly or is that pretty much par for the course?

Code:
ReadUp:
  LDA current_controller_state
  AND #UP_BUTTON
  BEQ EndReadUp ; do nothing if up is not pressed

;check if up is already pressed
  LDA last_controller_state
  AND #UP_BUTTON
  BNE UpAlreadyHeld ; if it's already pressed skip counter initialization

;if we make it to here, this is the initial up button press
;so we set the up button counters to zero
  LDA #$00
  STA up_button_counter
  STA up_button_active_counter

  ;this code is to do an unconditional branch. there must be better way to do
  ;this
  LDA #$00
  CMP $00
  BEQ MoveTheShipUp ;skip code to increment counter

UpAlreadyHeld: ;if we make it here, up is already held down so we
;increment counter

  INC up_button_counter
  LDA up_button_counter
  CMP #BUTTON_ACTIVE_DELAY1
  BCC EndReadUp ; if button is held down less than delay do nothing

  ;if we make it here, we've already completed the initial delay and are in
  ;sustained movement. let's throttle speed of movement a little bit
  INC up_button_active_counter
  LDA up_button_active_counter
  CMP #BUTTON_ACTIVE_DELAY2
  BCC EndReadUp

MoveTheShipUp:
  LDA shipTile1Y
  SEC
  SBC #$08
  STA shipTile1Y
  STA shipTile2Y
  STA shipTile3Y

  LDA shipTile4Y
  SEC
  SBC #$08
  STA shipTile4Y
  STA shipTile5Y
  STA shipTile6Y

  ;reset sustained movement throttle counter
  LDA #$00
  STA up_button_active_counter
EndReadUp:


Please let me know if anybody has any feedback or suggestions on how to improve my coding style. One thing I realized with my current implementation is that when in sustained movement, the counter will eventually overflow back to zero. The project I want to make is a single screen game where you would hit the boundaries long before this happens. However if someone is doing a platform game, how do they control how quickly the character moves across the screen as the player holds down the d-pad button?


Also, I notice there is no unconditional branch in this instruction set. I did a LDA and CMP with same value to make BEQ act like an unconditional branch. Is this how most folks do this or is there a smarter way?
Code:
  ;this code is to do an unconditional branch. there must be better way to do
  ;this
  LDA #$00
  CMP $00
  BEQ MoveTheShipUp ;skip code to increment counter


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 12:47 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11357
Location: Rio de Janeiro - Brazil
The 6502 indeed doesn't have an unconditional branch, but you can just use JMP instead of that convoluted solution you used. Sometimes we can use the conditional branch instructions to simulate unconditional branching, if we know for sure the state of one of the Z, C, N or V flags.

In the code you posted, for example, you do this:
Code:
  LDA #$00
  STA up_button_counter
  STA up_button_active_counter

In this case you could just put a BEQ after these lines and it would always branch, since the Z flag is guaranteed to be set because of the LDA #$00 instruction (loading $00 sets the Z flag, and the STA instructions don't affect it in any way). It's good practice to comment these cases, so that you don't accidentally forget that you need a specific condition to be true for the code to work.

In your case you could do something like this:
Code:
  LDA #$00
  STA up_button_counter
  STA up_button_active_counter
  ;LDA #$00 (don't need this because Z is already set)
  BEQ MoveTheShipUp ;branch always

The second LDA #$00 is commented because Z is already set, but its presence makes it clear that this is an optimization, and that the instruction below is supposed to always branch. A "branch always" comment like I put there doesn't hurt either.

If you don't want to take any risks with the status flags, just use JMP.


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 1:07 am 
Offline
User avatar

Joined: Thu Mar 31, 2016 11:15 am
Posts: 514
I don't recommend using a 'last_controller_state' variable. Instead, use a 'buttons_pressed' variable as shown earlier.

Quote:
I made some good progress tonight. I set it up so the up button has the kind of movement I want. I'm trying to do something similar to Tetris where the piece will move one square when pressing direction key, then pause slightly, then continue a sustained movement if you keep holding the button down.

If you use a 'buttons_pressed' variable, you can spoof button presses on a timer. Here's how I did it in one of my projects:
Code:
    lda p1_buttons_pressed
    and #BUTTON_DPAD
    beq notPressingDPad
    lda #0
    sta p1_buttons_repeat_timer
notPressingDPad:
    inc p1_buttons_repeat_timer
    lda p1_buttons_repeat_timer
    cmp #30
    bcc notHeldLongEnough
    lda p1_buttons_held
    and #BUTTON_DPAD
    ora p1_buttons_pressed
    sta p1_buttons_pressed
    lda #26
    sta p1_buttons_repeat_timer
notHeldLongEnough:

It uses a single timer and handles all 4 DPad buttons. Put this code in your 'readjoy' subroutine. Then, your game logic code only needs to check 'buttons_pressed'; nothing more.


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 8:58 am 
Offline

Joined: Tue Dec 11, 2018 9:47 am
Posts: 8
Thanks I'll give it a shot tonight! One question though - if the user is holding down the d-pad wouldn't lines 4 and 5 reset the counter to zero before the increment happens on lines 6 and 7 each time the subroutine is called? If so I can figure out logic to prevent that, I just want to make sure I'm reading it correctly


Top
 Profile  
 
PostPosted: Wed Dec 19, 2018 11:52 am 
Offline

Joined: Sun Mar 27, 2016 7:56 pm
Posts: 216
No, because p1_buttons_pressed represents when buttons are pressed starting this frame, not when they're held down. So, it'll only be reset the first frame it's being held.

To put it another way, if we look at a single button's state here, to simplify things:
Code:
FRAME     0123456789012
held      0001111110000
pressed   0001000000000
released  0000000001000

The button here is held down from frame 3 to frame 8, so its bit would be set in pressed only on frame 3, the first frame it's pressed, and it'd be set in released only on frame 9, the first frame it's released.


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 8 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