Game loops and Vertical Blank interupts

Ask anything your want about Megadrive/Genesis programming.

Moderator: BigEvilCorporation

OrangyTang
Interested
Posts: 33
Joined: Tue Feb 23, 2016 4:45 pm

Game loops and Vertical Blank interupts

Post by OrangyTang » Wed Apr 04, 2018 9:17 am

I'm vaugely hacking around Gunstar Heroes, and one thing that's confusing me (actually lots of things are, but one at a time :) ) is how it's managing it's game loop.

My understanding is that usually you'd do init and then game loop in the entry point, and wait on the vblank for end of frame. Meanwhile in the vblank interupt you'd do a few things you need correct timing for (dma'ing palettes?) and very little else. Poking around the Sonic 1 disassembly it seems to basically do this.

GH however appears to do all it's logic in the vblank interupt. There is a loop at the end of the entry point, but it's mostly just idling (I managed to get from boot to halfway into a level before it did anything other than nop, and seems to be some kind of loading routine). Is this normal for a MD game? I would have thought that doing so much work in the vblank would make it super hard to do the things you'd normally do in a vblank at the correct time.

Miquel
Very interested
Posts: 514
Joined: Sat Jul 30, 2016 12:33 am

Re: Game loops and Vertical Blank interupts

Post by Miquel » Wed Apr 04, 2018 1:58 pm

As long as you do vblank duty at the beginning of the exception (or interrupt as is called on others cpus) this method works too. Super Mario Bros (NES) works just like this.

The problem is that while you are answering a video exception you can't acknowledge another one, making it a sub-optimal solution. If there is a slow down one consequence will be that music will be delayed and therefore distorted, and curiously that makes a more big impact on the player that video delay for the same period.

If the game can ensure that will be no slow down (frame lost), there is no difference whatsoever.
HELP. Spanish TVs are brain washing people to be hostile to me.

OrangyTang
Interested
Posts: 33
Joined: Tue Feb 23, 2016 4:45 pm

Re: Game loops and Vertical Blank interupts

Post by OrangyTang » Wed Apr 04, 2018 2:21 pm

Hmm, I guess that makes sense. Wouldn't this make it really had to do h-blank effects as well? Because now the vblank code is still running and hblanks can't interupt it to do fancy scanline effects (and from what I can tell, Gunstar Heroes has a lot of hblank effects).

Miquel
Very interested
Posts: 514
Joined: Sat Jul 30, 2016 12:33 am

Re: Game loops and Vertical Blank interupts

Post by Miquel » Wed Apr 04, 2018 2:36 pm

Yes: vertical exception have more priority than the horizontal, in consequence vertical code will mask horizontal exception code, but still you can use the horizontal counter to keep track of the current line to make those effects.

That's in theory, I haven't dissect Gunstar Heroes code.
HELP. Spanish TVs are brain washing people to be hostile to me.

OrangyTang
Interested
Posts: 33
Joined: Tue Feb 23, 2016 4:45 pm

Re: Game loops and Vertical Blank interupts

Post by OrangyTang » Wed Apr 04, 2018 2:45 pm

Ok, that makes sense, thanks!

I still don't know why they wouldn't do it a more 'normal' way, as there doesn't seem to be an advantage to it. Maybe I'll find out when i've taken it apart a bit more. :)

Sik
Very interested
Posts: 939
Joined: Thu Apr 10, 2008 3:03 pm
Contact:

Re: Game loops and Vertical Blank interupts

Post by Sik » Wed Apr 04, 2018 3:36 pm

Then you have Sonic 3D doing part of the game loop in the main thread and part in the vblank interrupt. And the latter was doing the logic for every object on screen. It took a while to figure out how the heck the two were connected.

I honestly prefer the vblank interrupt to just set a flag and nothing else. The vsync subroutine in the main thread can then see it and resume work. If I ever really need to time something strictly to vblank I'll add that to the interrupt (maybe to setup raster effects reliably just in case the game slows down?), but so far vsync alone has been enough.
Sik is pronounced as "seek", not as "sick".

OrangyTang
Interested
Posts: 33
Joined: Tue Feb 23, 2016 4:45 pm

Re: Game loops and Vertical Blank interupts

Post by OrangyTang » Wed Apr 04, 2018 3:45 pm

Yeah, I don't have much experience with MD programming, but in my mind interupts should do the smallest amount of work possible and finish as quickly as possible.

Having said that - I wonder if what GH is doing is using the main thread as a background loading loop? It seems to be idle until memory addresses get poked with load requests. That way the vblank can do important logic, then yield and let the rest of the frame be filled with low-priority streaming. If the streaming is taking too long, the vblank will still kick in and run the next frame. Plausible?

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Re: Game loops and Vertical Blank interupts

Post by TmEE co.(TM) » Wed Apr 04, 2018 6:30 pm

I poll for VBL bit in the VDP in main loop and I also set a flag in the frame interrupt. If main loop sees that the flag is set after all game logic it can assume slowdown has happened and act upon it, when flag isn't set the logic ended before frame did.
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Miquel
Very interested
Posts: 514
Joined: Sat Jul 30, 2016 12:33 am

Re: Game loops and Vertical Blank interupts

Post by Miquel » Thu Apr 05, 2018 1:07 pm

OrangyTang wrote:
Wed Apr 04, 2018 3:45 pm
Having said that - I wonder if what GH is doing is using the main thread as a background loading loop? It seems to be idle until memory addresses get poked with load requests. That way the vblank can do important logic, then yield and let the rest of the frame be filled with low-priority streaming. If the streaming is taking too long, the vblank will still kick in and run the next frame. Plausible?
Actually is a pretty good dam idea... no, it's very close to necessary IF you are building a continuous level loading game, for other types of games it involves more problems than solutions.

Let's recapitulate: exceptions/interruptions allows the programmer to avoid uninterrupted polling along all the code. Then, if you are making a game that follows those rules:
- Game loads into ram continuously the level. So in ram we have a few screens around (or forward) the main character. The farthest is the one is loading.
- Having the level in ram let's you modify it. If you think a bit: you have to chose between the ability to modify it or double scroll direction.
- Only forward scroll, although it's not strictly necessary, helps simplifying things.
- Level is compressed in big chunks, like for example screens, that provides much better compression which is a winner since big levels take a lot of it.

So in the main/start/reset you basically have a procedure to decompress a level which have not to do pulling at all, neither has to be synchronized at a frame level with the game. And in the vertical exception all the remaining things. Half way to a current sand box really.

Plainly this approach is not useful at all for a more conventional game that loads the level only at the start, but other ways it rocks.

Other way to see it is that what makes it so impressive is that you don't need to compress the level in little chucks, probably columns, and accommodate this time inside a frame, but you can do it asynchronously in really big chunks without impact on the frame rate.
OrangyTang wrote:
Wed Apr 04, 2018 3:45 pm
Yeah, I don't have much experience with MD programming, but in my mind interupts should do the smallest amount of work possible and finish as quickly as possible.
That's completely correct within a operative system: just fill the buffer and quit the interrupt as quickly as possible.
But in a MD you can do what ever you want: there is no other program to be taken care of.
HELP. Spanish TVs are brain washing people to be hostile to me.

OrangyTang
Interested
Posts: 33
Joined: Tue Feb 23, 2016 4:45 pm

Re: Game loops and Vertical Blank interupts

Post by OrangyTang » Thu Apr 05, 2018 2:08 pm

Yeah, the more I think about it, the more it makes sense to be a background loading system. Especially as the main thread loop has what looks suspiciously like a decode routine and a whole bunch of vram copy routines.

I suspect GH uses it more for boss loading (it has a lot of bosses in quick succession) rather than level streaming, but we'll see what shakes out. :)

Miquel
Very interested
Posts: 514
Joined: Sat Jul 30, 2016 12:33 am

Re: Game loops and Vertical Blank interupts

Post by Miquel » Sat Apr 07, 2018 4:23 am

Transferring data to the video chip during display period is possible but very slow, unwise because cpu is blocked for much more time than the same data along vblank. For a single value is ok, but for buffer I think is unlikely Treasure did this mistake.

Another potential target to decompress "in the background" is the spawn array which has to be pretty large for this game.
HELP. Spanish TVs are brain washing people to be hostile to me.

Miquel
Very interested
Posts: 514
Joined: Sat Jul 30, 2016 12:33 am

Re: Game loops and Vertical Blank interupts

Post by Miquel » Mon May 14, 2018 12:23 pm

I think I have found a solution to get the best of both words, works but I'm still developing it:

That's the traditional model, stop and load:

Code: Select all

reset:
	jsr initialization
	jmp gameLoop				/* MUST be synchronized to frame*/

vertException:
	movem.l	%d?/%a?, -(%sp)
	jsr doDMAThings
	movem.l	(%sp)+,%d?/%a?
	rte
That's the continuous loading model:

Code: Select all

reset:
	jsr initialization
	jmp backgroundTasks

vertException:
	movem.l	%d?/%a?, -(%sp)
	jsr doDMAThings
	jsr gameLoop				/* No HINT available*/
	movem.l	(%sp)+,%d?/%a?
	rte
That's the continuous loading model with HINT's available:

Code: Select all

reset:
	jsr initialization
	jmp backgroundTasks

vertException:
	movem.l	%d?/%a?, -(%sp)
	jsr doDMAThings
	movem.l	(%sp)+,%d?/%a?
* Copy SR
	subq.l	#2, %sp
	move.w	2(%sp), -(%sp)
* Insert new return address
	move.l	#Frame, 2(%sp)
* It returns from exception but jumps to 'Frame', another 'rts' will jump to what was actually doing
	rte

Frame:
	move		%sr, -(%sp)
	movem.l	%d?/%a?, -(%sp)
	jsr gameLoop
	movem.l	(%sp)+,%d?/%a?
	move		(%sp)+, %sr
	rts
So yes, with a bit of work is possible to obtain HINT's too.
HELP. Spanish TVs are brain washing people to be hostile to me.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Game loops and Vertical Blank interupts

Post by Stef » Mon May 14, 2018 5:13 pm

Why you just don't change interrupt mask level directly in SR register before jumping to gameloop ?

Miquel
Very interested
Posts: 514
Joined: Sat Jul 30, 2016 12:33 am

Re: Game loops and Vertical Blank interupts

Post by Miquel » Mon May 14, 2018 5:29 pm

As mentioned before the problem is during the vertical exception you can receive horizontal ones, because them are of lower priority.

So in the 3rd example what is done is to return from exception but to a custom code, it only saves/restores SR content to preserve flags.

This is kind of a trick (or bad practice if you want): the final example returns from exception but only half.

Also with this trick enables you to receive vertical exceptions during gameLoop to synchronize things like sound. (Also notice the example lacks a variable so synchronize this last part.)

Basically you got 3 "threads":
- Vertical non-interruptible by h/v-ints, to do DMA work
- GameLoop interruptible by h/v-ints, synchronized after Vertical "thread"
- Background: decompress work and alike, completely unsynchronized (unless you code it)
HELP. Spanish TVs are brain washing people to be hostile to me.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Re: Game loops and Vertical Blank interupts

Post by Stef » Mon May 14, 2018 10:09 pm

Yeah i understand but just lowering interrupt mask level in SR register right after doDMAstuff and before jumping to gameloop should be enough to produce the same result, you can read/write SR register... or i'm missing something ?

Post Reply