aboutsummaryrefslogtreecommitdiffstats
path: root/src/proc.asm
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-15 23:54:13 +0200
committeruser <user@clank>2026-07-15 23:54:13 +0200
commitab3ebbe227c72b745a46f9233fbfbb27477586b8 (patch)
treed71f87605d17c5653f34ebcdef3941f898cd5b91 /src/proc.asm
parentgbos scaffold: working cooperative microkernel (verified ABAB in emulator) (diff)
downloadgbos-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 '')
-rw-r--r--src/proc.asm211
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