First time Homebrewer

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
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

bunnyboy wrote:Newbies need simplicity to learn first, being "professional" is overwhelming instead of teaching.
The thing is I'm not even sure this is simple... We've seen so many newbies stumbling with this method of reading the controller... it's very confusing because of the spaghetti code it generates.
3gengames
Formerly 65024U
Posts: 2281
Joined: Sat Mar 27, 2010 12:57 pm

Post by 3gengames »

Comment a controller reading routine and it shouldn't be hard at all. You need to have an idea of simple instructions, X indexing, and memory locations and loops and you can read a controller correctly. If you're doing stuff on the screen from input, you should be able to understand the routine to read it. The $4016 hardcoded reads don't make it easier, it just makes people think "Well what was different from last time? It's the same thing."
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

I know you basically said the same thing as the tut but I got your way more. Thanks ill keep you posted if I have more trouble.
Glad I could help, and it sounds like you're doing some exploring which is nice to do.

Re: Controller Reading.

I'll explain the other way. He's mentioned C, so I assume he's done some stuff in it. Bit shifts may already be in his repertoire, and if not it may not be as huge a leap for him as others.

We've got the benefit of being a forum, so we can tailor our teaching to the person.

Tokumaru, I actually wouldn't mind seeing a tutorial by you. You know this stuff better than I do AND provide shorter (and easier to understand) explanations than I do. Else one day I really will write my own. And I'll teach things in a different order than this post, I guess.

Camronas, this way is worth reading about even if you have already got the other way working. Also this turned into a long post (I warned of this!), and it may help to read a little at a time to let it sink in.

A quick note about this post: I haven't had time to thoroughly check it for errors. I promise I'll come back and fix it if I find some later, but just be aware there may be parts where there are nine bits in an LDA instruction or something. Hopefully nothing more serious than that, but I wrote this all in one go with not a lot of proofreading...

Edit: Also, shoot, I just realized I never mentioned how to USE the buttons variable once this code is finished. I'll add a quick note at the end.
I cant see the difference between the two function to be able to see what needs to change, this code works though.
That is correct. There is no difference between how you get the bit for each new button. :D

$4016 is a special address for interfacing with NES hardware. Every time you read from it, its value is updated to the state of the next button. So even in this string of code that looks like it would be redundant:

Code: Select all

lda $4016
lda $4016
The accumulator can actually be given a different value each time because it is moving to different buttons upon read.

The order is always:
A
B
Select
Start
Up
Down
Left
Right

What this code does:

Code: Select all

  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons 
is resets the button state you will get from a $4016 read back to the first button. (A)

I will now pause on controller reading to review some things about the 6502 that are important to understand for the other controller method.

There is a byte internal to the 6502 CPU. Each bit represents a different thing that is either true (1) or false (0). These bits are called the processor status flags.

Code: Select all

From Assembly in One Step
       bit ->   7                           0
              +---+---+---+---+---+---+---+---+
              | N | V |   | B | D | I | Z | C |  <-- flag, 0/1 = reset/set
              +---+---+---+---+---+---+---+---+
When the CPU does anything, the individual bits of that byte are set (to 1) or cleared (to 0) based on the result of the last instruction. We will only care about Z and C right now. (By the way, the order of the bits in this byte is not important to us at all right now. You just need to know that they exist and a little about how they update)

The easy one to explain is Z. It is set if the result of the last instruction was zero, and cleared if the result was not zero.

Code: Select all

lda #$00; Z is set.
lda #$FF; Z is cleared.

lda #$XX;where XX is anything but 00. Z is cleared
Here is something a little more tricky that will set Z.

Code: Select all

lda #$01
sec;We set the carry before a subtract
sbc #$01;1-1=0. 
;The result of the last operation was 0. Z is set.
One more:

Code: Select all

ldx #$01;X is another register that holds a value like A. There is also Y.
dex;Subtracts one from X.
1-1 = 0; Z is set.
Now I will teach a few more instructions that shift the bits in A. The easy two are ROR, and ROL. ROR ROtates all the bits in a byte to the Right. ROL ROtates them to the Left.

As you know a byte is eight bits. But ROR and ROL actually work on NINE bits. Say, WHAT? :lol:

The ninth bit is C in the processor status byte. So let's say we have a this byte in A: #%10000000

It looks like this to ROL and ROR:

Code: Select all

C = The carry bit. If cleared, it is zero. If set it is 1.
C10000000C
When you ROR, C goes to the left bit of A, that bit that was where C just moved goes one bit to the right, etc until the right most bit in A moves into C.

Let's look at this code:

Code: Select all

clc; Clears the carry flag. C is 0.
lda #%1000000R;(R is 0. I have marked it R for a reason)
ROR a;Rotates the bits in A to the right.
;A now has this byte: #%01000000
;What about the carry? It gets the bit that was on the very right of A. (marked R in the example).
;R was 0. C is now 0.
;The carry is now clear.
Sort of interesting. Let's look at this code:

Code: Select all

sec;Sets the carry flag. C is 1.
lda #%10000000;No need to mark R now, right?
ROR a
;A now has this byte: #%11000000
;The carry was set instead of cleared when we started, so a 1 was moved into A's leftmost bit instead of a zero.

;Because a zero was in A's rightmost bit, the carry is now clear.
If you get ROR, ROL is not any different except it moves left. Here is a quick example anyway:

Code: Select all

clc
lda #%10000000
rol a
;A = #%00000000
;Carry is set.
Got it? Cool.

Now for two that are a little different. LSR and ASL. LSR shifts bits right, but puts a ZERO into the left most bit of the byte instead of what was in C. The right most bit of A is still moved into C, however.

ASL shifts bits left but puts a ZERO in the right most bit of the byte instead of what was in C. The left most bit of A is still moved into C.

Similar enough, right? Here are some examples just in case.

Code: Select all

sec
lda #%10000000
ASL a
;A now has #%00000000. The zero flag was set! Don't forget about that.
;C has 1.
Another quick one.

Code: Select all

clc
lda #%10000000
ASL a
;A now has #%00000000.
;C has 1.
So as you can see, the state of the carry flag before ASL actually doesn't matter, unlike with ROL.
LSR is similar.

Code: Select all

;State of carry doesn't matter
lda #%00000010
lsr a
;A is #%00000001
;Carry is clear.

Code: Select all

;State of carry doesn't matter
lda #%00000001
lsr a
;A is #%00000000
;Carry is set.
You now know almost everything you need to know to understand the "standard" joypad reading code. Except the bitwise operators and branching (which you may know from Nerdy Nights by now). If you already know & (AND), |(ORA) and ^(EOR) from C, you're good on the bitwise operators. If not, please read this post.

Let's construct a simple endless loop using a branch.

Code: Select all

label:
lda #$00
beq label;This jmps to label if Z is set. It continues down if Z is clear.
And another using a different branch.

Code: Select all

label:
lda #$FF
bne label;This jmps to label if Z is clear. It continues down if Z is set.
That was pretty concise, but it should make sense if you think about it.

Now let's construct a regular loop. Sort of like the for loop in C.

Code: Select all

ldx #$02
label:
lda $4016
dex
bne label
clc;Just extra code.
What will this do? Read from $4016 twice. We start with 2 in X. We read from $4016. We subtract 1 from X. 2-1 is not equal to 0, so we branch back to label. 1 is in X. We read $4016. We subtract 1 from X. 1-1 is equal to 0. We continue to where the clc is.

From the explanation earlier, you may realize we actually read two button states just now. But we didn't store them anyplace we could use them later.

Code: Select all

LatchController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons 

sta buttons;A variable in RAM. All buttons are now unpressed.

ldx #$08;Why not read 8 button states?
buttonreadloop:
asl buttons;We can ASL bytes other than A. It works the same just using RAM instead of A.
lda $4016
and #%00000001;We only care about this one bit.
ora buttons
sta buttons
dex
bne buttonreadloop
So the first run through the loop reads $4016. It gets the bit that contains the button state. We isolate that bit with the AND. We ora it with #$00. (Since we set buttons to that before the loop started.) We store it in buttons. buttons now contains the state of A in its rightmost bit.

We loop again. We shift buttons so that the state of the A button is now in the part of the bit marked A. #%000000A0. It puts a 0 in place for the next button to set if pressed because of how asl works. We load the state of the button through $4016. We isolate the bit. We ora, so now A contains the state of both buttons. Then we store A in buttons so the actual buttons variable contains the state of both buttons.

We keep looping until we have all eight buttons.

If that was hyper confusing, give it some time to digest. If you need an alternate explanation on an instruction (ROR, ASL, ETC), let me know. Once you get those, the controller code should "click" after a little thought, but if not ask away! As always, sorry for writing so much.

Edit: To use the buttons variable: Load it, AND out the bits you don't want to check. and #%1000000 would give you the A button, because all the others will end up zero.

Then you can use BEQ to branch passed the code that requires A, because if A was pressed the result of the beq would not be zero.
Dr. Floppy
Posts: 47
Joined: Mon May 09, 2011 7:02 pm

Post by Dr. Floppy »

Kasumi wrote:

Code: Select all

LatchController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons 

sta buttons;A variable in RAM. All buttons are now unpressed.

ldx #$08;Why not read 8 button states?
buttonreadloop:
asl buttons;We can ASL bytes other than A. It works the same just using RAM instead of A.
lda $4016
and #%00000001;We only care about this one bit.
ora buttons
sta buttons
dex
bne buttonreadloop
This is awesome!

Is there an expanded version of this to control for redundant reads over several frames (as Tokumaru mentioned)? Or that infernal DPCM glitch that causes phony reads from the Right button?
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Kasumi wrote:

Code: Select all

LatchController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons 

sta buttons;A variable in RAM. All buttons are now unpressed.

ldx #$08;Why not read 8 button states?
buttonreadloop:
asl buttons;We can ASL bytes other than A. It works the same just using RAM instead of A.
lda $4016
and #%00000001;We only care about this one bit.
ora buttons
sta buttons
dex
bne buttonreadloop
First I'd like to point out that this routine is pretty inefficient... the following one is shorter, faster, and even reads Famicom controllers that go on the expansion port, which the code above doesn't:

Code: Select all

	ldx #$09 ;strobe the controllers and initialize the counter
	stx $4016 
	dex
	stx $4016

buttonreadloop:
	lda $4016 ;get the state of the button
	and #%00000011 ;keep only the 2 lowest bits
	cmp #$01 ;if any of those 2 bits are set, the carry gets set
	rol buttons ;put the state of the button with the others
	dex ;move on to the next button
	bne buttonreadloop
You can strobe the controllers and initialize the counter in one go because 9 in binary is %00001001, and since the lower bit is 1 writing this to $4016 is the same as writing $01, because the upper bits are ignored by the register. After the DEX, 9 becomes 8, which in binary is %00001000, and since the lower bit is 0, so it's like $00 is written to $4016. This is the kind of trick that beginners shouldn't worry about, but I decided to show it anyway so that you know what kind of things are possible.

Next, there's no need to clear the button states, because we will insert 8 new bits into it, effectively kicking all the old bits out.

Then we proceed to the loop. There we read the state of a button, and since there are Famicom controllers that return the button states in the second bit of $4016, you want to keep the 2 lowest bits, not only the first. Then we use another "trick": by comparing the value to 1, we can make the carry flag reflect the state of the button no matter if it came in bit 0 (regular controller) or bit 1 (expansion port Famicom controller). Think about it: if a a button is pressed and a regular controller is used, our value will be 1 (%00000001), if an expansion port controller is used, the value will be 2 (%00000010). Both values are larger than 0, so when we compare it to 1 we are making sure that it's at least equal to or larger than 1, in which case the carry gets set. If the value was smaller than 1 (which would happen only if no button was pressed), the carry would be clear.

With the state of the button in the carry flag, we are ready to put it in a variable, along with the other button states. There's no need to load it from memory or anything, you can directly put the new bit in the variable with a rotate instruction.
Dr. Floppy wrote:Is there an expanded version of this to control for redundant reads over several frames (as Tokumaru mentioned)?
You can handle that by saving the previous state of the controller and doing some bit operations between it and the new state. Here's an example:

Code: Select all

	lda buttons ;make a copy of the old state
	sta oldbuttons

	jsr readcontroller ;read the new state

	lda oldbuttons ;get the old state
	eor #$ff ;turn buttons that were down into 0 and the others into 1
	and buttons ;erase from the current state the buttons that were down last time
	sta newbuttons ;save the result in another variable
Now you can use buttons when you need to know the current state of the buttons (such as when moving your game character sideways) or newbuttons when you need to know when a button wasn't down before but is now (useful when shooting and navigating menus, for example).
Or that infernal DPCM glitch that causes phony reads from the Right button?
We usually handle this by reading the controller multiple times, until 2 consecutive reads return the same result. Something like this:

Code: Select all

	jsr readcontroller ;read the controller
validatebuttons:
	ldy buttons ;make a copy of what was read (use Y because A and X are used in the subroutine)
	jsr readcontroller ;read again
	cpy buttons ;compare the previous read with the new one
	bne validatebuttons ;if they differ, go back and try again
I'll let you put all of the above solutions together as an exercise! =)
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

Ah, neat! I've actually never read anything about famicom, so that's all news to me. I also wasn't aware about only the one bit mattering for that register, so the loading of 9 first is also a clever trick.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Another clever trick is the use of a ring counter: preload the buttons variable with 1, and once you've ROL'd it eight times, the 1 has moved to the carry flag and you can exit the loop without even touching X.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

That's a very good idea tepples... It's faster (we get rid of the DEX inside the loop) and frees one register. With this, the subroutine could look like this:

Code: Select all

readcontroller:
	lda #$01
	sta buttons ;initialize the buffer with a flag
	sta $4016
	lda #$00
	sta $4016
buttonreadloop:
	lda $4016
	and #%00000011
	cmp #$01
	rol buttons
	bcc buttonreadloop ;loop if the flag wasn't shifted out yet
	rts
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

Heh. But the code becomes a byte smaller if you DO use X, but only for the setup.

Code: Select all

readcontroller:
   ldx #$01
   stx buttons ;initialize the buffer with a flag
   stx $4016
   dex
   stx $4016
buttonreadloop: 
dex vs. lda #$00. You all know you need that byte! :lol:

And considering this probably wouldn't run when something else uses X, you might as well.

Optimization is fun. :D It's also amazing how much I still have to learn, because Tepples' trick would not have come to me. I would have had to read specifically about it.

Maybe we need a topic like this thread around here.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Or LDA #$00 could become LSR A. This gives you the byte back.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

And keeps X free. Everyone wins!
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Kasumi wrote:Tepples' trick would not have come to me.
I have used it before, and also the opposite trick, which you use to detect when a byte is empty, rather than full: when you shift the first bit out, insert a one at the other end. That bit will prevent the number from becoming 0 before all the bits have been shifted out. When the bit buffer finally becomes 0 you'll know that the flag has been shifted out, and all meaningful bits have been used up.

I never thought about using the trick for reading the controller though, and that's the perfect kind of situation for it.
Maybe we need a topic like this thread around here.
I love that topic. A lot of the tricks shown there are useful on the NES too.
User avatar
bigjt_2
Posts: 82
Joined: Wed Feb 10, 2010 4:00 pm
Location: Indianapolis, IN

Post by bigjt_2 »

Camronas wrote:My god this is confusing lol as I'm sure it was for everyone else when they started :P
YEP!

Also Camronas, here's some assembly tuts that I found very useful when I started learning. Just sent these to some friends the other night who are starting to learn.

http://bbitmaster.com/neshackingtutorial1.txt
http://bbitmaster.com/neshackingtutorial2.txt

These do a good job of teaching the bare, bare basics of 6502 and gearing it toward the NES in general. Also, have yo downloaded the 6502 simulator yet?

http://exifpro.com/utils.html

This is an awesome little program and I encourage you to learn how to use it to write little test programs, also how to debug those programs and see what's happening in memory as your program counter rolls through your opcodes. I still find it useful now to write little tests for pseudocode that I'm pondering. It gives me a chance to test it without plugging it strait into my large program and potentially doink something up.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

I guess I should post this here rather than create a new topic. The input code from the back and forth above doesn't seem to like the famicom 4 player adapter in FCEUX. When it's set as attached in the drop down, up on player 2's controller also triggers right. Is there some reason for this I'm not getting?

Edit: It seems it was using my player 4 keybinding which happened to be on the same key. :oops: Either way as someone who would like to support both the 4 score and the famicom 4 player adapter without button interference from multiple players at once is there some other way to do this since my old input code apparently wouldn't have liked the famicom either?
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Complicating factors:
  • When one or two players are playing, Famicom controllers 3 and 4 should control players 1 and 2. But when more than two players are playing, Famicom controllers 3 and 4 should control players 3 and 4.
  • The Four Score can be switched on or off while the power is on, so detection in the init code might not be enough.
  • The Four Score is slower to read, but it provides fairly reliable detection of DMC DMA bit deletions.
For the sake of the player, it's best to continuously poll for a Four Score on the title screen. If the NES Four Score is present, display a message "FOUR SCORE CONNECTED"; otherwise, erase it. Write in the manual that on an NES, players shouldn't start a 4-player game until the message appears.

I recommend two different reading loops, one for ordinary operation:
  1. Toggle strobe to 1 then 0.
  2. Read D0 and D1 from $4016 and $4017 eight times.
  3. Repeat steps 1 and 2, compare, and use the previous frame's keypresses if they differ.
  4. If a 3- or 4-player game has not been started, OR player 3 on top of player 1 and player 4 on top of player 2.
And one for NES Four Score:
  1. Toggle strobe to 1 then 0.
  2. Read D0 from $4016 and $4017 24 times.
  3. Check the signature for each of the two ports; if it differs, use the previous frame's keypresses.
Selecting which loop to use when depends on whether and how much you plan to use the DMC in your game.
Post Reply