diff options
| author | user <user@clank> | 2026-07-15 23:54:13 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-15 23:54:13 +0200 |
| commit | ab3ebbe227c72b745a46f9233fbfbb27477586b8 (patch) | |
| tree | d71f87605d17c5653f34ebcdef3941f898cd5b91 /src/proc.asm | |
| parent | gbos scaffold: working cooperative microkernel (verified ABAB in emulator) (diff) | |
| download | gbos-ab3ebbe227c72b745a46f9233fbfbb27477586b8.tar.gz gbos-ab3ebbe227c72b745a46f9233fbfbb27477586b8.tar.xz gbos-ab3ebbe227c72b745a46f9233fbfbb27477586b8.zip | |
kernel: implement fork()
- sys_fork: switch to a kernel stack, bump-allocate a cart-RAM bank, copy the
parent's 8 KiB bank via a WRAM bounce buffer, plant a switch-in frame so the
child returns 0 to the post-fork PC, fill the child PCB (shared ROM text)
- add AllocRamBank (bump allocator) + FindFreeSlot helpers
- demo: init forks; parent prints P, child prints C (PCPC...); nested fork
gives three distinct pids (123...)
- README: document the fork mechanism, mark syscall status
Diffstat (limited to 'src/proc.asm')
| -rw-r--r-- | src/proc.asm | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/src/proc.asm b/src/proc.asm index 762b30e..058881f 100644 --- a/src/proc.asm +++ b/src/proc.asm @@ -11,6 +11,48 @@ SECTION "proc", ROM0 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 ; ----------------------------------------------------------------------------- @@ -157,3 +199,172 @@ ProcSetupStack:: ; 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 |
