Techniques to detect scanline
Moderator: Moderators
Techniques to detect scanline
Besides sprite-0 hit and interrupts generated by mappers implementations like MMC3, which options exists for scanline detection for proper screen split, name table swap, chr bank switch, etc..?
Thanks in advance.
Thanks in advance.
Re: Techniques to detect scanline
That or 9 sprites on a line, which is mostly useful for a top status bar split.
Re: Techniques to detect scanline
The options are very limited. On a stock NES, only the sprite 0 hit and the sprite overflow flags will let you know which scanline the PPU is rendering. The sprite overflow can be triggered unintentionally though, so it's only reliable if you have full control over the number of sprites until the 9 sprites that will trigger the overflow. One big disadvantage of using these 2 flags is that you have to waste CPU time waiting for them.
The other thing you can do on a stock NES is abuse DPCM IRQs. You can configure the APU to fire an IRQ when a DPCM sample finishes playing, so it's possible to have the APU play a silent sample and fire an IRQ when it ends. This would work great if the APU started playing a sample exactly when you told it to, because IRQs would always fire a constant amount of time later, but unfortunately the APU doesn't work like that, and you have to "calibrate" the timing difference between the APU and the PPU and compensate for that difference every time an IRQ fires. I's quite a very complicated technique to pull off... I personally could never make it as steady as I'd like.
The other thing you can do on a stock NES is abuse DPCM IRQs. You can configure the APU to fire an IRQ when a DPCM sample finishes playing, so it's possible to have the APU play a silent sample and fire an IRQ when it ends. This would work great if the APU started playing a sample exactly when you told it to, because IRQs would always fire a constant amount of time later, but unfortunately the APU doesn't work like that, and you have to "calibrate" the timing difference between the APU and the PPU and compensate for that difference every time an IRQ fires. I's quite a very complicated technique to pull off... I personally could never make it as steady as I'd like.
I didn't know someone had explained this technique in detail... cool!lidnariq wrote:cleverly misusing DPCM IRQs
Re: Techniques to detect scanline
@tepples and @tokumaru, I read all the pages of this topic viewtopic.php?t=6521 and would like to congratulate you for your effort to make this work.
This technique works or not? From the topic I mentioned above, I'd the impression that it didn't...but the wiki tells the opposite: "Unfortunately it's a bit complicated, but used correctly, it can function as a crude scanline counter, eliminating the need for an advanced mapper."
By the way, I've read about 3 times the wiki and didn't get how to implement this yet...
Anyone have any clues how Battletoads implements all those background effects and still have a status bar, all this into a non-irq discrete board?
This technique works or not? From the topic I mentioned above, I'd the impression that it didn't...but the wiki tells the opposite: "Unfortunately it's a bit complicated, but used correctly, it can function as a crude scanline counter, eliminating the need for an advanced mapper."
By the way, I've read about 3 times the wiki and didn't get how to implement this yet...
Anyone have any clues how Battletoads implements all those background effects and still have a status bar, all this into a non-irq discrete board?
Re: Techniques to detect scanline
register $4010, set the IRQ enable bit.
Also allow irqs by CLI.
Then, at the beginning of NMI, trigger a DMC sample to start. When the sample reaches the end, an IRQ will fire.
You can point the DMC to all zeros, and it shouldn't sound like anything.
Don't touch the output load $4011. It will make popping sounds.
write IRQ code, pointed to by the IRQ/BRK vector, which does the mid screen change you want.
A DMC sample is triggered by...
set address of DMC sample, write to $4012
set length of DMC sample, write to $4013
set sample rate, write to $4010
turn off DMC channel, write $0f to 4015
tuen on DMC channel, write $1f to 4015
Edit, I'm not sure of the order of this. You might have to write to 4010 again after that last 4015 write, because the wiki ways that a write to 4015 clears the dmc irq bit, I can't remember.
It's been a while since I tried this, and I don't have working example code, since I never ended up using it. correct me if I'm wrong.
Also allow irqs by CLI.
Then, at the beginning of NMI, trigger a DMC sample to start. When the sample reaches the end, an IRQ will fire.
You can point the DMC to all zeros, and it shouldn't sound like anything.
Don't touch the output load $4011. It will make popping sounds.
write IRQ code, pointed to by the IRQ/BRK vector, which does the mid screen change you want.
A DMC sample is triggered by...
set address of DMC sample, write to $4012
set length of DMC sample, write to $4013
set sample rate, write to $4010
turn off DMC channel, write $0f to 4015
tuen on DMC channel, write $1f to 4015
Edit, I'm not sure of the order of this. You might have to write to 4010 again after that last 4015 write, because the wiki ways that a write to 4015 clears the dmc irq bit, I can't remember.
It's been a while since I tried this, and I don't have working example code, since I never ended up using it. correct me if I'm wrong.
nesdoug.com -- blog/tutorial on programming for the NES
Re: Techniques to detect scanline
dougeff, your description completely omits the compensation for the APU-PPU desynchronization, which is the "heart" of this technique. Without this compensation, there's not nearly enough precision to do raster effects (the time at which the IRQ fired can vary by up to 4 scanlines). This would only be useful if you were trying to get an IRQ to fire near a sprite 0 hit or sprite overflow in order to reduce the time spent waiting for the flags to change.
I remember tepples getting it to work in a demo, but I couldn't get it to work in a satisfactory way. IIRC, the split points in tepple's demo were fixed, but I was going for something dynamic that would take a variable scanline count. I ALMOST got it to work, the effect would stay stable for several frames, but every once in a while it would be several cycles off for a frame. That alone was a deal breaker for me, but the whole thing was too complex anyway, and there was still too much CPU time being wasted on wait loops, so I kinda wrote off the technique as impractical and moved on.NOOPr wrote:This technique works or not?
Re: Techniques to detect scanline
Ok.
Hypothetically. Could you use another method to improve the timing, such as polling the OAM after the IRQ? Doesn't one of the unlicensed games do this? Micro Machines or something?
(however, I have a feeling that exactly zero emulators would play the game).
Hypothetically. Could you use another method to improve the timing, such as polling the OAM after the IRQ? Doesn't one of the unlicensed games do this? Micro Machines or something?
(however, I have a feeling that exactly zero emulators would play the game).
nesdoug.com -- blog/tutorial on programming for the NES
Re: Techniques to detect scanline
nesdevwiki:PPU sprite evaluation says that:
cycles 1-64 : reading OAMDATA shows $FF
cycles 65-256 : reading OAMDATA returns random contents of OAM
cycles 257-320 : reading OAMDATA returns the entirety of OAM for the upcoming scanline (i.e. "secondary" OAM, mostly repeating the X coordinate for each active sprite)
cycles 321-340,0 : reading OAMDATA returns the Y coordinate for the first sprite that is present on the upcoming scanline
... maybe there's something klever here?
edit x 2: Micro Machines is looking at the contents of the scanline that had the sprite 0 hit and nothing else: in this case, it's looking at the edge between cycle 320 and cycle 321, where the value read back will switch from $FF (because secondary OAM is mostly empty, and $FF is the "empty" value) to 16 (the Y coordinate for the sprite used to generate the sprite 0 hit)
Last edited by lidnariq on Tue Sep 18, 2018 12:15 pm, edited 2 times in total.
Re: Techniques to detect scanline
Sounds interesting, but isn't OAM readback something that doesn't work on all PPU revisions? I seem to remember early Famicoms not allowing it...
Re: Techniques to detect scanline
https://wiki.nesdev.com/w/index.php/PPU ... ad.2Fwrite
...has a very large bullet list about the complications this MMIO reg brings with it. Early model Famicoms can't read from this register, but later model ones can.
P.S. -- I love how the Wiki is now riddled with these arbitrary chip silkscreenings everywhere, intermixed with PAL or NTSC or Dendy, with absolutely no reference table anywhere that I can find -- RP2A03, RP2A03G, 2A03E, 2C02, 2C02E, 2C03, 2C04, 2C05, 2C07, RP2C02G, and it just keeps getting worse. Don't tell me I'm the only one either, because I'm not. While it's good for precision (and I commend that), without a sane reference table none of it helps anyone except for those who are in-the-know. I think this weekend I'm going to make a page that acts as a reference for all of those, then set it up so that all of those strings (when used as links) go to that page.
...has a very large bullet list about the complications this MMIO reg brings with it. Early model Famicoms can't read from this register, but later model ones can.
P.S. -- I love how the Wiki is now riddled with these arbitrary chip silkscreenings everywhere, intermixed with PAL or NTSC or Dendy, with absolutely no reference table anywhere that I can find -- RP2A03, RP2A03G, 2A03E, 2C02, 2C02E, 2C03, 2C04, 2C05, 2C07, RP2C02G, and it just keeps getting worse. Don't tell me I'm the only one either, because I'm not. While it's good for precision (and I commend that), without a sane reference table none of it helps anyone except for those who are in-the-know. I think this weekend I'm going to make a page that acts as a reference for all of those, then set it up so that all of those strings (when used as links) go to that page.
Last edited by koitsu on Tue Sep 18, 2018 12:11 pm, edited 1 time in total.
Re: Techniques to detect scanline
Doesn't work in the older NTSC PPUs (2C02letterless, A, B, C, D, E). But as far as I can tell, the number of NESes/Famicoms/Famiclones without the ability to read OAM or palette memory is a tremendously smaller proportion than the number of SNESes with the DMA-HDMA collision bug.
(P.S. Koitsu: I have an extremely rough draft (mostly "pile of notes") pair of articles here and here)
(P.S. Koitsu: I have an extremely rough draft (mostly "pile of notes") pair of articles here and here)
Re: Techniques to detect scanline
It's possible to arrange for a combination of APU interrupts to either take 0.5 less than a frame or 1.5 cycles more than a frame. Use the former for three frames, the latter for one, etc. and the result will be raster splits with less than 10 cycles of total jitter (not much more than on carts with scan-line IRQ support!). When doing splits every eight lines, each IRQ will need to take about 80 cycles, and the location of stores should vary by about 50 cycles within that 80 (most of the operations necessary to prepare for the next interrupt can be done before or after the video-manipulation stores).tokumaru wrote:dougeff, your description completely omits the compensation for the APU-PPU desynchronization, which is the "heart" of this technique. Without this compensation, there's not nearly enough precision to do raster effects (the time at which the IRQ fired can vary by up to 4 scanlines). This would only be useful if you were trying to get an IRQ to fire near a sprite 0 hit or sprite overflow in order to reduce the time spent waiting for the flags to change.
Re: Techniques to detect scanline
In Battletoads, each level has its own way of achieving the desired effects.
Examples:
Level 1, 2
Sprite Hit at top of screen
Level 3
Timed code from top of screen, sprite hit before bottommost split
Examples:
Level 1, 2
Sprite Hit at top of screen
Level 3
Timed code from top of screen, sprite hit before bottommost split
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
Re: Techniques to detect scanline
Some time ago I proposed the idea of polling the noise channel's length counter state bit in $4015 to wait for approximately half of the visible scalines to be drawn (depends on the timing difference between the CPU and the APU frame sequencer).
It's not the most useful thing in the world and I never really tested it either, it was more of a thought-experiment. The proposed method is:
1) Wait for a fixed time to sync up at the start of VBlank or at the end of it (wait for the sprite 0 flag to be cleared)
2) Write %0001100 to $400F to start the noise channel length counter (selects 2 half-frame clocks for the counter)
3) Write $C0 to $4017 to immediately clock the counter (leaving it with one more clock to go)
4) During the frame, start polling the noise bit $4015 (the closer to the middle of the screen the better)
5) Immediately write to $400F to restart the noise channel to avoid sound artifacts (any value should do because the next NMI will happen faster before even the lowest counter value could expire)
If anything, this lets you free up the other flags for something different, but it's a very fixed-time thing. I can only imagine it being used for a 2-player split-screen solution or something similar if you want to use DPCM samples and you don't have a mapper IRQ.
It's not the most useful thing in the world and I never really tested it either, it was more of a thought-experiment. The proposed method is:
1) Wait for a fixed time to sync up at the start of VBlank or at the end of it (wait for the sprite 0 flag to be cleared)
2) Write %0001100 to $400F to start the noise channel length counter (selects 2 half-frame clocks for the counter)
3) Write $C0 to $4017 to immediately clock the counter (leaving it with one more clock to go)
4) During the frame, start polling the noise bit $4015 (the closer to the middle of the screen the better)
5) Immediately write to $400F to restart the noise channel to avoid sound artifacts (any value should do because the next NMI will happen faster before even the lowest counter value could expire)
If anything, this lets you free up the other flags for something different, but it's a very fixed-time thing. I can only imagine it being used for a 2-player split-screen solution or something similar if you want to use DPCM samples and you don't have a mapper IRQ.