Page 2 of 2

Posted: Sun Jan 14, 2007 2:43 pm
by tokumaru
I played a lot with the timing and all, but there is some quite big fluctuation that makes it impossible to use this without the aid of sprite 0 hits.

In the end, I'd also lose some time waiting for the sprite hit. Quite a few scanlines, considering the high fluctuation of the time the IRQ fires. Or course, I wouldn't lose the time every frame, as sometimes the IRQ would fire very close to sprite 0.

And as I said earlier, setting up sprite hits may not be trivial because of all the scrolling that goes on. Bregalad's suggestion of using a special tile that is cloned and altered to force the hit was nice, but in the end that's just more "wasted" time (for cloning the tile).

So, I spent some more time on the tasks that should be performed during vblank, looking for a way to alternate between tasks (since not everything needs to be done every frame) in a way that the total time would not require more than 16 "stolen" scanlines (24 was just too much). Also, those tasks are now so tightly packed that I'll rarely be wasting time (there will almost always be something to do).

Routines that alternate are programmed to use the same ammount of time, so that calling one or the other has no effect on timing. It seems to work pretty well, so I'm happy about this method.

So, I ended up not using the DMC interrupts, but I'm glad I learned something new. I now know what you can and can not do with them. And hopefully I'll use them for some other project. So even though I'm not using that know, thank you all (specially blargg) for helping me out with this.

So, since I decided to go with the (16 pixels tall) blank bar at the top of the screen, what do you feel is the best way to color it? Simply leave it as current color 0, or go through the trouble of setting a new color 0 (as black, for example) during vblank and restore original color 0 just before enabling rendering again?

Sometimes I feel like having the black bar, as I think it will be less evident when seen on a TV. However, if the level uses a lot of color 0 on the background, the bar may not be so evident either, since the player will hardly be looking directly at it, it may seem like part of the level since it has the same overall color of the level. What do you think?

Posted: Sun Jan 14, 2007 2:57 pm
by blargg
Hey, simpler is better (if it works).

For the background color, use palette entries $3F04, $3F08, or $3F0C for your border color(s), as these three distinct entries are otherwise never displayed during rendering. This way you don't have to save/restore the color every frame. See this thread: http://nesdev.com/bbs/viewtopic.php?p=8346

Posted: Sun Jan 14, 2007 3:32 pm
by Disch
I would assume he would have to use $3F00, since he's going to be making PPU accesses for those lines (why else would he want 16 blank scanlines?). You can only use $3F04, $3F08, and $3F0C if you leave the PPU address there -- somewhat defeating the point of extending VBlank.

Posted: Sun Jan 14, 2007 6:10 pm
by tokumaru
blargg wrote:Hey, simpler is better (if it works).
Yeah, that's what I figured too. Now most of the vblank code (copying routines) is ready and I can move on to game logic, map decoding, collision detection, object engine... (wow, there's a lot to go!)! =)

I have most of this stuff figured out (and documented) already though, I just need to write the code. =)
For the background color, use palette entries $3F04, $3F08, or $3F0C for your border color(s), as these three distinct entries are otherwise never displayed during rendering. This way you don't have to save/restore the color every frame.
I actually thought about using that trick (pointing to the color I want to display) but soon I realized that...
Disch wrote:I would assume he would have to use $3F00, since he's going to be making PPU accesses for those lines (why else would he want 16 blank scanlines?).
I was indeed going to make a lot of PPU access, so if I want to use anything specific I'd have to use $3F00.

I have almost decided to leave color 0 as it is (as opposed to setting it to some known value while rendering is off), mainly because it's easier (I don't have to do anything different from what I already am), but also because I believe it's harder for the player to notice that something is missing if the border uses the same overall color of the level.

Posted: Mon Jan 15, 2007 12:42 am
by tepples
tokumaru wrote:I have almost decided to leave color 0 as it is (as opposed to setting it to some known value while rendering is off), mainly because it's easier (I don't have to do anything different from what I already am), but also because I believe it's harder for the player to notice that something is missing if the border uses the same overall color of the level.
Nintendo engineers agreed with you in the early days of the NES when developing Super Mario Bros.

And yes it should be possible to squeeze more-or-less precise timing out of the DMC. Set the rate to (i.e. one sample every 54 cycles on NTSC) and the length to 0 (i.e. one byte, or 8 samples), which should make an IRQ every 432 CPU cycles. Have the IRQ copy the low byte of the program counter from the stack to a variable in zero page and increment the number of IRQs encountered. Then in each frame, do the following:
  1. Starting at a known raster point, run a NOP slide subroutine. The DMC IRQ will fire sometime during this and copy the PC. This gives a delta between the raster and the DMC IRQ.
  2. Wait until nLines*341/(3*432) iterations of the DMC IRQ have elapsed.
  3. Run a partial NOP slide, computing where to start based on the saved program counter.
Anyone want me to write a proof-of-concept ROM based on this principle?

Posted: Mon Jan 15, 2007 8:35 am
by tokumaru
Your idea seems very interesting, but although I think I got the general idea, I'm not familiar with the concepts of "NOP slide subroutines" or "partial NOP slide".

It would be cool to see a proof-of-concept demo, but I'm not sure if it would worth the trouble in a real game.

What do you have in mind when you say "more-or-less precise timing" anyway?

Posted: Mon Jan 15, 2007 8:45 am
by blargg
tepples wrote:Set the rate to (i.e. one sample every 54 cycles on NTSC) and the length to 0 (i.e. one byte, or 8 samples), which should make an IRQ every 432 CPU cycles. Have the IRQ copy the low byte of the program counter from the stack to a variable in zero page and increment the number of IRQs encountered.
And say bye-bye to at least 10% of your CPU time. How many cycles would this IRQ routine take? Getting at the saved PC takes several instructions, and you've got to restart the DMC. If you're going to take a complex route like this, I bet you could use a combination of several different lower DMC rates so that the sum of the delays totals 29380 2/3 CPU clocks each frame (maybe you give the routine feedback every frame or something). Someone needs to examine Fire Hawk and Silent Service to find out how they use the DMC to get an interrupt lower in the frame (at least I've heard they use the DMC for this).

Posted: Mon Jan 15, 2007 10:20 am
by tepples
blargg wrote:
tepples wrote:Set the rate to (i.e. one sample every 54 cycles on NTSC) and the length to 0 (i.e. one byte, or 8 samples), which should make an IRQ every 432 CPU cycles. Have the IRQ copy the low byte of the program counter from the stack to a variable in zero page and increment the number of IRQs encountered.
And say bye-bye to at least 10% of your CPU time.
Compare to about 100% if you have to use timed code for multiple splits.

Posted: Mon Jan 15, 2007 11:51 am
by Disch
Having a length of 0 for the DMC makes good sense to get it started -- but once you have it moving, it makes more sense to lengthen it a bit so that IRQs don't occur as frequently.

Set the length to 0 (1 byte) and have the speed at max. Once that IRQs, slow it down, and maybe increase the length a bit and run the DMC again. If you're careful about when you change the speed, you can keep the timing relatively reliable.

If you [carefully] change the speed to $08, and the length to 1 ($11 bytes), the second IRQ will come 25840 cycles after the first. And since it was triggered by a shorter/faster IRQ, you'll still have the same ~432 cycle window you had before, even though you upped the freq.

I highly suspect MiG 29 Soviet Fighter does something like this, but I haven't checked. I know that emulating that game becomes very difficult unless you have your DMC timing pretty sharp -- which makes sense if it does something like this which relies so heavily on when the IRQ trips and when the frequency change affects the playing samples. Nonetheless, tokumaru, if you plan to go this route -- I would not recommend you test it on anything other than the actual system... as this is the kind of thing that existing emus might be emulating all wrong without knowing it.

Posted: Mon Jan 15, 2007 12:25 pm
by tokumaru
Disch wrote:Nonetheless, tokumaru, if you plan to go this route -- I would not recommend you test it on anything other than the actual system... as this is the kind of thing that existing emus might be emulating all wrong without knowing it.
Got it! Thanks for the tips. =)

Posted: Wed Jan 17, 2007 12:42 pm
by tokumaru
Hello all, it's me again.

I got one more thing to ask, but decided to do it here instead of starting a new thread just for this.

I'm enabling rendering late, OK? Let's say that scanline 16 is the first one that gets rendered. What do I get, in addition to the 16 (0 to 15) scanlines that were not displayed? There are 20 scanlines in vblank, but there's that "pre-render" scanline also right? So do I get 20 + 1 + 16 = 37 scanlines worth of vblank time, is that it?

I know that something happens to the video when rendering is disabled (everyone uses Battletoads as an example for that), I don't know exactly what (because I don't know much about video), I just know it happens because of that scanline that's 1 PPU cycle shorter. I'm not worried about that though, I don't think the video looks bad at all.

So, my other question is: what happens to the pre-render scanline when rendering is disabled? Does it happen after I enable rendering instead, or something weird like that?

Is there anything I should actually be worried about when enabling rendering late?

Posted: Wed Jan 17, 2007 12:53 pm
by tepples
All that really happens is that the dot-crawl pattern changes.

Posted: Wed Jan 17, 2007 6:39 pm
by tokumaru
tepples wrote:All that really happens is that the dot-crawl pattern changes.
If that's the only side-effect, I'm cool. =)