Page 1 of 2

help debugging code for controller input

Posted: Sun Dec 16, 2018 11:54 pm
by buschi
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&hil ... ay#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?

Re: help debugging code for controller input

Posted: Mon Dec 17, 2018 4:36 am
by pubby
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: Select all

    jsr readjoy
Then checking buttons is done with an AND instruction:

Code: Select all

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

Re: help debugging code for controller input

Posted: Mon Dec 17, 2018 5:54 pm
by russellsprouts
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: Select all

if (leftButton is pressed AND leftButton was not pressed last frame):
    move left
In code, following pubby's example, that looks like this:

Code: Select all

; 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: Select all

    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:

Re: help debugging code for controller input

Posted: Mon Dec 17, 2018 8:04 pm
by pubby
I didn't explain this well at all, but I was assuming two variables would be used:

Code: Select all

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

    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

Re: help debugging code for controller input

Posted: Mon Dec 17, 2018 8:11 pm
by Nicole
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.

Re: help debugging code for controller input

Posted: Mon Dec 17, 2018 8:38 pm
by tepples
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.

Re: help debugging code for controller input

Posted: Mon Dec 17, 2018 8:52 pm
by Nicole
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.

Re: help debugging code for controller input

Posted: Tue Dec 18, 2018 12:08 am
by buschi
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: Select all

current_controller_state .rs 1
I created a subroutine that reads controller input and saves it to memory:

Code: Select all

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

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

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

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

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.

Re: help debugging code for controller input

Posted: Tue Dec 18, 2018 12:43 am
by pubby
Since the values are constants, you wouldn't store them in RAM. Instead, you'd define them like this:

Code: Select all

A_BUTTON = %10000000
And you'd use them like:

Code: Select all

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.

Re: help debugging code for controller input

Posted: Tue Dec 18, 2018 1:23 am
by buschi
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 :)

Re: help debugging code for controller input

Posted: Wed Dec 19, 2018 12:30 am
by buschi
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: Select all

;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: Select all

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

  ;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

Re: help debugging code for controller input

Posted: Wed Dec 19, 2018 12:47 am
by tokumaru
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: Select all

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

  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.

Re: help debugging code for controller input

Posted: Wed Dec 19, 2018 1:07 am
by pubby
I don't recommend using a 'last_controller_state' variable. Instead, use a 'buttons_pressed' variable as shown earlier.
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: Select all

    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.

Re: help debugging code for controller input

Posted: Wed Dec 19, 2018 8:58 am
by buschi
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

Re: help debugging code for controller input

Posted: Wed Dec 19, 2018 11:52 am
by Nicole
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: Select all

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.