; ============================================================================= ; hram.asm - the trampoline. ; ; The context switch writes SVBK ($D000-$DFFF) and the MBC5 RAM bank ; ($A000-$BFFF). Because a task's stack lives in the RAM window, the code ; performing the swap MUST NOT execute from any bank it is about to change and ; MUST NOT touch the stack across the swap. HRAM is never banked, so the ; switch lives here. We assemble it with HRAM addresses (LOAD) but store the ; bytes in ROM0 and copy them up at boot. ; ; Convention: to compute &(PCB + OFF) we do ; ld a,e : add OFF : ld l,a : ld a,d : adc 0 : ld h,a ; HL = DE + OFF ; ============================================================================= INCLUDE "include/gbos.inc" SECTION "hram_vars", HRAM hCurBankRAM: DS 1 ; shadow of active cart-RAM bank hCurBankWRAM: DS 1 ; shadow of active SVBK bank hSwitchTgt: DS 2 ; &incoming PCB (set before the pushes clobber DE) hSCY:: DS 1 ; VBlank-latched SCY: term_view_update writes this, ; the VBlank ISR applies it - a mid-frame SCY write ; would shear the picture (SCY is sampled per ; scanline), so ring-scrolls land at frame boundaries ; ----------------------------------------------------------------------------- ; Source image (lives in ROM0); CopyHramCode blits it to HRAM at boot. ; ----------------------------------------------------------------------------- SECTION "hram_src", ROM0 HramSrc: LOAD "hram_code", HRAM ; --------------------------------------------------------------------------- ; hSwitchTo: switch from the current task (wCurProc) to the PCB in DE. ; in: DE = &incoming PCB ; Runs from HRAM. Saves/restores full context. Clobbers everything. ; --------------------------------------------------------------------------- hSwitchTo:: di ; no tick ISR while SP and the banks under it ; are out of sync (pushes would hit the wrong ; bank); ei below once the incoming stack is up ; stash target while DE is still valid ld a, e ld [hSwitchTgt], a ld a, d ld [hSwitchTgt+1], a ; --- save outgoing context on its (currently-mapped) stack --- push af push bc push de push hl ; SP -> outgoing PCB.PROC_SP (outgoing = wCurProc, in WRAM0, always mapped) ld hl, sp+0 ; HL = SP ld b, h ld c, l ; BC = SP value ld a, [wCurProc] add PROC_SP ld l, a ld a, [wCurProc+1] adc 0 ld h, a ; HL = &outgoing.PROC_SP ld a, c ld [hl+], a ld a, b ld [hl], a ; --- incoming becomes current --- ld a, [hSwitchTgt] ld e, a ld [wCurProc], a ld a, [hSwitchTgt+1] ld d, a ; DE = &incoming PCB ld [wCurProc+1], a ; --- program banks for incoming task (order: RAM, WRAM, ROM) --- ; cart-RAM bank ld a, e add PROC_RAMB ld l, a ld a, d adc 0 ld h, a ld a, [hl] ld [hCurBankRAM], a ld [MBC5_RAMB], a ; u-area WRAM bank via SVBK ld a, e add PROC_WRAMB ld l, a ld a, d adc 0 ld h, a ld a, [hl] ld [hCurBankWRAM], a ld [rSVBK], a ; text ROM bank (16-bit) ld a, e add PROC_ROMB ld l, a ld a, d adc 0 ld h, a ld a, [hl+] ld [MBC5_ROMB_LO], a ld a, [hl] ld [MBC5_ROMB_HI], a ; --- restore incoming SP (its banks are now mapped) --- ld a, e add PROC_SP ld l, a ld a, d adc 0 ld h, a ld a, [hl+] ld c, a ld a, [hl] ld h, a ld l, c ld sp, hl ; --- pop incoming context and resume it --- ei ; SP + banks consistent again (takes effect ; after the next instruction) pop hl pop de pop bc pop af ret ENDL HramSrcEnd: ; ----------------------------------------------------------------------------- ; CopyHramCode - blit the LOAD'd image up to HRAM. Runs once at boot. ; ----------------------------------------------------------------------------- SECTION "hram_copy", ROM0 CopyHramCode:: ld hl, HramSrc ld de, hSwitchTo ld bc, HramSrcEnd - HramSrc .loop ld a, [hl+] ld [de], a inc de dec bc ld a, b or c jr nz, .loop ret