reading controller status when strobe bit is set

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
reading controller status when strobe bit is set
by on (#14004)
If you read the NES controller ($4016/$4017) when the strobe bit is set ($4016=1), will it always return the status of the 1st button in queue, or will it cause conflict and return open-bus (lsb 0 for $4016, 1 for $4017)?

by on (#14008)
What's the strobe bit? When you strobe, that's completed long before the next op.

by on (#14011)
Quote:
What's the strobe bit?
lsb of data written to $4016: http://nesdevwiki.ath.cx/index.php/Standard_Controller

To read joypad button status, you'd normally write 1, then 0 to $4016. What I want to know is what happens when you read it after the first write.

by on (#14013)
I remember hearing about this a while back -- from what I recall, when the strobe bit is set, reading $4016/7 will return in real-time status of the first button (A button).

I vaguely remember blargg or someone explaining the details on this some time ago.

by on (#14015)
Disch recalls correctly. When strobe bit is left at 1, reading the joypad status returns the current A button status at the time of the read, not the A button status when you set strobe.

by on (#14019)
Thanks.

*edit* something even more obscure: what happens if you read button status after turning the NES on, or after reset, without having strobed?

by on (#14026)
o.O news to me.

Ok, so when $4016.d0 is set, the read position is reset to 0 (status of A), and all reads from $4016 at this point return the status of A, at the time of reading $4016, as if having the strobe bit set means the NES is constantly polling the joypad in realtime, and acts basically like a real-time state buffer? So eg multiple reads can return different values at this point?

But then, when you write a 1 to $4016.d0, it then stops polling the joypad any more. So now you read $4016 and it returns the status of each button in sequential order, at the time that you write to $4016, not when you read from it? And it's been verified that the NES controller doesn't just keep polling anyway, eg by doing something like holding down B, reading A, releasing B, waiting 5 seconds, and then reading B and seeing if it is set? So that eg the strobe bit only affects the read position?

So you could theoretically just set the strobe bit to 1 and use the A button as a real-time status button without ever writing to $4016 again? Not that that's useful in any way, but I do want to emulate the behavior anyway. An excellent find, no doubt!

by on (#14029)
$4016 bit 0 output is connected to the load pin of the 4021 8-bit shift register. When load is 1, the data output always reflects the current status of data 0 (the A button). When load transitions from 1 to 0, the shift register latches the data at each input and the output reflects bit 0 of the shift register.

byuu wrote:
Ok, so when $4016.d0 is set, the read position is reset to 0 (status of A), and all reads from $4016 at this point return the status of A, at the time of reading $4016, as if having the strobe bit set means the NES is constantly polling the joypad in realtime, and acts basically like a real-time state buffer? So eg multiple reads can return different values at this point?


Yes. The joypad would be constantly feeding the current status of A to the data input on the joypad connector, and when you read the CPU just takes the current value.

Quote:
But then, when you write a 1 to $4016.d0, it then stops polling the joypad any more. So now you read $4016 and it returns the status of each button in sequential order, at the time that you write to $4016, not when you read from it?


Assuming you mean writing 0 to $4016, yes.

Quote:
And it's been verified that the NES controller doesn't just keep polling anyway, eg by doing something like holding down B, reading A, releasing B, waiting 5 seconds, and then reading B and seeing if it is set? So that eg the strobe bit only affects the read position?


It's all based on the behavior of the 4021 shift register. When strobe transitions from 1 to 0, it latches. When you clock it, the the register's contents shift right by one bit. Not much else to it. Here's some test code I just ran on my NES, with a NES and SNES controller (using a SNES-to-NES adaptor I made):

Code:
; I start holding B before running code
lda   #0          ; strobe = 0
sta   $4016
lda   #1          ; strobe = 1
sta   $4016
lda   #0          ; strobe = 0
sta   $4016
lda   $4016       ; read status of A
lda   #100        ; wait one second
jsr   delay_10msec
; I release B button during delay
lda   $4016       ; read status of B
and   #$01
jsr   print_a     ; prints 1


Quote:
So you could theoretically just set the strobe bit to 1 and use the A button as a real-time status button without ever writing to $4016 again?


Right. You could make a one-button game that never needed to toggle the strobe line. :)

There was another thread a while back where I posted some other explanation and test code too:

http://nesdev.com/bbs/viewtopi ... =4606#4606

hap wrote:
something even more obscure: what happens if you read button status after turning the NES on, or after reset, without having strobed?


Heh, I like the obscurity of this. I just tried a few times and at power-up and reset and it acts as if you had written 1 then 0 to $4016, i.e. it latches the current button state and is in read mode. Apparently it's the controller itself, because I get the same behavior when I plug it in then immediately try reading data. Same for NES and SNES joypads.

by on (#14030)
Excellent, thanks for the info :)
This behavior shall now be emulated on the SNES as well as the NES.

Quote:
Assuming you mean writing 0 to $4016, yes.


Whoops, yes I do.

Quote:
When load transitions from 1 to 0, the shift register latches the data at each input and the output reflects bit 0 of the shift register.


And my understanding is that a one appears on the end of the shift register after each shift, correct? Eg :

SR = 01000001
read
SR = 10100000
read
SR = 11010000
etc.

Hmm, ok then. So the write to $4016 that forces 1->0 latches the current buttons into SR, 0->0 does absolutely nothing, 0->1 sets the controller into the special "always read A in real-time" mode, and 1->1 does nothing as well.

I'll have to do a good bit of reprogramming to support this, but I think it will clarify the interface a lot more. I was using a counter to move through the joypad polls, but I think I'll make a true 16-bit (12 buttons on SNES) shift register and drop the value into that for now. Eventually I can take that concept further and isolate controllers from the CPU inteface and treat them as actual input hardware and such. Fun stuff.

Quote:
Heh, I like the obscurity of this.


Do you now? Heheh. Ok, what if you already have a controller in port 1, and you are halfway through reading it, and then you plug a controller into port 2. Will the controller itself then mess with $4016.d0 in such a way as to "retrigger" the port 1 controller? My guess is no, because the controller is probably manually detecting being plugged in and setting up the joypad for the first time, ignoring $4016.d0.

Does each controller handle $4016.d0 separately, despite sharing the pin on the SNES? Since plugging in a new controller causes it to be in latched read mode (0), I wonder what happens if there's a controller in the other port and $4016.d0 = 1.

Or, really better: does setting $4016.d0 result in a constant pin being set that the controller can see indefinitely, or does it only last one write cycle where the controller "sees" it and acts accordingly? The latter makes more sense, due to the controllers starting in read mode (0). But the former is more likely because otherwise how would the controllers detect 0->1 and 1->0 writes, unless they are analyzing the CPU output / address pins to look for $4016 writes, and then manually store the last write inside the controller chip itself. Seems too complex in comparison to the former :/

This is useless so don't waste your time with it unless you're bored out of your mind. I was intentionally trying to be asinine :)

by on (#14031)
Quote:
And my understanding is that a one appears on the end of the shift register after each shift, correct?


Yeah, the top bit of the shift register is wired up to fill with 1, so if you keep reading bits you'll eventually get an infinite stream of 1s (I've verified this on NES and SNES joypads). For NES, you get the 8 data bits then 1s; for SNES, you get the 12 data bits, four 0 bits (due to it having two 8-bit shift registers), then 1s:

NES: dddddddd1.....
SNES: dddddddddddd00001.....

Quote:
Do you now? Heheh. Ok, what if you already have a controller in port 1, and you are halfway through reading it, and then you plug a controller into port 2. Will the controller itself then mess with $4016.d0 in such a way as to "retrigger" the port 1 controller? My guess is no, because the controller is probably manually detecting being plugged in and setting up the joypad for the first time, ignoring $4016.d0.


Heh, I had thought about clarifying this. I haven't tried, but it's most likely that the controllers are independent as you say, since it's the shift register chip in the controller that's the source of the behavior, not the console. The only way one controller could affect the other is if it's somehow overpowering the strobe output from the console momentarily, but that would be highly unusual for an input line (even in the pathological case where the strobe line were connecting before the +5V and the CMOS chip was getting powered by the input protection diodes).

Quote:
Does each controller handle $4016.d0 separately, despite sharing the pin on the SNES?


Yeah. The console just has a single latched output line set by the last write to $4016 bit 0. The controller decides how to interpret it (my serial interface interprets it as the value to drive the RS-232 line at, for example). Find a schematic online and it should be clearer. I tried to find some simple datasheets for the 4021 earlier but failed.

By the way, a moderator should move this thread into the NES development section, since it's not specific to emulators.