Micro Machines glitches

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Micro Machines glitches
by on (#141961)
Shaky horizontal lines appear on the right-side of some of the screens of Micro Machines in my emulator. The game itself seems to play okay though.

Image

Image

The Tricky-to-emulate games lists mentions, "Micro Machines requires correct values when reading PPU $2004 (OAMDATA) during rendering." What does this refer to exactly?
Re: Micro Machines glitches
by on (#141964)
It's referring to the behavior of $2004 reads during visible scanlines described here: http://wiki.nesdev.com/w/index.php/PPU_ ... evaluation
Re: Micro Machines glitches
by on (#141971)
James wrote:
It's referring to the behavior of $2004 reads during visible scanlines described here: http://wiki.nesdev.com/w/index.php/PPU_ ... evaluation


I carefully implemented that logic (see here), but I'm still getting the Micro Machines glitches. Did I miss something on that page?
Re: Micro Machines glitches
by on (#141973)
Wait a second. I downloaded a different ROM and this one wants to use mapper 71 instead of mapper 2. WTF?

I'll implement that mapper and see what happens...

Edit: Unfortunately, implementing mapper 71 did not help. The graphical glitches still look the same.
Re: Micro Machines glitches
by on (#141975)
re: mapper 2 vs. 71: http://wiki.nesdev.com/w/index.php/INES_Mapper_071

As far as $2004 reads go, this (hack) should be enough to make Micro Machines work:

Code:
if (rendering)
{
   if (current_cycle < 64)
      return_value = 0xFF;
   else if (current_cycle < 256)
      return_value = 0x00;
   else if (current_cycle < 320)
      return_value = 0xFF;
   else
      return_value = sprite_buffer[0];
}


I don't completely understand what the game is doing here, but it seems that the whole purpose of these reads is to adjust timing:

Code:
...
FD6E:   BIT $2004
FD71:   BMI $FD73
FD73:   PHA
...


The only impact that BMI can have is on the number of clock cycles consumed, so make sure that you're accounting for the extra clock cycle on a taken branch.
Re: Micro Machines glitches
by on (#141979)
Thanks. That did not solve the problem, but it did have a noticeable effect. It reduced the amount of glitching.

What's the concept behind the hack? Is there an accurate way to do this?
Re: Micro Machines glitches
by on (#141988)
zeroone wrote:
What's the concept behind the hack?

Micro Machines reads $2004 around dot 320. If the read occurs before dot 320, it needs to see a value with the high bit set (implemented properly, the read would return $FF because it would take place while the PPU is fetching from an empty sprite slot). If the read occurs >= dot 320, it needs to see a value without the high bit set (which it does, because the first byte in secondary OAM is < 0x80).

Quote:
Is there an accurate way to do this?

Yep. Implement it as described on the wiki.
Re: Micro Machines glitches
by on (#141997)
James, thanks for the reply.

James wrote:
Yep. Implement it as described on the wiki.


I implemented as close to the wiki description as I could, but I must be misinterpreting something.

James wrote:
implemented properly, the read would return $FF because it would take place while the PPU is fetching from an empty sprite slot


I see the wiki says:

Quote:
Cycles 1-64: Secondary OAM (32-byte buffer for current sprites on scanline) is initialized to $FF - attempting to read $2004 will return $FF.


Does that mean that $2004 returns the contents of secondary OAM?

James wrote:
If the read occurs >= dot 320, it needs to see a value without the high bit set (which it does, because the first byte in secondary OAM is < 0x80).


You also seem to be implying that $2004 returns the contents of secondary OAM. What address/offset into secondary OAM is accessed (if it is accessed)?

Why will $2004 return 0 between PPU cycles 64 and 256?
Re: Micro Machines glitches
by on (#142001)
zeroone wrote:
Does that mean that $2004 returns the contents of secondary OAM?...You also seem to be implying that $2004 returns the contents of secondary OAM. What address/offset into secondary OAM is accessed (if it is accessed)?

$2004 reads return the contents of secondary OAM during these phases:
wiki wrote:
Cycles 257-320: Sprite fetches (8 sprites total, 8 cycles per sprite)
1-4: Read the Y-coordinate, tile number, attributes, and X-coordinate of the selected sprite from secondary OAM
5-8: Read the X-coordinate of the selected sprite from secondary OAM 4 times (while the PPU fetches the sprite tile data)
For the first empty sprite slot, this will consist of sprite #63's Y-coordinate followed by 3 $FF bytes; for subsequent empty sprite slots, this will be four $FF bytes

Cycles 321-340+0: Background render pipeline initialization
Read the first byte in secondary OAM (while the PPU fetches the first two background tiles for the next scanline)

zeroone wrote:
Why will $2004 return 0 between PPU cycles 64 and 256?

$2004 reads at that time will expose what's happening during sprite eval. If you're referring to my code, it's not accurate behavior.

Somebody who has implemented proper sprite eval logic can probably explain it better.

Oh, one more thing: I'm using the "old" PPU cycle terminology: e.g., idle cycle = 340, not 0, so adjust accordingly if you're not.
Re: Micro Machines glitches
by on (#142004)
James wrote:
$2004 reads return the contents of secondary OAM during these phases


What address of secondary OAM does it return?

James wrote:
$2004 reads at that time will expose what's happening during sprite eval.


By returning what exactly?
Re: Micro Machines glitches
by on (#142051)
Here is the $2004 read according to Nintendulator:

Code:
int   __fastcall   Read4 (void)
{
   if (IsRendering)
      readLatch = Sprite[SpritePtr];
   else   readLatch = Sprite[SprAddr];
   return readLatch;
}


The Sprite array appears to represent both primary and secondary OAM.

According to the wiki:

Quote:
Reading OAMDATA while the PPU is rendering will expose internal OAM accesses during sprite evaluation and loading; Micro Machines does this. It used to be thought that reading from this register wasn't reliable, however more recent evidence seems to suggest that this is solely due to corruption by OAMADDR writes. In the oldest instantiations of the PPU, as found on earlier Famicoms, this register is not readable. It's not known exactly which revision of the 2C02 added the readability—it is certainly absent in the RP2C02C, and present by the RP2C02G.


Can someone please provide more information? The wiki seems incomplete.

Edit: On a side note, FCEUX 2.2.1 looks more screwed up than my emulator:

Image

Image
Re: Micro Machines glitches
by on (#142062)
FCEUX is pretty bad at emulating these kinds of obscure quirks.
Re: Micro Machines glitches
by on (#142063)
If the rendering is disabled and the PPU address is between $3F00-$3F1F, that's the color it outputs.
Re: Micro Machines glitches
by on (#142067)
Quote:
If the rendering is disabled and the PPU address is between $3F00-$3F1F, that's the color it outputs.


Can you elaborate on this?
Re: Micro Machines glitches
by on (#142068)
  • If rendering is disabled, and the current VRAM address is $0000-$3EFF, the PPU will output the color at $3F00.
  • If rendering is disabled, and the current VRAM address is $3F00, the PPU will output the color at $3F00.
  • If rendering is disabled, and the current VRAM address is $3F01, the PPU will output the color at $3F01.
  • If rendering is disabled, and the current VRAM address is $3F02, the PPU will output the color at $3F02.
  • If rendering is disabled, and the current VRAM address is $3F03, the PPU will output the color at $3F03.

It stores a table of colors at $3F00-$3F1F and expects to be able to switch to the next color by reading one byte from or writing one byte to the VRAM data port, which advances the VRAM address by 1.
Re: Micro Machines glitches
by on (#142071)
tepples wrote:
  • If rendering is disabled, and the current VRAM address is $0000-$3EFF, the PPU will output the color at $3F00.
  • If rendering is disabled, and the current VRAM address is $3F00, the PPU will output the color at $3F00.
  • If rendering is disabled, and the current VRAM address is $3F01, the PPU will output the color at $3F01.
  • If rendering is disabled, and the current VRAM address is $3F02, the PPU will output the color at $3F02.
  • If rendering is disabled, and the current VRAM address is $3F03, the PPU will output the color at $3F03.

It stores a table of colors at $3F00-$3F1F and expects to be able to switch to the next color by reading one byte from or writing one byte to the VRAM data port, which advances the VRAM address by 1.


You are on to something here. I implemented that feature a while ago after reading about it in the wiki and after discovering some other games (or demos) depended on it.

As a test, I just modified the associated code to return white when that code executes:

Image

Image

So, those glitches are related. Maybe it is some sort of timing issue where the color is not updated at the right point.
Re: Micro Machines glitches
by on (#142072)
If you run it on a real machine, you can see a few more things about the palette state via the colours in the hblank/overscan at the sides of the image.

https://www.youtube.com/watch?v=BMpZznee74I
Re: Micro Machines glitches
by on (#142073)
rainwarrior wrote:
If you run it on a real machine, you can see a few more things about the palette state via the colours in the hblank/overscan at the sides of the image.

https://www.youtube.com/watch?v=BMpZznee74I


Woah!
Re: Micro Machines glitches
by on (#142077)
Interestingly, it positions sprite 0 toward the top-right of the screen and it relies on that hit to coordinate those lines in the middle of the screen. I wonder if it is doing very careful timing based off of that hit.
Re: Micro Machines glitches
by on (#142087)
tepples wrote:
If rendering is disabled, and the current VRAM address is $0000-$3EFF, the PPU will output the color at $3F00.


My bad regarding "PPU address" instead of "VRAM address". :oops: :oops: :oops:
Re: Micro Machines glitches
by on (#142088)
zeroone wrote:
Interestingly, it positions sprite 0 toward the top-right of the screen and it relies on that hit to coordinate those lines in the middle of the screen. I wonder if it is doing very careful timing based off of that hit.

The game spins waiting for that sprite 0 hit immediately before jumping to the $2004 read routine. There it loops 8 times, burning ~113 cycles/loop.
Re: Micro Machines glitches
by on (#142089)
Quote:
The game spins waiting for that sprite 0 hit immediately before jumping to the $2004 read routine. There it loops 8 times, burning ~113 cycles/loop.


What is it waiting for? The HBlank?

As for timing, my emulator renders the text boxes in Marble Madness correctly and Battletoads runs without a problem. I don't know if I can make it more accurate than that.
Re: Micro Machines glitches
by on (#142090)
Apparently it's snooping on OAM bus as a way to synchronize its raster effects to horizontal blanking.
Re: Micro Machines glitches
by on (#142092)
tepples wrote:
Apparently it's snooping on OAM bus as a way to synchronize its raster effects to horizontal blanking.


It's not gaining/losing that many cycles from the snoop. And, why would it have to anyway? It should always be the same number of cycles after the sprite 0 hit to the edge.
Re: Micro Machines glitches
by on (#142093)
Polling on $2002 reads induces up to a 6-cycle jitter. Eight scanlines of asking the PPU where it is and adding an extra cycle if necessary should just about be enough to get cycle-exact timing out. (Does it?)
Re: Micro Machines glitches
by on (#142094)
lidnariq wrote:
Polling on $2002 reads induces up to a 6-cycle jitter. Eight scanlines of asking the PPU where it is and adding an extra cycle if necessary should just about be enough to get cycle-exact timing out. (Does it?)


I think you mean $2004 and it seems to be doing all that polling on the same scanline (the sprite 0 hit scanline).
Re: Micro Machines glitches
by on (#142097)
It's so weird that this game goes through all this trouble to pull off some stupid raster effects in menu screens that could have easily used timed code all the way, with the beginning of the frame and/or a sprite 0 hit as a reference point. But no, they decided to use an obscure/undocumented aspect of the console, that's not even guaranteed to work on all revisions (didn't OAM read back fail on some Famicom models?).
Re: Micro Machines glitches
by on (#142098)
Sprite 0 hit gets into a ~20-pixel-wide ballpark. Polling on $2004 over the next few lines allows correcting from that ballpark to an exact position plus or minus one pixel. This sort of precise alignment to the horizontal blank becomes especially important for mid-frame palette changes.

And to Codemasters and Color Dreams, everything was undocumented.
Re: Micro Machines glitches
by on (#142103)
zeroone wrote:
I think you mean $2004 and it seems to be doing all that polling on the same scanline (the sprite 0 hit scanline).
I meant what I said, exactly as I said it.

It polls $2002, waiting for sprite 0 hit. That provides a 6-7 cycle/18-21 pixel jitter.

It then tests $2004 over eight successive scanlines, adjusting the PPU/CPU phase by one cycle (+1 pixel / -2 pixels) if necessary on each of those scanlines.
Re: Micro Machines glitches
by on (#142122)
tokumaru wrote:
But no, they decided to use an obscure/undocumented aspect of the console, that's not even guaranteed to work on all revisions (didn't OAM read back fail on some Famicom models?).

They probably didn't have any official documentation at all to work from. A lot of unlicensed NES work was done on the basis of reverse engineering. The game was also never released on Famicom, so even if the technique would have failed on that, it wasn't relevant.

If it works, it works. The fact that it's annoying to emulate is our problem, not theirs. The emulated-NES platform didn't even exist then, they had no reason to make any concessions for it (even if they could predict the future and know that this technique would be tricky for us).
Re: Micro Machines glitches
by on (#142125)
rainwarrior wrote:
They probably didn't have any official documentation at all to work from. A lot of unlicensed NES work was done on the basis of reverse engineering.

Sure, and as far as I know, a big part of the reverse engineering process involved analyzing disassembles of official games. Surely they have analyzed every register on their own, but when you don't have any official documentation to guide you, the official software should provide you with guidelines for development. Just because the hardware you happen to be testing on behaves a certain way, you can't expect that behavior to be consistent across every revision (now or in the future) unless the manufacturers say so. That's common sense.

Now, my point isn't necessarily that they shouldn't have used such advanced/obscure techniques, but that it was very risky to use them for something as silly as title/menu screens that don't even look particularly impressive. As far as I can see, all the glitches happen in blank areas, so instead of wasting 8 scanlines on an obscure technique to sync with the PPU, wouldn't it be simpler to change 2 colors every HBlank during those 8 scanlines? With 85 pixels of HBlank, a 21-pixel jitter wouldn't get in the way of 2 palette writes and 2 more to move the VRAM address away from the palette range before the next scanline starts. Besides palette changes, what other kinds of raster effects are they doing? Horizontal scroll changes? Those don't need perfect PPU synchronization either.

Quote:
The game was also never released on Famicom, so even if the technique would have failed on that, it wasn't relevant.

Unlicensed or not, I'm sure developers would want their software to have a high compatibility rate, even if the initial target market was limited.

Quote:
If it works, it works. The fact that it's annoying to emulate is our problem, not theirs.

This is not about emulation, but about using the hardware in a safe way so that random customers don't show up complaining that the game doesn't work properly for them. I don't know how much they tested this technique (and on how many different consoles) before considering it safe to use, but the fact that it was not used in any official release (not that they have looked at every officially released game, but still) should have raised some flags. They took a chance when they chose this approach (which wasn't even absolutely necessarily to begin with), and luckily for them no consoles had trouble with it. Things might have been differently if the game was released in Japan.
Re: Micro Machines glitches
by on (#142126)
tokumaru wrote:
It's so weird that this game goes through all this trouble to pull off some stupid raster effects in menu screens that could have easily used timed code all the way, with the beginning of the frame and/or a sprite 0 hit as a reference point. But no, they decided to use an obscure/undocumented aspect of the console, that's not even guaranteed to work on all revisions (didn't OAM read back fail on some Famicom models?).


From the look of things, I suspect that they hired a bunch of devs who used to work on Atari 2600 games.

lidnariq wrote:
zeroone wrote:
I think you mean $2004 and it seems to be doing all that polling on the same scanline (the sprite 0 hit scanline).
I meant what I said, exactly as I said it.

It polls $2002, waiting for sprite 0 hit. That provides a 6-7 cycle/18-21 pixel jitter.

It then tests $2004 over eight successive scanlines, adjusting the PPU/CPU phase by one cycle (+1 pixel / -2 pixels) if necessary on each of those scanlines.


Agreed.

But, the wiki does not provide enough information about $2004 reads for me to enhance my emulator. How anyone solved this for Nintendulator or Nestopia is a mystery.
Re: Micro Machines glitches
by on (#142128)
tokumaru wrote:
But no, they decided to use an obscure/undocumented aspect of the console, that's not even guaranteed to work on all revisions (didn't OAM read back fail on some Famicom models?).

It would not be the only time they do stuff like this, literally every single one of their Master System games rely on the revised VDP in order to have a higher resolution, despite the fact there isn't any single official game using it. I guess that their reverse engineering consisted on disassembling a couple of games to get a vague outline then poking stuff to figure out the rest.
Re: Micro Machines glitches
by on (#142131)
Not surprisingly, Bee 52 is also quite glitchy on my emulator.
Re: Micro Machines glitches
by on (#142132)
Dug something up from an older thread:

Quote:
In the title screen it reads $2004 8 times on scanlines 16..23 around scanline clock ~310:

FD6E BIT $2004
FD71 BMI $FD73
FD73 ...

So it adds an extra cycle whenever the value returned by $2004 has the top bit set. It's exploiting the fact that at certain times $2004 read always returns $FF and at certain times proper values from OAM, depending on which PPU cycle the read falls on. That way they get a more precise CPU-PPU sync.


Which PPU cycles will $2004 return $FF and which ones will it return OAM values? Of those that return OAM values, which OAM values are returned?
Re: Micro Machines glitches
by on (#142135)
Did you find this page on the wiki? http://wiki.nesdev.com/w/index.php/PPU_ ... evaluation

It really should contain enough information for what you are doing.
Re: Micro Machines glitches
by on (#142136)
lidnariq wrote:
Did you find this page on the wiki? http://wiki.nesdev.com/w/index.php/PPU_ ... evaluation

It really should contain enough information for what you are doing.


I implemented that logic as best as I could interpret the page. Does $2004 effectively return whatever was last read from either primary or secondary OAM? For instance, at the bottom:

Quote:
Read the first byte in secondary OAM (while the PPU fetches the first two background tiles for the next scanline)


Does that mean that $2004 returns the first byte in secondary OAM?
Re: Micro Machines glitches
by on (#142137)
tokumaru wrote:
Sure, and as far as I know, a big part of the reverse engineering process involved analyzing disassembles of official games. Surely they have analyzed every register on their own, but when you don't have any official documentation to guide you, the official software should provide you with guidelines for development. Just because the hardware you happen to be testing on behaves a certain way, you can't expect that behavior to be consistent across every revision (now or in the future) unless the manufacturers say so. That's common sense.

Without any official documentation, there's no reason at all to assume the behaviour isn't consistent. That's not common sense at all. Why would you assume that something that works now wouldn't later? You can only find this by being told by the designers, or testing the revisions in question, and obviously you can't test future revisions.

You act like disassembling a game isn't a monumental effort of its own. It's not like they could just set a breakpoint in an emulator and try out 100 games in an afternoon like we can today. Think of how much time (i.e. think of how much it cost) to disassemble a game for them. Every one they disassembled was burning time out of their development budget.

Imagine they know this technique works from their testing. A programmer shows it to their lead. The lead says "no don't do that. we haven't seen that in a game before. Go disassemble games until you find one that does this and then you will have my permission." If a lead used this strategy, the game would never be finished.

tokumaru wrote:
Unlicensed or not, I'm sure developers would want their software to have a high compatibility rate, even if the initial target market was limited.

You can't target an unknown platform. The only way to know what is out-of-design and what's not is to have the designer tell you. If you're not licensed, you're not given that information.

There's really no reason to assume any particular technique is taboo with the information they had.

If your goal is really to make the "most compatible" software possible, skipping the licensing process and relying entirely on reverse engineering is probably the last thing you should do. If you've accepted that they're willing to go rogue to cut costs, it's obvious that they're willing to take a risks like that to get their software out at lower cost to them.

Quote:
This is not about emulation, but about using the hardware in a safe way so that random customers don't show up complaining that the game doesn't work properly for them. I don't know how much they tested this technique (and on how many different consoles) before considering it safe to use, but the fact that it was not used in any official release (not that they have looked at every officially released game, but still) should have raised some flags. They took a chance when they chose this approach (which wasn't even absolutely necessarily to begin with), and luckily for them no consoles had trouble with it. Things might have been differently if the game was released in Japan.

I just tested it on my Famicom, and it is indeed slightly glitchy looking. There is a little bit of white marks on the right hand side of the screen, and a bit of 1-line jitter up and down on the character select screen. It doesn't make the game unplayable even slightly. It doesn't make any of the names or graphics unreadable. It doesn't put garbage faces on the characters. It's just a couple of white marks and a little bit of jitter. This is not to mention that the actual gameplay takes place entirely on screens without this technique, so playing the game is entirely unaffected.

As far as bugs go, this is pretty acceptable. It's not GOOD to have minor graphical glitches on the screen, but this is such a mild bug they might not even consider it worth fixing in a revision if they sold a lot of units and had an opportunity to get a new run of PRG ROMs made. How many well selling NES games had far worse problems than this? Lots of well selling games today even have bugs worse than this that the company doesn't want to pay to patch.

This is all moot, though, if they had released it on Famicom, they would have tested that platform and fixed the problem (or possibly decide it didn't need fixing). It seems really strange to me to complain about a bug that wasn't even present on any platform the release was for (i.e. not actually a bug).
Re: Micro Machines glitches
by on (#142139)
rainwarrior wrote:
Without any official documentation, there's no reason at all to assume the behaviour isn't consistent.

I agree with this regarding many other behaviors, but this one in particular is just too damn weird to be considered stable... If reverse engineering takes time, do you think they studied in depth what the values being read back from $2004 even meant? Do you think they got to figure out the sprite evaluation logic based on those reads? At first glance, a register that returns quickly changing values when you read it in a loop looks like the last think you'll want to trust.

Quote:
That's not common sense at all. Why would you assume that something that works now wouldn't later?

Because it's weird even when it's working. The values change faster than the CPU is able to keep up with and you have to use all sorts of trickery to do something useful with that register. Doesn't sound like something that was meant to be used like that. Again, not that I'm against being creative with the resources available, but I wonder if they were careful enough while going this route, considering that there were safer alternatives.

Quote:
You act like disassembling a game isn't a monumental effort of its own.

And figuring out the details of the rendering logic inside the PPU by fiddling with its registers isn't?

Quote:
There's really no reason to assume any particular technique is taboo with the information they had.

It's a register returning rapidly changing values that the CPU can hardly keep up with unless the OAM is organized in specific ways. It's weird.

Quote:
I just tested it on my Famicom, and it is indeed slightly glitchy looking. There is a little bit of white marks on the right hand side of the screen, and a bit of 1-line jitter up and down on the character select screen. It doesn't make the game unplayable even slightly.

Certainly not, but the reason they used the technique in the first place was so that they could make the game look better (otherwise they would have settled for less colors and effects), while glitches like these make the game look worse. If they had caught the compatibility issues earlier on, I'm sure they'd have done things differently.

Quote:
It seems really strange to me to complain about a bug that wasn't even present on any platform the release was for (i.e. not actually a bug).

I wasn't complaining. I don't give a shit if a game I don't play much (although I do sorta like Micro Machines) has glitches here and there or whether emulator authors are having trouble running it. If you read my first post in this thread again you'll see that I was just surprised that they did it the way they did instead of using a less complex technique or even not implementing the effects at all, since they don't add much to the experience. Maybe you are right and they simply didn't perceive their choices as risky, or just didn't care.
Re: Micro Machines glitches
by on (#142140)
tokumaru wrote:
rainwarrior wrote:
Without any official documentation, there's no reason at all to assume the behaviour isn't consistent.

I agree with this regarding many other behaviors, but this one in particular is just too damn weird to be considered stable... If reverse engineering takes time, do you think they studied in depth what the values being read back from $2004 even meant? Do you think they got to figure out the sprite evaluation logic based on those reads? At first glance, a register that returns quickly changing values when you read it in a loop looks like the last think you'll want to trust.

You'd be surprised. The Apple II had one of the registers return something from the state of the video circuitry. Its mouse card used that to synchronize to vertical blanking.
Re: Micro Machines glitches
by on (#142144)
tokumaru wrote:
If reverse engineering takes time, do you think they studied in depth what the values being read back from $2004 even meant? Do you think they got to figure out the sprite evaluation logic based on those reads? At first glance, a register that returns quickly changing values when you read it in a loop looks like the last think you'll want to trust.
...
I wonder if they were careful enough while going this route, considering that there were safer alternatives.
...
rainwarrior wrote:
You act like disassembling a game isn't a monumental effort of its own.

And figuring out the details of the rendering logic inside the PPU by fiddling with its registers isn't?

I can't speculate very specifically about what they knew, or how much time they spent on circuit analysis vs. disassembly, but given the unusual solutions present in Codemasters games, I'd guess they probably were able to do more analysis of the machine than disassembly of other software. I do think that at the time, with the tools available, disassembly was a lot more time intensive / expensive to do than hardware analysis. That's not at all the case now, of course, but I think it was for them.

I doubt they knew all the consequences and behaviour of $2004, but they knew enough to figure out a way to write some sort of scanline timer with it, and it worked reliably for them. Remember their goal was to build software for the machine, not a clone of the machine itself. They only needed to know things that help you build software.

Were they careful enough? I'd say yes. If it ran without problem on all target platforms, the risk they were taking paid off.

tokumaru wrote:
I wasn't complaining. I don't give a shit if a game I don't play much (although I do sorta like Micro Machines) has glitches here and there or whether emulator authors are having trouble running it. If you read my first post in this thread again you'll see that I was just surprised that they did it the way they did instead of using a less complex technique or even not implementing the effects at all, since they don't add much to the experience. Maybe you are right and they simply didn't perceive their choices as risky, or just didn't care.

I dunno, I guess to me... I look at this and see a successfully finished game, and I admire it for being that. You can look back on any game ever released, with all the knowledge gained across the years in your hindsight, and find all sorts of things and say "that's not how I would have done it". Maybe it puts me on the defensive because in my professional life I've seen so much time wasted in useless arguments about how something should be implemented. And you know, most of the time a bad implementation is not even how the developer wanted to do it, really, but a consequence of the limited time and money available.

If I were manging a project, I would not demand a perfect elegant solution to the problem I have from a (cheap) junior programmer. Conversely, I would expect an (expensive) experienced programmer not to waste my money refactoring an inelegant solution that works fine. The only place for perfect code is where the scope is small enough that time and money are irrelevant.

As far as I can tell, this feature in Micro Machines worked perfectly, so in that respect I kind of feel it's above reproach here. Obviously with what we know and practice now it's not a good technique to use, but I see nothing at all wrong with it in its original context.
Re: Micro Machines glitches
by on (#142146)
From the wiki, $2004 returns $FF for PPU cycles 1--64 and likely not $FF for the rest of the scanline. Maybe that's the only thing that is relevant here.
Re: Micro Machines glitches
by on (#142147)
Code:
if (rendering)
{
   if (current_cycle < 64)
      return_value = 0xFF;
   else if (current_cycle < 256)
      return_value = 0x00;
//Micro Machines relies on this:
   else if (current_cycle < 320)
      return_value = 0xFF;
//and this:
   else
      return_value = sprite_buffer[0];
}
Re: Micro Machines glitches
by on (#142148)
James wrote:
Code:
if (rendering)
{
   if (current_cycle < 64)
      return_value = 0xFF;
   else if (current_cycle < 256)
      return_value = 0x00;
//Micro Machines relies on this:
   else if (current_cycle < 320)
      return_value = 0xFF;
//and this:
   else
      return_value = sprite_buffer[0];
}


That does minimize the effect:

Image

That black line flickers. Many frames are completely line-free. It's almost doing the job.

I'm not sure where to take it from here though.
Re: Micro Machines glitches
by on (#142149)
sprite_buffer[0] ?

Is this the primary OAM (max. 64 sprites) or the secondary OAM (max. 8 sprites)?
Re: Micro Machines glitches
by on (#142150)
Secondary
Re: Micro Machines glitches
by on (#142151)
Hmm... Guess I'm the only one to say... this code makes no difference running MM. :shock:
Re: Micro Machines glitches
by on (#142153)
Zepper wrote:
Hmm... Guess I'm the only one to say... this code makes no difference running MM. :shock:


Before:

Image

After:

Image
Re: Micro Machines glitches
by on (#142160)
Random question, but what operations is the game doing exactly once it hits the point it wants? (because don't forget you need to make sure that part is properly timed as well) And where is it getting the graphics for that line? Not like I know much about the NES inner workings but I wouldn't be surprised if it's expecting something to have a delayed side effect or something like that.
Re: Micro Machines glitches
by on (#142165)
Sik wrote:
Random question, but what operations is the game doing exactly once it hits the point it wants? (because don't forget you need to make sure that part is properly timed as well) And where is it getting the graphics for that line? Not like I know much about the NES inner workings but I wouldn't be surprised if it's expecting something to have a delayed side effect or something like that.


The sprite 0 hit occurs toward the end of scanline 16. I experimented with advancing or delaying that signal. It offsets where the black line appears, but the line just wraps around the frame. It never vanishes into the hidden horizontal area.

When rendering is disabled (sprites and background is off) and V >= $3F00, it uses this color:

Code:
palette[readVRAM(V) & 0x3F]


If I modify that code to return white instead of a palette entry, it produces this:

Image

That white bar stretches perfectly from the left side to the right side. The location of the black line is within that region. Meaning, the code disables rendering and then it alters the palette entry from blue to black and then back to blue.

Or, V advances to a palette entry that was set to black in that section.

I noticed that when V is divisible by 4, it produces blue. For instance, if I change the code to:

Code:
palette[readVRAM(0x3F0C) & 0x3F]


Then, the black line disappears entirely. But, the subsequent menu screen is missing some graphics.

This might indicate that my V register is not update at the right moments in time.
Re: Micro Machines glitches
by on (#142166)
Are you accounting for palette mirroring and this, re: $3F04/$3F08/$3F0C?
wiki wrote:
If the current VRAM address points in the range $3F00-$3FFF during forced blanking, the color indicated by this palette location will be shown on screen instead of the backdrop color. (Looking at the relevant circuitry in Visual 2C02, this is an intentional feature of the PPU and not merely a side effect of how rendering works.) This can be used to display colors from the normally unused $3F04/$3F08/$3F0C palette locations. A loop that fills the palette will cause each color in turn to be shown on the screen, so to avoid horizontal rainbow bar glitches while loading the palette, wait for a real vertical blank first using an NMI technique.
Re: Micro Machines glitches
by on (#142167)
James wrote:
Are you accounting for palette mirroring and this, re: $3F04/$3F08/$3F0C?


Yes. Per the wiki:

Quote:
Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C. Note that this goes for writing as well as reading. A symptom of not having implemented this correctly in an emulator is the sky being black in Super Mario Bros., which writes the backdrop color through $3F10.
Re: Micro Machines glitches
by on (#142168)
zeroone wrote:
Yes. Per the wiki:

Quote:
Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C. Note that this goes for writing as well as reading. A symptom of not having implemented this correctly in an emulator is the sky being black in Super Mario Bros., which writes the backdrop color through $3F10.

What about the colors at $3F04, $3F08 and $3F0C? These were once believed to be mirrors of $3F00, but turns out they are individual memory locations, and the colors in these positions are only visible when rendering is off and the VRAM address is pointing at them.
Re: Micro Machines glitches
by on (#142173)
I can reproduce the black line issue by breaking writes to the palette. Specifically, by mirroring $3F00 writes to $3F04, $3F08, and $3F0C. Writes shouldn't be mirrored here, but reads from these locations while rendering should return the value at $3F00.
Re: Micro Machines glitches
by on (#142179)
The problem is that "piece of" line in dark blue.
Re: Micro Machines glitches
by on (#142183)
James wrote:
I can reproduce the black line issue by breaking writes to the palette. Specifically, by mirroring $3F00 writes to $3F04, $3F08, and $3F0C. Writes shouldn't be mirrored here, but reads from these locations while rendering should return the value at $3F00.


Unfortunately, that did not the solve the problem for me. Is this read-only mirroring mentioned in the wiki? I don't recall reading about this before.

Thanks for your suggestions.
Re: Micro Machines glitches
by on (#142194)
@zeroone
Currently, what's the problem with Micro Machines? Just a recap.
Re: Micro Machines glitches
by on (#142205)
Zepper wrote:
@zeroone
Currently, what's the problem with Micro Machines? Just a recap.


On the title screen and on the player select screen, there are black lines in the middle-right and other minor distortions. The game itself seems to play correctly. Scroll through the images in this thread to see some of the slight improvements that have happened.
Re: Micro Machines glitches
by on (#142215)
Quote:
Specifically, by mirroring $3F00 writes to $3F04, $3F08, and $3F0C. Writes shouldn't be mirrored here, but reads from these locations while rendering should return the value at $3F00.


Writing to $3F00 mirrors to locations 4/8/C, but writes to 4/8/C are NOT mirrored to $3F00..??? And reading from 4/8/C should return data at $3F00..?
Re: Micro Machines glitches
by on (#142216)
The Wiki article on PPU palettes wrote:
Addresses $3F04/$3F08/$3F0C can contain unique data, though these values are not used by the PPU when normally rendering (since the pattern values that would otherwise select those cells select the backdrop color instead). They can still be shown using the background palette hack, explained below.

Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C. Note that this goes for writing as well as reading. A symptom of not having implemented this correctly in an emulator is the sky being black in Super Mario Bros., which writes the backdrop color through $3F10.


I don't think there's any special case for reading vs writing. $3F04/8/C is not a mirror for $3F00, there's just no way to display it on the screen normally (except by setting the PPU address to it). $3F10 is a mirror for $3F00, though, and $3F14 for $3F04, etc. There's 28 unique bytes in the palette (with 4 mirrors), and only 25 can display to the screen.
Re: Micro Machines glitches
by on (#142220)
Just to be crystal clear. Writing to $3F10 mirrors to $3F00, but does writing to $3F00 mirror to $3F10? Since SMB background was mentioned, $3F00 could have unique data instead of mirroring to $3F10.
Re: Micro Machines glitches
by on (#142224)
Zepper wrote:
Just to be crystal clear. Writing to $3F10 mirrors to $3F00, but does writing to $3F00 mirror to $3F10? Since SMB background was mentioned, $3F00 could have unique data instead of mirroring to $3F10.


I don't understand what the one-way concept of the mirror is supposed to be. There is only one piece of data, but two addresses you can access it with. It doesn't make a difference which one you use.
Re: Micro Machines glitches
by on (#142227)
rainwarrior wrote:
Zepper wrote:
Just to be crystal clear. Writing to $3F10 mirrors to $3F00, but does writing to $3F00 mirror to $3F10?

I don't understand what the one-way concept of the mirror is supposed to be.

The mistaken concept might have been that there exist two pieces of data and a particular address would select both for writing. It's similar to how the Codemasters mapper selects both the lockout chip and the bank switching circuitry at $E000-$FFFF.
Re: Micro Machines glitches
by on (#142228)
Even Nintendulator have this "piece of" dark blue line. So, I believe the following code is NOT correct after all... :(
Code:
              int offset = VRAMaddr & 0x1F;
              int value = data & 0x3F;
              ram_color[offset] = value;
              //Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C.
              if( !(offset & 3) )
                 ram_color[offset ^ 0x10] = value;
Re: Micro Machines glitches
by on (#142229)
Zepper wrote:
Even Nintendulator have this "piece of" dark blue line. So, I believe the following code is NOT correct after all... :(
Code:
              int offset = VRAMaddr & 0x1F;
              int value = data & 0x3F;
              ram_color[offset] = value;
              //Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C.
              if( !(offset & 3) )
                 ram_color[offset ^ 0x10] = value;


Can you post a screen shot of Nintendulator?
Re: Micro Machines glitches
by on (#142231)
Not needed, here's another. See the darker line there? Only Nestopia has a perfect image.
Re: Micro Machines glitches
by on (#142232)
Zepper wrote:
Not needed, here's another. See the darker line there? Only Nestopia has a perfect image.


My emulator looks just like that! Is Nestopia cheating?
Re: Micro Machines glitches
by on (#142237)
No. It uses a cryptic code though... :(
Re: Micro Machines glitches
by on (#142253)
What about puNES?
Re: Micro Machines glitches
by on (#142256)
Zepper wrote:
Even Nintendulator have this "piece of" dark blue line. So, I believe the following code is NOT correct after all... :(
Code:
              int offset = VRAMaddr & 0x1F;
              int value = data & 0x3F;
              ram_color[offset] = value;
              //Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C.
              if( !(offset & 3) )
                 ram_color[offset ^ 0x10] = value;

Oh, that code is definitely correct - the behavior was discovered by early tests and later confirmed by examining the silicon itself.

As explained earlier, there are 28 "bytes" (only 6 bits wide, though) of palette SRAM inside the PPU, but 32 addresses by which they can be selected. Most addresses select unique cells, but $3F10 selects the same cell as $3F00 (and vice-versa, by definition), and the same for 3F14/3F04, 3F18/3F08, and 3F1C/3F0C, and that obviously affects both reading and writing - in my code, I chose to do the mirroring during writes because it's faster (writes tend to be counted in dozens, while reads tend to be counted in tens of thousands).

It's more likely that the graphical glitch in Nintendulator is being caused by something else. Exactly what, I can't be sure without checking more closely - probably a timing glitch or something.
Re: Micro Machines glitches
by on (#142273)
Notice that my emu uses a different way of rendering graphics, and the glitch is exactly the same. ^_^;;
Re: Micro Machines glitches
by on (#142278)
From this thread:

Quote:
I also fixed Micromachines which requires exact cycle accuracy on the 2004 readback during rendering. even being off a cycle or two is too much- it won't work right. Once I fixed that so the readback is exactly what it should be, the game worked great.
Re: Micro Machines glitches
by on (#142323)
I didn't know about wrapping to zero if the sprite address is incremented from $FF. Is this documented?? Other than that, the Nestopia source doesn't bring much info after all. Here we go.
Code:
NES_POKE_D(Ppu,2004)
      {
         Update( cycles.one );

         NST_ASSERT( regs.oam < Oam::SIZE );
         NST_VERIFY( IsDead() );

         if (IsDead())
         {
            if ((regs.oam & 0x03) == 0x02)
               data &= 0xE3;
         }
         else
         {
            data = 0xFF;
         } //note: I wonder about "data" being used uninitialized..?

         byte* const NST_RESTRICT value = oam.ram + regs.oam;
         regs.oam = (regs.oam + 1) & 0xFF;
         io.latch = data;
         *value = data;
      }

      NES_PEEK(Ppu,2004)
      {
         NST_ASSERT( regs.oam <= 0xFF );

         if (!(regs.ctrl[1] & Regs::CTRL1_BG_SP_ENABLED) || cpu.GetCycles() - (cpu.GetFrameCycles() - (341 * 241) * cycles.one) >= (341 * 240) * cycles.one)
         {
            io.latch = oam.ram[regs.oam];
         }
         else
         {
            Update( cycles.one );

            io.latch = oam.latch;
         }

         return io.latch;
      }
Re: Micro Machines glitches
by on (#142327)
Zepper wrote:
I didn't know about wrapping to zero if the sprite address is incremented from $FF. Is this documented??

Well, sprite DMA is just a stream of writes to $2004, so if address didn't wrap it wouldn't work at all if one didn't set $2003 before each write to $4014. Also, it would require more logic to do something different than wrapping for a 8-bit up counter.
Re: Micro Machines glitches
by on (#146277)
My Everdrive N8 with MM shows more glitches than the example on YouTube. Are you sure that is not emulated? Must be a stock cart? How do I switch to read mapper 2?
Re: Micro Machines glitches
by on (#146291)
Why is it nobody in this thread (from what I can discern) has taken the time to check this out on actual hardware? Are people unaware of the fact that it looks glitchy even on the real thing? Here's proof, a la kevtris:

https://www.youtube.com/watch?v=oNkbuQx5v0E&t=4m40s

You guys are trying to solve something that shouldn't need to be solved. The level of obsession over this is amazing.
Re: Micro Machines glitches
by on (#146309)
koitsu, I posted a link to a real hardware video on page 2 of this thread.

It's not glitchy on a real NES (I have tested this, and so have others). Kevtris' video is demonstrating that Micro Machines isn't quite right on his HD recreation, but close. (He even says this a few seconds before the place in the video you linked.)

It is very slightly glitchy on a Famicom, however. I verified this on page 3 when I tested it there as well.
Re: Micro Machines glitches
by on (#179566)
There are 2 version of Micro Machines - one with 1991 on title screen (works better on PAL Famiclone, without any glitches, on NTSC there are minor glitches) and one with 1992 on title screen (works without any glitches on NTSC, hangs after codemasters logo on PAL).
Re: Micro Machines glitches
by on (#190424)
zeroone wrote:

After:

Image


Sorry for bumping an old thread, but I recently improved my emulators timings and I ended up with this same black line that flashes every other frame.

I dug a little into the problem and this is what I found:

That black line occurs at scanline 109. Right where it starts is where a $2006 write (the second one) occurs to change the VRAM address to $3F60.

The problem actually occurs in scanline 108. On alternating frames , the horiz(v) increment at 328 is missed because rendering is turned off slightly too early. This causes a write to $2007 that should go to $3F10 (a mirror of $3F60) to go to $3F09 instead. Then on the next scanline the wrong palette color is in the address so the black line appears.

According to the emualator, rendering is disabled at either 326 or 328 in scanline 108. Presumably the 2 pixel difference is due NMI happening at 1 cpu cycle different timing between frames and 1 of the 3 pixels is used up due to the skipped pixel on alternating frames.

So that is the cause of the black line. The question is, what is different between emulators that this line isn't showing up in others? What is the correct way to fix the problem?

EDIT: Turns out I had an order of operations problem. I was incrememnting the cycle count of the PPU before executing the CPU read, so for the $2004 read code, I had to use '<=' instead of '<' when comparin to the cycle time. Simple fix and Micro Machines now looks perfect! However, in order for this to work I have to do the 'PPU on' check for the horiz(v) increment 2 cycles before it happens. This needs investigating.
Re: Micro Machines glitches
by on (#190631)
Alyosha_TAS wrote:
Sorry for bumping an old thread, but I recently improved my emulators timings and I ended up with this same black line that flashes every other frame.

I dug a little into the problem and this is what I found:

That black line occurs at scanline 109. Right where it starts is where a $2006 write (the second one) occurs to change the VRAM address to $3F60.

The problem actually occurs in scanline 108. On alternating frames , the horiz(v) increment at 328 is missed because rendering is turned off slightly too early. This causes a write to $2007 that should go to $3F10 (a mirror of $3F60) to go to $3F09 instead. Then on the next scanline the wrong palette color is in the address so the black line appears.

According to the emualator, rendering is disabled at either 326 or 328 in scanline 108. Presumably the 2 pixel difference is due NMI happening at 1 cpu cycle different timing between frames and 1 of the 3 pixels is used up due to the skipped pixel on alternating frames.

So that is the cause of the black line. The question is, what is different between emulators that this line isn't showing up in others? What is the correct way to fix the problem?

EDIT: Turns out I had an order of operations problem. I was incrememnting the cycle count of the PPU before executing the CPU read, so for the $2004 read code, I had to use '<=' instead of '<' when comparin to the cycle time. Simple fix and Micro Machines now looks perfect! However, in order for this to work I have to do the 'PPU on' check for the horiz(v) increment 2 cycles before it happens. This needs investigating.


The black line does not appear in Nintendulator 970, but it is present in the newer Nintendulator 975. The older 970 contains a hack where the X/Y-increments occur 4 dots earlier than they should. Nintendulator 975 attempted to be more accurate by moving the increments closer to where they should be; however, accuracy problems manifested itself, such as in this game.

Can you elaborate on your "PPU on" check?
Re: Micro Machines glitches
by on (#190659)
zeroone wrote:
The black line does not appear in Nintendulator 970, but it is present in the newer Nintendulator 975. The older 970 contains a hack where the X/Y-increments occur 4 dots earlier than they should. Nintendulator 975 attempted to be more accurate by moving the increments closer to where they should be; however, accuracy problems manifested itself, such as in this game.

Can you elaborate on your "PPU on" check?



Are there any emulators that contain no hacks, do $2004 reads correctly, and still emualte micro machines without graphical errors?

By "PPU on" check I simply mean checking if the PPU is rendering before doing the increment, as the wiki says that increments only happen if this is the case. I did this check one tick earlier then usual and it removed the bug (obviously since rendering was being turned off early.) This caused other bugs though so I have reverted that back to the way it was and the black line remains.

I implemented all the $2004 read characteristics documented by Quietust and others, so in my emulator read2004.nes now shows this:

Image

This matches what Quietust posted with one extra FF at the beginning for some reason (the FF's at the end are just RAM initialized to FF.) Nothing I do can get me to exactly 4 starting FF's, so maybe that is a hint at the problem? Or are there just a variable number of FF's?

This is enough to remove the black line on the title screen but not on the subsequent select one/two player screen.

So yeah, I'm currently at a loss as to how fix the black line.
Re: Micro Machines glitches
by on (#190670)
Alyosha_TAS wrote:
This matches what Quietust posted with one extra FF at the beginning for some reason (the FF's at the end are just RAM initialized to FF.) Nothing I do can get me to exactly 4 starting FF's, so maybe that is a hint at the problem? Or are there just a variable number of FF's?

The number of FFs you get is timing-dependent - each number corresponds to a single PPU cycle (i.e. 1/3 of a CPU cycle), so clock alignment has a very noticeable effect.
Re: Micro Machines glitches
by on (#190682)
Thanks for bringing up that test rom - I did not even know it existed!
This made me notice a few things that were wrong/broken about Mesen's sprite overflow bug & $2004 reads.
I've managed to fix the result to be near-identical to the screen you posted (6 FFs on mine - I can alter this by changing the timing between the CPU/PPU, like Quietust said)

Things that were wrong in Mesen:
1- Reads from $2004 during sprite evaluation (cycles 65 to 256) were completely wrong (never returned the correct result)
2- Fixing 1) made me notice I was also not returning the secondary OAM values on even cycles once the secondary oam was full.
3- The sprite overflow bug wasn't being emulated correctly - after the $00 read that "fixes" the bug in the test, my code did not read the next 3 bytes properly and didn't realign correctly after those 3 bytes.

.. And yet none of these mattered. Fixing them changed nothing to the result of all 225 test roms I test Mesen against, and Micro Machines still works correctly (like it did before).

For Micro Machines to work properly, it's the $2004 reads outside sprite evaluation that matter (cycles < 65 || cycle > 256), if I remember correctly. Those reads are based on the timing of sprite rendering (rather than sprite evaluation).

Hopefully this at least lets you rule out some possibilities and helps you figure out what your issue is!
Re: Micro Machines glitches
by on (#190690)
@sour: at what cycle is rendering disabled on scanline 108 on the micro machines title screen in mesen? This is really the heart of the problem. Some other test roms you might not be aware of are oam.nes and oam3.nes . They outright crash mesen :shock:
Re: Micro Machines glitches
by on (#190693)
Ah, there we go.
The "rendering enabled" flag is delayed by 2 cycles in Mesen (I guess that's what you'd call a hack!)
If the CPU writes to $2000 on cycle 326 - cycles 327 & 328 are both considered to have rendering still active.

This is the only way I had found to fix Battleloads while passing all other tests a long time ago.
I wasn't aware it happened to have an impact on Micro Machines though.

I wouldn't be surprised if something like this is actually a thing, considering the recent thread that discovered (still unconfirmed on hardware, though) that the video ram address update done after the 2nd write to $2006 seems to be delayed by 3-4 cycles.

Whether or not a delay exists in the rendering flag's state would probably be fairly easy to figure out with the Visual 2C02/Visual NES, though I don't have time to check right now.


I actually found out about the oam3 tests a couple of weeks ago - the crash was a silly memory mapping issues due to the rom's unusual NES 2.0 headers if I recall correctly. It's fixed in the code, but not in the latest release. Thanks for mentioning it though!
Re: Micro Machines glitches
by on (#190714)
I managed to get 4 FF's by adjusting the power up timing of the emulator. It now also matches the startup behaviour indicated in the wiki so I guess that solves that.

Delaying the flag does indeed fix the black line (although Battletoads wasnt crashing for me before that so not sure what's happening there exactly.) Now Micro Machines runs without glitches, although that delay definitely needs confirmation.

I can't help but wonder what other delays exist in the ppu, seems like a can of worms might be opening up here. :?
Re: Micro Machines glitches
by on (#190718)
@Sour @Alyosha_TAS Do your emulators delay $2007 VRAM reads and writes like Nintendulator?
Re: Micro Machines glitches
by on (#190726)
zeroone wrote:
@Sour @Alyosha_TAS Do your emulators delay $2007 VRAM reads and writes like Nintendulator?


No, mine doesn't, it seems plausible given what is known about $2006.

What situation/game does this effect?
Re: Micro Machines glitches
by on (#190745)
I'm not quite sure what you mean by VRAM read/writes delays, so I'll go ahead and assume I haven't implemented any. I haven't looked at Nintendulator's source in a long time, either..

I played a bit with the Visual NES, this might not be 100% accurate (especially since I did this in like 15 minutes), but nonetheless:
Code:
0A7960,55,F1,0819,19,18,FF,00,DF,2001,00,01,01,00,  ;PPU rendering is disabled (01 in before-last column)
0A7961,55,F1,0819,19,18,FF,00,18,2001,00,00,01,00,  ;CPU starts writing $18 to $2001, which will enable rendering
0A7961,56,F1,0819,19,18,FF,00,18,2001,00,00,01,00,  ;PPU Cycle transitions from 85 to 86 ($55 -> $56)
0A7962,56,F1,0819,19,18,FF,00,18,2001,00,00,01,00,
0A7962,56,F1,0819,19,18,FF,00,18,2001,00,00,01,00,
0A7963,56,F1,0819,19,18,FF,00,18,2001,00,00,01,00,
0A7963,56,F1,0819,19,18,FF,00,18,2001,00,00,01,00,
0A7964,56,F1,0819,19,18,FF,00,18,2001,00,00,01,00,
0A7964,56,F1,0819,19,18,FF,00,18,2001,00,00,01,00,
0A7965,56,F1,0819,19,18,FF,00,18,2001,00,00,01,00,
0A7965,57,F1,0819,19,18,FF,00,18,2001,00,00,01,00,  ;PPU cycle transition from 86->87 ($56 -> $57)
0A7966,57,F1,0819,19,18,FF,00,18,2001,00,00,01,00,
0A7966,57,F1,0819,19,18,FF,00,18,2001,00,01,00,01   ;Rendering is enabled (before-last column turns to 00), 1.5ish PPU cycles after the write
So there are 12 master clocks (1.5 ppu clocks) between the start of the write & the rendering flag being enabled.
This assumes I'm tracing the right signals, obviously - but it is called "rendering_disabled", so it's a pretty good bet.

Again, though, testing with Visual NES is not enough to prove anything, would need a test rom of sorts (but I do not have the slightest clue how one could test this, so I'm definitely not going to be the one writing a test for it :p)