; ============================================================================= ; proc.asm - process table management, task creation, fork/exec (stubs) ; ============================================================================= INCLUDE "include/gbos.inc" SECTION "proc", ROM0 ; ----------------------------------------------------------------------------- ; ProcInit - table already zeroed by ClearKernelRAM (PS_FREE=0). Seed pids. ; ----------------------------------------------------------------------------- ProcInit:: ld a, 1 ld [wNextPid], a ld a, 1 ; bank 0 is the init task's; allocate from 1 up ld [wNextRamBank], a ret ; ----------------------------------------------------------------------------- ; AllocRamBank - bump-allocate a cart-RAM bank. out: A=bank, CF set if none. ; ----------------------------------------------------------------------------- AllocRamBank:: ld a, [wNextRamBank] cp NUM_RAM_BANKS jr nc, .none ld b, a inc a ld [wNextRamBank], a ld a, b and a ; clear carry ret .none scf ret ; ----------------------------------------------------------------------------- ; FindFreeSlot - scan the proc table for a PS_FREE slot. ; out: A=slot, DE=&PCB, CF set if the table is full. ; ----------------------------------------------------------------------------- FindFreeSlot:: ld c, 0 .l ld a, c call PcbPtr ; DE = &PCB[c] ld a, [de] cp PS_FREE jr z, .ok inc c ld a, c cp MAX_PROCS jr c, .l scf ret .ok ld a, c and a ; clear carry ret ; ----------------------------------------------------------------------------- ; PcbPtr - slot -> PCB pointer. in: A=slot out: DE=&PCB. preserves BC. ; ----------------------------------------------------------------------------- PcbPtr:: push bc ld hl, wProcTable ld bc, PROC_SIZE and a jr z, .done .mul add hl, bc dec a jr nz, .mul .done ld d, h ld e, l pop bc ret ; ----------------------------------------------------------------------------- ; ProcCreate - allocate a PCB and build an initial (runnable) task. ; in: HL = task entry, B = cart-RAM bank, C = SVBK u-area bank ; out: A = pid, or 0 if the table is full ; ----------------------------------------------------------------------------- ProcCreate:: ; stash params ld a, l ld [wTmpEntry], a ld a, h ld [wTmpEntry+1], a ld a, b ld [wTmpRamB], a ld a, c ld [wTmpWramB], a ; find a free slot ld c, 0 .find ld a, c call PcbPtr ; DE = &PCB[c] ld a, [de] cp PS_FREE jr z, .found inc c ld a, c cp MAX_PROCS jr c, .find xor a ; table full ret .found ld a, c ld [wTmpSlot], a ; fill fields; HL walks the PCB ld h, d ld l, e ld a, PS_READY ld [hl+], a ; PROC_STATE ld a, [wNextPid] ld [hl+], a ; PROC_PID ld [wTmpPid], a inc a ld [wNextPid], a inc hl ; skip PROC_SP (filled below) inc hl ld a, 1 ld [hl+], a ; PROC_ROMB lo (placeholder text bank) xor a ld [hl+], a ; PROC_ROMB hi ld a, [wTmpRamB] ld [hl+], a ; PROC_RAMB ld a, [wTmpWramB] ld [hl+], a ; PROC_WRAMB xor a ld [hl+], a ; PROC_PARENT ld [hl], a ; PROC_EXIT ; build the initial stack frame in the task's RAM bank ld a, [wTmpEntry] ld l, a ld a, [wTmpEntry+1] ld h, a ; HL = entry ld a, [wTmpRamB] ld b, a ; B = RAM bank call ProcSetupStack ; out: HL = initial SP ld a, l ld [wTmpSP], a ld a, h ld [wTmpSP+1], a ; store SP into PCB.PROC_SP ld a, [wTmpSlot] call PcbPtr ; DE = &PCB ld a, e add PROC_SP ld l, a ld a, d adc 0 ld h, a ; HL = &PROC_SP ld a, [wTmpSP] ld [hl+], a ld a, [wTmpSP+1] ld [hl], a ld a, [wTmpPid] ret ; ----------------------------------------------------------------------------- ; ProcSetupStack - lay a fresh switch-in frame at the top of a task RAM bank. ; in: HL = entry, B = cart-RAM bank ; out: HL = initial SP (points at the saved-HL slot) ; The frame matches hSwitchTo's restore sequence: ; [SP+0..1]=hl [+2..3]=de [+4..5]=bc [+6]=f [+7]=a [+8..9]=entry (ret target) ; ----------------------------------------------------------------------------- ProcSetupStack:: ld a, b ld [MBC5_RAMB], a ; map task's RAM bank into $A000-$BFFF push hl ; save entry ld hl, TASK_STACK_TOP - 9 ; frame base = $BFF6 xor a ld [hl+], a ; hl_lo ld [hl+], a ; hl_hi ld [hl+], a ; de_lo ld [hl+], a ; de_hi ld [hl+], a ; bc_lo ld [hl+], a ; bc_hi ld [hl+], a ; f ld [hl+], a ; a pop de ; DE = entry ld a, e ld [hl+], a ; entry lo (ret target) ld a, d ld [hl], a ; entry hi ld hl, TASK_STACK_TOP - 9 ; initial SP ret ; ----------------------------------------------------------------------------- ; sys_fork / sys_exec - TODO. Plan: ; fork: alloc slot, copy parent's 8 KiB RAM bank -> child bank, dup u-area, ; patch child's saved-A to 0 (child return), parent gets child pid. ; exec: point PROC_ROMB at a flashed program image, reset RAM-bank layout ; (copy .data from ROM, zero .bss, fresh heap/stack), longjmp in. ; ----------------------------------------------------------------------------- ; ============================================================================= ; sys_fork() - duplicate the current process. ; out: A = child pid in the parent, 0 in the child, $FF on failure. ; ; Runs on the parent's user stack at entry (SP = Uafter, pointing at the rst ; return address). We switch to a kernel stack in WRAM0 so we're free to swap ; the $A000 cart-RAM window (the parent's user stack lives there), copy the ; parent's 8 KiB bank into a freshly allocated child bank via a WRAM bounce ; buffer, then plant a switch-in frame in the child so it "returns from fork" ; with A=0 to the same user PC. ; ============================================================================= sys_fork:: ; capture parent user SP (= Uafter) ld hl, sp+0 ld a, l ld [wForkUserSP], a ld a, h ld [wForkUserSP+1], a ld sp, KSTACK_TOP2 ; move off the cart-RAM stack call FindFreeSlot ; A=slot, DE=&child PCB jp c, .fail ld [wForkChildSlot], a call AllocRamBank ; A=child cart-RAM bank jp c, .fail ld [wForkChildBank], a ; remember the parent's bank so we can restore the window afterwards ld a, [wCurProc] add PROC_RAMB ld l, a ld a, [wCurProc+1] adc 0 ld h, a ld a, [hl] ld [wForkParentBank], a ; ---- copy parent bank -> child bank, 32 pages of 256 bytes ---- ld c, 0 ; page index 0..31 .page ld a, [wForkParentBank] ld [MBC5_RAMB], a ; map parent bank ld a, $A0 add c ld h, a ld l, 0 ; HL = $A000 + c*256 ld de, wCopyBuf ld b, 0 ; 256 iterations (b wraps 0->255..->0) .rd ld a, [hl+] ld [de], a inc de dec b jr nz, .rd ld a, [wForkChildBank] ld [MBC5_RAMB], a ; map child bank ld a, $A0 add c ld h, a ld l, 0 ld de, wCopyBuf ld b, 0 .wr ld a, [de] ld [hl+], a inc de dec b jr nz, .wr inc c ld a, c cp 32 jr c, .page ; child bank is currently mapped ; ---- plant the child's switch-in frame at Uafter-8 (in child bank) ---- ; hSwitchTo restore pops hl,de,bc,af then rets; we zero those 8 bytes and ; leave the copied return address (retU) at Uafter untouched, so the child ; resumes at the post-fork PC with A=0. ld a, [wForkUserSP] sub 8 ld l, a ld a, [wForkUserSP+1] sbc 0 ld h, a ; HL = Uafter-8 xor a ld b, 8 .zframe ld [hl+], a dec b jr nz, .zframe ; restore the parent's bank into the window (parent stack valid again) ld a, [wForkParentBank] ld [MBC5_RAMB], a ; ---- fill the child PCB ---- ld a, [wForkChildSlot] call PcbPtr ; DE = &child PCB ld h, d ld l, e ; HL walks child PCB ld a, PS_READY ld [hl+], a ; STATE ld a, [wNextPid] ld [hl+], a ; PID ld [wForkChildPid], a inc a ld [wNextPid], a ld a, [wForkUserSP] ; PROC_SP = Uafter-8 sub 8 ld [hl+], a ld a, [wForkUserSP+1] sbc 0 ld [hl+], a ; PROC_ROMB = parent ROMB (shared read-only text) ld a, [wCurProc] add PROC_ROMB ld e, a ld a, [wCurProc+1] adc 0 ld d, a ; DE = &parent.PROC_ROMB ld a, [de] ld [hl+], a inc de ld a, [de] ld [hl+], a ; PROC_RAMB = child bank ld a, [wForkChildBank] ld [hl+], a ; PROC_WRAMB = parent WRAMB (u-area shared for now) ld a, [wCurProc] add PROC_WRAMB ld e, a ld a, [wCurProc+1] adc 0 ld d, a ld a, [de] ld [hl+], a ; PROC_PARENT = parent pid ld a, [wCurProc] add PROC_PID ld e, a ld a, [wCurProc+1] adc 0 ld d, a ld a, [de] ld [hl+], a ; PROC_EXIT = 0 xor a ld [hl], a ; ---- return to the parent with child pid ---- ld a, [wForkUserSP] ld l, a ld a, [wForkUserSP+1] ld h, a ld sp, hl ; back on the parent's user stack ld a, [wForkChildPid] ret .fail ; restore parent user stack and report failure ld a, [wForkUserSP] ld l, a ld a, [wForkUserSP+1] ld h, a ld sp, hl ld a, $FF ret