NES (6502) Assembler
Jeff Bernard

So, I'm trying to read controller data. I have written a little function thing to handle all this, but uhh... I get problems when I try to program responses for buttons other than the directional buttons.

This is what I've got:

1controller equ $4016 ; controller 1 is at register $4016
2 
3; ----- reads the controller ------------
4 
5readController:
6 ; this bit is required, just ignore if you wish
7 lda #1 ; strobe controller
8 sta controller
9 lda #0 ; reset controller
10 sta controller
11 ; end that bit
12 
13 
14 jsr buttonA ; check buttons by jumping to the sub-routine
15 jsr buttonB
16 jsr buttonSelect
17 jsr buttonStart
18 jsr buttonUp
19 jsr buttonDown
20 jsr buttonLeft
21 jsr buttonRight
22
23 rts ; return from sub-routine
24 
25; ------------------------------------

The buttons on the NES controller are read one a time, in that order. And then the button* sub-routines are all basically the same, like this:

button*:
        ; check if button * is pressed
  lda controller       ; load button state into register A
  and #1               ; bitwise AND with 1
  beq callbackReturn   ; if the AND was true, branch to callbackReturn
                             ; callbackReturn jumps to an "rts" command
                             ; in other words, it's equivalent to a return in C
  
  ; do whatever

  rts                  ; return

Now, whenever that ; do whatever (besides the four directional buttons) involves the loading of a non-immediate value (ie- from a label or from a specific register) or the storing of a value, it sort of freezes the game (or at least prevents the working input from happening).

Goalie Ca

Umm.. your method looks overcomplicated but before i can figure out what might be wrong i need some more details.

So.. you say that controller is register-mapped to address $4016. I assume that each button is a 'bit' (how is the signal stored) 8 buttons = 8-bits?

Otherwise i don't see anything inherently wrong. You don't touch the stack, and you have a simple branch..

Actually.. how far is the branch (in memory distance)? Relative addressing is only 8-bit IIRC.

Jeff Bernard

It may be overly complicated, but I don't currently know of any better ways to do it. I've seen some examples of a loop that reads all 8 buttons, but I have no idea how it works, so I'm staying away from it for now.

Quote:

So.. you say that controller is register-mapped to address $4016. I assume that each button is a 'bit' (how is the signal stored) 8 buttons = 8-bits?

As far as I understand, yes that's how it works. It seems to only read one bit at a time, each time that register is read. I've tried bitwise ANDing it, having only read it once, using an 8 bit binary number, but it doesn't work, so it has to be read 8 times.

Quote:

how far is the branch (in memory distance)?

I'm new to assembly, so let me know if this was not what you meant. It's not very deep, it's within the "main loop", so it only goes 2 branches deep when running.

gameLoop:
  ; check if in VBlank

  ; draw sprite

  jsr readController
  jmp gameLoop

Goalie Ca

I can't recall the 6502 ISA off the top of my head but why not something like this

1;possibly the wrong numbers
2;masks for each button
3buttonA equ #1
4buttonB equ #2
5buttonSt equ #4
6buttonSe equ #8
7buttonUp equ #16
8buttonDo equ #32
9buttonLe equ #64
10buttonRi equ #128
11 
12 
13readController:
14 
15;read value from controller (make sure these are right!)
16 lda #1 ; strobe controller
17 sta controller
18 lda #0 ; reset controller
19 sta controller
20 ; end that bit
21 
22;read value from controller
23 lda controller
24 
25;store this value somewhere convenient.
26 sta controller_status
27 
28;check if button A pushed
29 lda controller_status
30 and buttonA
31 beq butB ;if button a is not pressed.. and will return 0. beq jumps when z flag is zero.. correct!?
32 jsr buttonA_action
33butB:
34 lda controller_status
35 and buttonB
36 beq butC
37 jsr buttonB_action
38butC: ;.. and so on
39 
40 rts ;end of function!

Jeff Bernard

That's what I was talking about by only reading the controller once and then ANDing it with a single binary number. Unfortunately, it does not work.

Goalie Ca

If it doesn't work then maybe you should read the documents and open up your debugger. Test the simplest of cases.. track the stack. check out what happens when you hit that instruction. It is quite possibly you are overwriting your own instructions or perhaps your stack is screwed up.

kikabo

ah 6502, takes me back, like Goalie Ca said, it may be your branch distance.
I think that you can branch back 127 bytes and forward 128.

To keep jumps small it may be better doing it like this :-

   bne dontreturn
   rts
dontreturn: ...

Thomas Harte
Quote:

like Goalie Ca said, it may be your branch distance.
I think that you can branch back 127 bytes and forward 128.

That's JR (jump relative), not JSR (jump to subroutine). JSR takes an absolute, 16bit, address.

I think the problem is most likely that you've misunderstood the NES specs, but I don't know them off hand. Maybe I can help more at lunchtime...

kikabo

eh, bne is a branch which is 8 bit, jump is 16 bit yes, but the potential problem is that there is a "beq callbackReturn", callbackReturn isn't listed and could easily be beyond 128 bytes away from the branch.

Thomas Harte
Quote:

eh, bne is a branch which is 8 bit, jump is 16 bit yes, but the potential problem is that there is a "beq callbackReturn"

I didn't spot the BEQ. I was looking at the JSRs.

Besides that, your code seems to be compliant to the docs here. Does it work if you change it to the code below?

button*:
        ; check if button * is pressed
  lda controller       ; load button state into register A
  and #1               ; bitwise AND with 1
  bne .noReturnNow  ; if the AND was true, avoid the rts that comes next

  rts
.noReturnNow
  
  ; do whatever

  rts                  ; return

I'm not sure if it's a universal trait of 6502 assemblers, but the 65SC02 assembler I used to use treated labels with a dot at the start of their name as local labels, i.e. they're accessible only between one real label (such as buttonA) and the next (presumably buttonB). So it's safe to use them in macros, and have a different .noReturnNow for each of your button functions.

Also, if you want to save a cycle, try this:

button*:
        ; check if button * is pressed
  lda controller       ; load button state into register A
  lsr               ; shift lowest bit into the carry register
  bcs .noReturnNow  ; if the AND was true, branch to callbackReturn
                             ; callbackReturn jumps to an "rts" command
                             ; in other words, it's equivalent to a return in C

  rts
.noReturnNow
  
  ; do whatever

  rts                  ; return

It's not very helpful here, but if you had an 8bit byte and wanted to do different things depending on the status of individual bits then often the fastest way to decompose it is to shift each bit in turn into carry.

Thread #593971. Printed from Allegro.cc