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

Time to update the screen slower when using sprite 0?

Time to update the screen slower when using sprite 0?
by on (#155666)
There's something that came to my mind recently and I'd like to know if I'm correct:

My game switches between NMI and game logic:

When the game logic is done, a variable is set to 1 and the game logic goes into a wait loop until the variable is 0 again.
When it's 0 again, the game logic is processed for the next frame, including preparation of all the graphics updates. Then the variable is set to 1 again.

When NMI hits, it checks the variable. If it is 0, NMI is immediately left again. If the variable is 1, NMI is processed, including putting the graphics updates to PPU. Then it sets the variable to 0.

This means the game updates its graphics during vblank and does all the preparation and game logic in the time when the 240 scanlines are drawn. Just like every game is supposed to do.

But now I also wait for a sprite 0 hit. But I don't use it for a status bar, but for parallax scrolling. This means the sprite 0 is not located somewhere at the top, but right in the middle of the screen.

So, my NMI basically looks like this (pseudo code):
Code:
NmiStart:
SaveRegisters
if WaitForNmi = false then goto NmiEnd
GraphicsUpdate
SetScrollingPosition upperScreen
while not Sprite0Hit do nothing
SetScrollingPosition lowerScreen
WaitForNmi = false
NmiEnd:
RestoreRegisters


My question: Since the NMI function runs until it has hit sprite 0, which is in the middle of the screen, does this mean that I only have half the time for processing my game logic that I would usually have?
After all, the NMI doesn't just do its graphics updates and then returns, enabling the game logic again. It does its graphics updates, waits until half of the screen is drawn and only then returns.

If my assumption is correct, is there any way to circumvent this? Or do I have to live with the fact that my game logic time is cut in half when I use parallax scrolling with sprite 0?
How do games do this when they put the status bar at the bottom? This would mean their game logic only has the very little time when it updates the status bar since everything else was just the NMI waiting for sprite 0.
Re: Time to update the screen slower when using sprite 0?
by on (#155668)
Quote:
sprite 0, which is in the middle of the screen, does this mean that I only have half the time for processing my game logic that I would usually have?


Yes.

But, you can do some game logic between the NMI and the sprite 0 hit, as long as it is certain to be shorter than the time till sprite 0 hit.
Re: Time to update the screen slower when using sprite 0?
by on (#155670)
dougeff wrote:
But, you can do some game logic between the NMI and the sprite 0 hit, as long as it is certain to be shorter than the time till sprite 0 hit.

What kind of game logic would that be? I mean, if I have a function with a whole bunch of ifs and elses, function calls and data copying, I cannot just split this in half.

Also, is there a way to do the waiting for sprite 0 as an interrupt? I.e. the NMI ends before vblank, I do the game logic normally and the sprite 0 hit triggers an interrupt where I just do the scrolling adjustment and then it returns to wherever I was in my game logic.
Re: Time to update the screen slower when using sprite 0?
by on (#155672)
Yes, if you spend time polling for sprite 0 hit you'll have less time left for other stuff. This is why scanline IRQs are preferable, if you have the hardware.

You can use tricks like DPCM IRQs to land closer to the sprite 0 hit position to waste less time on polling, but it's never much fun to work with.
Re: Time to update the screen slower when using sprite 0?
by on (#155673)
Quote:
What kind of game logic would that be?


For example, you could run your music code, which is likely to be a fixed cycle length, and some other short functions, like automatic sprite movements.
Re: Time to update the screen slower when using sprite 0?
by on (#155675)
thefox wrote:
Yes, if you spend time polling for sprite 0 hit you'll have less time left for other stuff. This is why scanline IRQs are preferable, if you have the hardware.

My program is supposed to run on mapper 0, so no, I guess I don't have that.

I really hope it won't make any probems. I don't want to have to abolish parallax scrolling. It looks so much cooler.
But I don't want to work with the really difficult stuff either.

So, how do programs with a bottom status bar manage it? Is the time of drawing 1/8 of the screen more than enough for updating the game logic? Or are there only games with a bottom status bar that use more advanced techniques and mappers?

dougeff wrote:
For example, you could run your music code, which is likely to be a fixed cycle length, and some other short functions, like automatic sprite movements.

O.k., music wasn't done by me yet. I'll have to read the tutorials to get to know how it works at all.
Automatic sprite movement might not be apllicable.

I might be able to do some of the graphics copy functions, i.e. copying data into an array that will be read by the NMI for PPU writes. The problem is: Graphics updates are supposed to be the last thing in the game logic: Move the sprites and then update the graphics data. But here it would be the first thing.

Reading the controllers is something else that comes to mind. This is always done at the beginning of the game logic, so I might do this. But for the fact that half the screen time gets unused, I really need to find something else that I can use, so that I don't waste that time.
Re: Time to update the screen slower when using sprite 0?
by on (#155678)
Gradius has a bottom status bar on a simple mapper. I'm told it performs intentional slowdown if more than a specific number of object handlers have run in a given frame.

What you need to do is profile your game engine to find which routines can go above the split point with an acceptable safety margin.
Re: Time to update the screen slower when using sprite 0?
by on (#155681)
Alright, I'll see what I can do.

Intentional slowdown is not an option. Doesn't the game slow down anyway when the game logic takes too long?

I guess creating an interrupt that always hits when the sprite 0 is reached is completely impossible, right?
Re: Time to update the screen slower when using sprite 0?
by on (#155686)
DRW wrote:
I guess creating an interrupt that always hits when the sprite 0 is reached is completely impossible, right?

It can be done if you aren't playing samples through the DMC. You create a silent sample that finishes just before sprite 0, then you play it at each vblank.
Re: Time to update the screen slower when using sprite 0?
by on (#155687)
You mean doing this with music?
Re: Time to update the screen slower when using sprite 0?
by on (#155690)
Yes, using the APU. If you're not using mappers, the only IRQs you have are those generated by the APU. When the APU plays DPCM a sample, it can optionally generate an IRQ when the sample ends. So what you do is play a sample thatis silent and lasts roughly the number of scanlines before the sprite 0 hit.

DPCM IRQs would be really useful for raster effects if you could synchronize them with the PPU properly, but the APU runs at its own pace and just because you start playing a sample at the exact same time every frame it doesn't mean it will play right away and always finish at a predictable time, so you still need sprite 0 hits after the IRQ or another method to compensate the error.
Re: Time to update the screen slower when using sprite 0?
by on (#155691)
I have a demo of a method to compensate for the error, called "DPCM Letterbox". But it's sort of advanced, and if you only need one split, you probably don't need this method. Instead, you can just play a sample that finishes 2 to 10 lines above sprite 0 and fires an IRQ, spin on $2002, and then set the scroll position.
Re: Time to update the screen slower when using sprite 0?
by on (#155780)
I'll have a look at the APU interrupt as soon as I learn sounds on the NES. This will be the next step as soon as my first sprite can interact with the level.
Re: Time to update the screen slower when using sprite 0?
by on (#155784)
Quite frankly, if you have a sprite 0 screen split working now for parallax, and you are implementing your game logic in C from this point, you're going to run into a lot of sprite 0-wait-related game crashes as you add new code. We've seen that CC65 generates machine code of unexpected length and speed, so each new "discovery" might make your perfectly timed parallax die in testing.

I hope that your game engine can run continuously, and totally independently of NMI/Sprite 0 code, through flags, message-passing, and PPU write buffering (meaning game engine slowdown may become inevitable as the buffer is full/empty). That may be the only robust way to mix C-compiled code and PPU-dependent screen code.
Re: Time to update the screen slower when using sprite 0?
by on (#155785)
If the sprite-0 wait happens in an IRQ thread, you could easily have a stable parallax split despite slowdown in the main thread. DPCM IRQ might actually be quite sensible for this.
Re: Time to update the screen slower when using sprite 0?
by on (#155787)
ccovell wrote:
Quite frankly, if you have a sprite 0 screen split working now for parallax, and you are implementing your game logic in C from this point, you're going to run into a lot of sprite 0-wait-related game crashes as you add new code.

What? Why?

ccovell wrote:
We've seen that CC65 generates machine code of unexpected length and speed, so each new "discovery" might make your perfectly timed parallax die in testing.

The parallax works like this:
After updating the screen, I wait for sprite 0. I designed the background so that sprite 0 is positioned in a scanline that consists of one continous color, so the whole change of the scrolling position during mid-frame doesn't produce flicker.

Afterwards, the game logic starts.
When the game logic is running, a variable is set to false. If NMI gets started and discovers that the variable is false, it immediately ends without changing anything. (Except for the whole saving and restoring of the registers.) I.e. if the game logic hasn't finished before the next NMI, NMI doesn't update any graphics, nor the scrolling position.

Is this correct so far or do I do anything wrong?

Pseudo code:
Code:
GameLogic:
    if (WaitForNmi == true)
        goto GameLogicEnd
    DoTheGameLogic
    WaitForNmi = true
GameLogicEnd:
    goto GameLogic

Nmi:
    SaveRegisters
    if (WaitForNmi == false)
        goto NmiEnd
    UpdateGraphics
    UpdateScrolling
    WaitForSprite0
    UpdateOtherScrolling
    WaitForNmi = false
NmiEnd:
    RestoreRegisters
Re: Time to update the screen slower when using sprite 0?
by on (#155789)
OK, so does your sprite 0 handler still hit even when NMI sees that the game logic is still busy? In that case, then the sprite 0 hit should be fine.

edit: according to your pseudo-code, the sprite 0 hit will not happen if the game logic is still processing when NMI hits. That means there will be a visible scrolling glitch because sprite 0 doesn't restore the parallax scrolling for 1 frame at least.
Re: Time to update the screen slower when using sprite 0?
by on (#155792)
So, the scrolling update still has to be done, even though everything else in the NMI is skipped since the game logic isn't ready?

Why is this the case? I thought when I don't do anything, the background just stays the same...Oh, I guess I understand: If I set the scrolling position twice due to parallax scrolling, then a screen update where NMI is skipped will simply read the last scrolling value and therefore apply the lower scrolling value to the whole screen, right?

If that's correct, well, I guess I can easily change the code:
Code:
GameLogic:
    if (WaitForNmi == true)
        goto GameLogicEnd
    DoTheGameLogic
    WaitForNmi = true
GameLogicEnd:
    goto GameLogic

Nmi:
    SaveRegisters
    if (WaitForNmi == false)
        goto NmiEnd
    UpdateGraphics
    WaitForNmi = false
NmiEnd:
    UpdateScrolling
    WaitForSprite0
    UpdateOtherScrolling
    RestoreRegisters
Re: Time to update the screen slower when using sprite 0?
by on (#155794)
That's the version that leaves you with only have the frame to do your game update. The DPCM IRQ could let you use that:

Code:
GameLogic:
    if (WaitForNmi == true)
        goto GameLogicEnd
    DoTheGameLogic
    WaitForNmi = true
GameLogicEnd:
    goto GameLogic

Nmi:
    SaveRegisters
    SetupIRQTimer
    if (WaitForNmi == false)
        goto NmiEnd
    UpdateGraphics
    WaitForNmi = false
NmiEnd:
    UpdateScrolling
    WaitForSprite0
    UpdateOtherScrolling
    RestoreRegisters

IRQ:
    SaveRegisters
    WaitForSprite0
    UpdateOtherScrolling
    RestoreRegisters


Execution for a typical frame looks like:

1. game thread is wating for NMI update
2. NMI
3. game thread begins updating
4. IRQ
5. game thread finishes updating, go to 1.

For a slowdown frame that takes too long it might look like:

1. game thread is waiting for NMI update
2. NMI
3. game thread begins updating
4. IRQ
5. game thread continues updating
6. NMI
7. game thread continues updating
8. IRQ
9. game thread finall finished, go to 1.

Using the IRQ you don't have to waste half your frame waiting for the sprite 0 hit, and you can deal with slowdown gracefully.
Re: Time to update the screen slower when using sprite 0?
by on (#155796)
I've got two questions about this:

1. Is this easily doable with a simple mapper 0 game or does it require a more advanced mapper to be convenient?

2. Do I have to adjust the value for PAL and NTSC? (My current version doesn't need any adjustments between PAL and NTSC.)
Re: Time to update the screen slower when using sprite 0?
by on (#155799)
DRW wrote:
1. Is this easily doable with a simple mapper 0 game or does it require a more advanced mapper to be convenient?

DPCM IRQ is available with any mapper as long as you don't need the DPCM channel for sample playback.

DRW wrote:
2. Do I have to adjust the value for PAL and NTSC? (My current version doesn't need any adjustments between PAL and NTSC.)

Ideally yes, but in practice maybe not. The only real requirement is that the IRQ trigger sometime before the sprite 0 hit. The closer you can get it, the more time your game thread has to run, but any timing that is before the sprite 0 hit on both platforms would be "good enough" to work.
Re: Time to update the screen slower when using sprite 0?
by on (#155800)
O.k., I'll try it out as soon as I do the whole music stuff. (I don't know anything about it yet.)

Let's see if it is even necessary. Maybe my game will be simple enough that I won't run into a slowdown anyway.