Original Post

Does anyone familiar with disassembling commercial games know whether any commercial games implemented a small heap manager in the VB RAM for maintaining data structures like event queues and possibly alternative stacks for interrupt processing and the like?

As a follow up, does anyone have any experience writing a small heap manager for VB RAM?

Like it or not, at some point, I’m going to have to write an event queue.. something where the main game logic makes a request to a queue manager to draw to the screen directly, or read the joypad. More generally, a queue to request that the hardware to “do something specific in response to game logic or a change in the game state”.

I could possibly set a static global max for the queue size, but in the case the queue is truly full, that could lead to some significant performance degradation if for instance, a request to send a draw_event has to wait until the next frame.

Ahh, the joys of tradeoffs…

If anyone has alternative ways of handling hardware events that don’t just resort to polling each time a hardware request is made, and are a bit more controlled than “worrying if a received interrupt will interfere with speed-critical game-logic processing or other fun race conditions” I’d love to hear your feedback. Event queues are really the only way I know how to deal with asynchronous hardware processing in a controlled manner, where the game logic still proceeds when a hardware request needs to be made (i.e. no polling) without worrying about being interrupted at any point after processing is done (i.e. defer response to “hardware done” interrupts).

And one last q: Is VB music typically controlled by the timer interrupt (i.e. wait until timer interrupt to change the playing sample)?

7 Replies

cr1901 wrote:
Does anyone familiar with disassembling commercial games know whether any commercial games implemented a small heap manager in the VB RAM for maintaining data structures like event queues and possibly alternative stacks for interrupt processing and the like?

I don’t know, but I guess Red Alarm uses one because of its complexity (huge levels with moving parts and tons of enemies). Maybe Golf does too, although I’m not sure whether a renderer like that requires dynamic memory allocation.

As a follow up, does anyone have any experience writing a small heap manager for VB RAM?

Yes.

(Edit: you’re probably better off writing your own for your specific requirements, e.g. whether you need one that uses handles and can defragment the heap, like the classic Mac OS, or you want one that’s really fast but uses pointers directly, like DOS.)

If anyone has alternative ways of handling hardware events that don’t just resort to polling each time a hardware request is made, and are a bit more controlled than “worrying if a received interrupt will interfere with speed-critical game-logic processing or other fun race conditions” I’d love to hear your feedback.

Can you give some examples? You can often get away with polling, but yes, the code might be more elegant with interrupts.

And one last q: Is VB music typically controlled by the timer interrupt (i.e. wait until timer interrupt to change the playing sample)?

Yes, I think that’s how all commercial games do it. As usual, you can get away with calling a “do sounds” function in a VIP-synchronized loop, but if you want more precise timing, interrupts are the way to go, and you usually don’t have to worry about race conditions.

  • This reply was modified 9 years, 7 months ago by HorvatM.

cr1901 wrote:
Does anyone familiar with disassembling commercial games know whether any commercial games implemented a small heap manager in the VB RAM for maintaining data structures like event queues and possibly alternative stacks for interrupt processing and the like?

I might just be totally wrong here, but I’d like to believe that no commercial games used a heap allocation method akin to malloc() and free(). It’s far simpler to use pre-allocated static memory and use the stack by declaring struct variables in high-order functions.

Virtual Boy has 64KB of on-board memory. If you’re considering setting up a paging system, be careful you don’t wind up using it all just trying to keep track of where it is.

cr1901 wrote:
Like it or not, at some point, I’m going to have to write an event queue..

[…]

Event queues are really the only way I know how to deal with asynchronous hardware processing in a controlled manner, where the game logic still proceeds when a hardware request needs to be made (i.e. no polling) without worrying about being interrupted at any point after processing is done (i.e. defer response to “hardware done” interrupts).

I won’t pretend to know exactly what you have in mind, but I can tell you how I handle things…

My programs have two “threads”, if you will, that run side-by-side to present the user experience: there’s the audio thread, and the video/game thread. I put “threads” in quotation marks because it’s not all that fancy–I didn’t design an OS or anything. It’s just logical processing of interrupts that mimic two distinct code contexts.

Games for earlier systems lumped all operations into one code path. The NSF file format actually specifies whether to process audio programs 50 times a second or 60 times a second, according to the video framerate of the game it came from… but I digress.

My audio thread runs on the timer interrupt, at something like 80 or 100 times a second in order to give a little more precision than the 50fps video clock. If my audio worked just as well at 50 audio frames per second, then I’d definitely be running it on the video/game thread all at once.

The video/game thread is the game logic and processing timed around the performance of the video hardware. It basically comes in four stages in a loop:

* Read game pad state
* Process one video frame’s (1/50 second) worth of game
* [font=Courier New]HALT[/font] and wait for the previous frame to finish drawing (not to be confused with finished displaying)
* Update video memory to reflect the frame that was just processed

This is a streamlined approach: use of the CPU’s [font=Courier New]HALT[/font] instruction only happens as a necessity because video resources are in use. The frame that is processed starting with reading the game pad state is the frame after the one currently being drawn, and the frame currently being drawn is the frame after the one currently being displayed. So it has the usual double-buffered drawback: what the user sees is actually 2 video frames in the past.

In practice, the processing of the audio thread happens much the same way: schedule + [font=Courier New]HALT[/font] + update hardware. As such, I’ve got the code to do it plugged in up there with the video stuff, but exactly which type of frame to process depends on the interrupts that occurred (which use static global variables to indicate they’ve completed–my interrupt handlers are quite barren).

Audio needs to have higher priority than video. That is to say, if you had to pick which one was going to happen, you should always make sure audio is the one that happens. I personally do a lot of bench testing to establish metrics that will prevent the audio+video from running into overtime even in a worst-case scenario.

Other programmers have other approaches: I know for a fact that the GBA Pokémon games process all of the audio through interrupts, since whenever you crash the game by putting a hacked mon in the PC, the music keeps playing…

Guy Perfect wrote:
It’s far simpler to use pre-allocated static memory and use the stack by declaring struct variables in high-order functions.

Sure, unless you don’t know and can’t predict how much memory you’re going to need, or you need variable-sized structures, which is probably cr1901’s situation.

Virtual Boy has 64KB of on-board memory. If you’re considering setting up a paging system, be careful you don’t wind up using it all just trying to keep track of where it is.

Wouldn’t you need extra hardware to do paging?

My programs have two “threads”, if you will, that run side-by-side to present the user experience: there’s the audio thread, and the video/game thread.

So basically, they work the same way commercial games do.

Well, actually, Red Alarm’s gameplay runs at the same speed regardless of the frame rate (look at the final stage where it gets really low), so that’s an approach to take if you can’t guarantee that your frames will always render in the same time.

Insmouse also updates objects on the VIP interrupt, but I think all other video stuff is done in the same “thread” as gameplay.

Other programmers have other approaches: I know for a fact that the GBA Pokémon games process all of the audio through interrupts, since whenever you crash the game by putting a hacked mon in the PC, the music keeps playing…

So does Galactic Pinball. Try getting more than 100 million on UFO. 🙂

HorvatM wrote:
So does Galactic Pinball. Try getting more than 100 million on UFO. 🙂

There is no chance in hell of me getting that type of score in Galactic Pinball, so go ahead and spoil it for me… what happens?

I’ll actually contribute something productive to this thread tomorrow- I am tired and mulling over something I read in the V810 seminar slides…

Small steps…

I’m at the point where I now need to implement my first interrupt handler- even on the Warning/Focus screen, I want to play a small piece of music and make sure that input is ignored for the two seconds required by the development manual for an official game.

Additionally, I’ve read that games running on a real console aren’t supposed to initialize the screen until the VIP status says that the servo and drawing circuitry is in sync.

I think for the sake of having only one main thread, I’ll do interrupt-driven music that is based on the timer interrupt. However, I want the main game loop to be able to set timers as well, and be notified in some manner when the timer expires. So I need a small timer-manager of some sort to deal with multiple timing events.

Such code would probably look like this:

void game_mode_loop()
{
  void * timer_handle;
/* Unconditional processing goes here */

  if(condition_to_set_timerA_met) /* Condition is irrelevant. */
  {
    timer_handle = add_a_timer_source(1000); /* Set timerA_event_in_progress.
    timerA_has_elapsed will be set when the number of ticks since
    timer started == 1000. The timer interrupt will run a routine
    FOR EACH TIMER REGISTERED to compare the ticks elapsed to the 
    number of ticks desired, before setting a variable. */
  }

  if(timerA_event_in_progress)
  {
    if(timerA_has_elapsed)
    {
      give_bonus_score = 0; /* Lame example. */
      remove_timer_source(timer_handle); /* Unset timerA_event_in_progress. */
    }
  }

/* More conditional processing. */
}

The “variable-sized structures” HorvatM mentions comes into play because I don’t know how many simultaneous timer compares need to be kept track of at compile time.

In addition to monitoring the various elapsed times, the timer interrupt will unconditionally call the music driver.

Thoughts? 😀

When I get the Focus Screen/Timer Interrupt fully working, I’ll use that time to push my (admittedly complicated) source tree to a public repo.

Maybe I’m being too simplistic but if just need to count down ticks and it needs to be able to grow dynamically why not use a linked list of “timer counters”. Add nodes to the list when you start the “event” being timed, subract 1 tick from each counter in the list, when you hit 0 remove the node and handle the event. There will probably still be a limit of some sort for max timers 50, 100 and just use that max for an array of available counters. Im writing from my phone so I won’t be sending any code examples and I’ve never tried it so its just an idea/theory

Greg Stevens wrote:
Maybe I’m being too simplistic but if just need to count down ticks and it needs to be able to grow dynamically why not use a linked list of “timer counters”. Add nodes to the list when you start the “event” being timed, subract 1 tick from each counter in the list, when you hit 0 remove the node and handle the event. There will probably still be a limit of some sort for max timers 50, 100 and just use that max for an array of available counters. Im writing from my phone so I won’t be sending any code examples and I’ve never tried it so its just an idea/theory

I did exactly that, insulating the game logic code from the timer driver using an interface. I got a base driver done tonight, and it works! For those who want to see it, you can find the relevant commit and files here.

The relevant files are:

include/backend/timedriv.h
include/intrface/timerif.h
src/drivers/timedriv.c
src/gamemode/focscrn.c
src/timerif.c

All the code does right now is make sure the game loop waits two seconds using the timer driver callback before continuing. It’s a toy example that REALLY doesn’t need this sort of framework, but more complicated examples will let me update multiple objects and conditions asynchronously, especially the sound registers.

As is, the function run_inuse_comparators() pseudo-implements the Observer Pattern, hence the struct TIMER_OBSERVER in include/backend/timedriv.h. If anyone has any feedback, I’d appreciate it :).

… I REALLY wish I didn’t need to declare all those volatile void pointers though XD.

 

Write a reply

You must be logged in to reply to this topic.