Techniques to detect scanline

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
User avatar
NOOPr
Posts: 68
Joined: Tue Feb 27, 2018 10:41 am
Location: Brazil
Contact:

Techniques to detect scanline

Post by NOOPr » Mon Sep 17, 2018 4:32 pm

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.

lidnariq
Posts: 9316
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Techniques to detect scanline

Post by lidnariq » Mon Sep 17, 2018 4:35 pm

The only remaining option is cleverly misusing DPCM IRQs.

And manually counting cycles.

tepples
Posts: 21949
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Techniques to detect scanline

Post by tepples » Mon Sep 17, 2018 4:35 pm

That or 9 sprites on a line, which is mostly useful for a top status bar split.

User avatar
tokumaru
Posts: 11648
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Techniques to detect scanline

Post by tokumaru » Mon Sep 17, 2018 8:14 pm

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.
I didn't know someone had explained this technique in detail... cool!

User avatar
NOOPr
Posts: 68
Joined: Tue Feb 27, 2018 10:41 am
Location: Brazil
Contact:

Re: Techniques to detect scanline

Post by NOOPr » Tue Sep 18, 2018 7:25 am

@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... :oops:

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?

User avatar
dougeff
Posts: 2675
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Techniques to detect scanline

Post by dougeff » Tue Sep 18, 2018 7:33 am

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.
nesdoug.com -- blog/tutorial on programming for the NES

User avatar
tokumaru
Posts: 11648
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Techniques to detect scanline

Post by tokumaru » Tue Sep 18, 2018 10:49 am

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.
NOOPr wrote:This technique works or not?
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.

User avatar
dougeff
Posts: 2675
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Techniques to detect scanline

Post by dougeff » Tue Sep 18, 2018 11:28 am

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).
nesdoug.com -- blog/tutorial on programming for the NES

lidnariq
Posts: 9316
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Techniques to detect scanline

Post by lidnariq » Tue Sep 18, 2018 11:45 am

OAM polling only really lets you get timing within a scanline; it's not really helpful if your DPCM IRQ could vary by up to four.

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.

User avatar
tokumaru
Posts: 11648
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Techniques to detect scanline

Post by tokumaru » Tue Sep 18, 2018 11:54 am

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...

User avatar
koitsu
Posts: 4217
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Techniques to detect scanline

Post by koitsu » Tue Sep 18, 2018 12:07 pm

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.
Last edited by koitsu on Tue Sep 18, 2018 12:11 pm, edited 1 time in total.

lidnariq
Posts: 9316
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Techniques to detect scanline

Post by lidnariq » Tue Sep 18, 2018 12:10 pm

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)

supercat
Posts: 161
Joined: Thu Apr 18, 2019 9:13 am

Re: Techniques to detect scanline

Post by supercat » Tue May 14, 2019 9:40 am

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.
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).

User avatar
Dwedit
Posts: 4301
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: Techniques to detect scanline

Post by Dwedit » Wed May 15, 2019 1:19 pm

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
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

User avatar
za909
Posts: 207
Joined: Fri Jan 24, 2014 9:05 am
Location: Hungary

Re: Techniques to detect scanline

Post by za909 » Thu May 16, 2019 3:21 am

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.

Post Reply