Skip to content

Vbi

Vbi installs and removes Vertical-Blank-Interrupt handlers. The xt6502 fires a VBI about 50 times a second on PAL and 60 on NTSC. The OS dispatches first to an immediate vector for time-critical work, then to a deferred vector after its own housekeeping.

#import <Vbi.xc> // or the Foundation umbrella

Vbi is a static utility class: call every method on the class (Vbi.addDeferred(&fn)). The init is class boilerplate you never use. Install and remove always go through SETVBV ($E45C), so the OS performs an SEI-safe atomic write to the vector pair. Without it, a VBI could fire between the lo and hi byte writes and jump through a half-updated pointer.

The OS holds two vectors and runs them in order during every vertical blank:

VectorAddressDefaultUse for
Immediate (VVBLKI)$0222SYSVBV ($E45F)display-list updates, scroll registers — anything that must be in place before ANTIC starts the next frame
Deferred (VVBLKD)$0224XITVBV ($E462)counters, music, slow state machines — everything else

After the deferred handler returns, the OS exits the interrupt via XITVBV.

Your handler must be declared with the :vbi function annotation. This tells the codegen to save A / X / Y in the prologue, restore them in the epilogue, and replace the usual RTS with a JMP XITVBV so the OS finishes the interrupt cleanly. A plain function would corrupt registers in the interrupted code and either lock up the machine or skip the rest of the OS chain. On the banked xt target, :vbi (and :irq) handlers are placed automatically in unbanked RAM at a stable address, because the OS dispatcher jumps through the vector slot with no chance for the bank-switch trampoline to page the handler in. You do not annotate the handler :main yourself.

volatile u8* COLBK = (u8*)$D01A; // background colour register
void rainbow(void) :vbi
{
*COLBK = *COLBK + (u8)1; // change border colour every frame
}
void main(void)
{
Vbi.addDeferred(&rainbow);
while (1) { /* spin */ }
}

Installing · addImmediate · addDeferred

Removing · removeImmediate · removeDeferred


Choose immediate when the work has tight timing (display-list updates and scroll registers, which must run before ANTIC starts the next frame) and deferred for everything else (counters, music, AI ticks). Without a clear reason to use immediate, prefer deferred, which keeps your handler out of the critical path of the OS’s own vector handling.

static void addImmediate(pointer fn)

Installs fn at the immediate vector VVBLKI ($0222) via SETVBV with A=6. fn must be a :vbi-annotated handler.

static void addDeferred(pointer fn)

Installs fn at the deferred vector VVBLKD ($0224) via SETVBV with A=7. fn must be a :vbi-annotated handler.

↑ Topics

static void removeImmediate(void)

Restores VVBLKI to its OS default SYSVBV ($E45F). This is the teardown for addImmediate.

static void removeDeferred(void)

Restores VVBLKD to its OS default XITVBV ($E462). This is the teardown for addDeferred.

↑ Topics