This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

DMC-fortified controller read routine

DMC-fortified controller read routine
by on (#33577)
Here's a controller read routine that is reliable even when the DMC is running. There is a safe version that takes 457 clocks, and a faster unsafe one that takes 137 clocks. Test ROMs + ca65 source:

read_joy.zip

The safe version reads three times in a row using the fast version, then compares the last two. If equal, that result is returned, otherwise the first read is returned. It doesn't need to check it in that case because the DMC can't clash with more than one of the reads, due to them being so closely spaced. The routine is timed so that the same number of clocks is used in each case.

The tests run the DMC at maximum rate and repeatedly read from the controller, printing an X when the DMC clashed with that read. A clash causes the fast version to give an erroneous result (left screenshot), but doesn't affect the safe version (right screenshot). Since the safe version compares two of the three reads it does, as expected it has twice the number of clashes (3.0% versus 1.3% for fast).

Image Image

by on (#33582)
Interesting. Can this be adapted to read both controllers, or would that take so long as to make a glitch that hits two reads?

by on (#33585)
Just make a second version that reads the other controller. Do games which use both controllers use some kind of optimized read-both-at-once routine or something? I imagine such a thing would suffer from the possibility of two corrupt reads, one during the first, and the other during the third. Even if this would work, it seems it'd only save around 120 clocks total.

by on (#33604)
The games I looked at read each controller separately. The controller is read multiple times until two identical results are returned for that controller.

I might point out, though it is not related to this discussion, that it is "proper" to read both bit 0 and bit 1 from each $4016/4017 access, combining the two to produce the final controller data. This is for the benefit of Famicom users who are using controllers that plug in to the expansion port instead of the normal ports. Every commercial game I've looked at acknowledges inputs on both bit 0 and bit 1 of $4016/4017.

by on (#33607)
dvdmth wrote:
Every commercial game I've looked at acknowledges inputs on both bit 0 and bit 1 of $4016/4017.

Even PAL games that were never released in NTSC-land?

by on (#33617)
No, basically all JP-made games, and perhaps some (but not many) US/UK-developed games acknowledge the extra FC controller. This has made PowerPak playing on the Famicom a little bit annoying with a rapid-fire controller.

by on (#33620)
Remember kids, Pal =/= japanese. They use ntsc also known as crappyness or never the same color.

by on (#33622)
How can both bits be efficiently merged without taking more than 432 clocks to read the controllers three times? Or perhaps it can go over and avoid conflicts on more than one read by having the $4016 reads at just the right timing? That'd be more tricky to code and test thoroughly.

EDIT: Figured out how to OR both bits and only add 2 clocks per iteration! Just change the LSR A to AND #$03, CMP #$01. This general technique can OR any number of bits from A into carry. Just mask off the bits, then CMP #1 (to have the opposite carry - set if any of the masked bits are zero - CMP #mask instead).
Code:
loop:
lda $4016   ; 4 bits 0 and 1 contain relevant data
and #$03    ; 2
cmp #$01    ; 2 carry = bit 0 OR bit 1
ror <temp   ; 5
bcc loop    ; 3

by on (#33623)
I've seen different methods for combining bits 0 and 1, the best of which is the AND #3 : CMP #1 method already posted. I've also seen a pair of LSR instructions with a BCS in between (to skip the second LSR if bit 0 was set). Nintendo's games use a much less efficient way, something like:
Code:
lda $4016
lsr
rol <$00
lsr
rol <$01

Then they OR bytes $00 and $01 after the read is complete. Interestingly, when they do their DPCM interference check, they only look at $00, not $01, so if nothing is plugged in the normal Famicom port (causing nothing but zeroes to be sent to $4016 bit 0), the interference check will never catch any errors.

by on (#33625)
In blargg's algorithm, what happens if the player presses or releases a button sometime between read 1 and read 3?

by on (#33628)
If the player pressed a key between pass 2 and 3 it would register as not pressed.

But really, this is probably going to be run every frame and a 1/60th of a second delay for input to register isn't going to be noticable.

by on (#33631)
So what if there were a DPCM fetch in read 1 and a press before read 3? (I'm trying to determine why Nintendo didn't just use this algorithm.)

by on (#33637)
Yeah, that is an issue. If all three read attempts give different results, there's no way to tell with certainty which was caused by the player pressing/releasing a button and which was caused by DMA interference, so you don't know which of the three to use. Probability of this happening is extremely rare, though.

by on (#33639)
If A != B and B != C and A != C, just use the previous frame's presses. Would that work?

by on (#33640)
Here's an updated version that should work correctly for Famicom external controllers. I thoroughly tested it at maximum DMC rate and it passes. The three reads now take longer than 432 clocks, but the reads are spaced so that the DMC cannot corrupt more than one. Inserting an 8+16*n (n >= 0) clock delay after the first read causes it to fail, confirming that they are spaced properly. I also added a controller button test.

read_joy2.zip

I also finally got around to doing the analysis of the case where the DMC corrupts the first read, and the controller input changes during the the second two reads. In this case, the corrupted first read will be returned, rather than one of the two correct reads from the controller.

There are 8 opportunities for DMC corruption of the first read, and the window for the controller change during the second two is 162 clocks. That means that the DMC corruption at maximum rate has an 8 in 29780 = 1 in ~3722 chance in a given frame, and the controller change a 162 in 29780 = 1 in ~184 chance in a given frame.

Assuming the controller were changing at a random time each frame, and the DMC were running, that makes the chance of both occurring in a given frame 1 in ~684848. So there would be an average of one of these errors every 3.2 hours at this worst-case setup. If the controller input were changing on average 10 times per frame, that would put one error every 19 hours.

I guess I'll try writing and analyzing a version that reads until two consecutive reads give the same value. The main problem is that this takes a varying amount of time, and would hang if someone fed the controller a very rapid turbo signal.

by on (#33643)
blargg wrote:
I guess I'll try writing and analyzing a version that reads until two consecutive reads give the same value. The main problem is that this takes a varying amount of time, and would hang if someone fed the controller a very rapid turbo signal.

As far as I can tell, the turbo controllers that carry the Official Nintendo Seal use a timer that turns on and off at up to 30 Hz or so, not a flip-flop that inverts after every strobe. At least my SN ProPad's box said it used a timer, and the NES Advantage uses adjustable timers. Has anyone reverse-engineered other licensed turbo controllers such as the NES Max?

by on (#33648)
I wrote a version that keeps reading until it gets two in a row that match. I worked out the worst-case to be 5 reads, which can occur in the following two cases:

1: Correct
Controller changes here
2: Correct
3: Corrupted by DMC DMA
4: Correct
5: Correct

1: Correct
2: Corrupted by DMC DMA
3: Correct
Controller changes here
4: Correct
5: Correct

Due to the timing of the reads, DMC DMA cannot corrupt more than one read in 5 consecutive reads, even though 5 reads takes longer than 432 clocks. So the only thing that would hang this algorithm is a controller which kept changing more than once every 800 clocks or so.

I guess I could make this multi-read algorithm always read 5 times, then analyze the results, so as to reduce the timing variation.

tepples wrote:
As far as I can tell, the turbo controllers that carry the Official Nintendo Seal use a timer that turns on and off at up to 30 Hz or so, not a flip-flop that inverts after every strobe.

The NES Max also used an oscillator for turbo. I doubt any controllers clock turbo from strobe.

by on (#33651)
OK, probably the final version of the algorithm. This reads four times, then returns two matching readings, taking right around 660 clocks. DMC DMA can only corrupt one, and assuming only one controller change at most between the first and last read, at least two readings will always match. This one uses three zero-page temporary bytes temp, temp2, temp3.

read_joy3.zip

by on (#33652)
Has anyone thought of building a list of games potentially affected by the bug?

by on (#36661)
Running count_errors.nes from the ReadJoy3 package on my PAL NES outputs me Conflicts : 0/1000 on all tries.

Should we conclude that the PAL NES is exempt from this DMC bug ?

by on (#36677)
I'm pretty sure the test program is tuned for NTSC DMC rates. I'll have to write a PAL version sometime soon.

EDIT: nevermind, that test doesn't do any synchronization. That suggests it doesn't occur on the PAL NES, though a thorough test would be good.

by on (#61283)
The file is gone, anyone care to rehost it?

I also read the companion thread, and realized that pocketnes has a buggy joystick reader (does not require strobe to be stopped before reading bits).

by on (#61316)
Dwedit wrote:
The file is gone, anyone care to rehost it?

I also read the companion thread, and realized that pocketnes has a buggy joystick reader (does not require strobe to be stopped before reading bits).

Attachment:
read_joy3.zip [26.95 KiB]
Downloaded 197 times

by on (#91061)
Anyone got a backup of these? The links appear down (at least for me)

edit: i'm retarded and didnt see the page 2 link >_> sorry for the necrobump

edit2: that being said...any chance of a asm6 version?

by on (#91070)
Jeroen wrote:
Anyone got a backup of these? The links appear down (at least for me)

edit: i'm retarded and didnt see the page 2 link >_> sorry for the necrobump

edit2: that being said...any chance of a asm6 version?


here

edit: i'm retarded and didn't read your second sentence but decided to submit my post anyway since my answer to your question is valid.

by on (#91071)
Awesome. Also I don't see why that would make YOU retarded....the thread's not magicly gonna be unnecrobumped. (And some extra mirrors are never gonna hurt)

by on (#91072)
Jeroen wrote:
Awesome. Also I don't see why that would make YOU retarded....the thread's not magicly gonna be unnecrobumped. (And some extra mirrors are never gonna hurt)


It's funny. Every time I see a thread like this pop back up my first reaction is "blargg's baaaaaaaack?!!".

by on (#91079)
Just for double checking the things: if no buttons are pressed, it should give a PASS; otherwise, ERROR. Is this ok?

by on (#91080)
Yeah, assuming it's a official controller as the 9th read would be a high.