diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/boot.asm | 12 | ||||
| -rw-r--r-- | src/kdata.asm | 11 | ||||
| -rw-r--r-- | src/proc.asm | 211 | ||||
| -rw-r--r-- | src/syscall.asm | 2 | ||||
| -rw-r--r-- | src/tasks.asm | 40 |
5 files changed, 249 insertions, 27 deletions
diff --git a/src/boot.asm b/src/boot.asm index 513c392..73ea860 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -50,18 +50,12 @@ KernelInit: call CopyHramCode ; move context-switch trampoline into HRAM call ProcInit ; wipe process table - ; --- spawn the two demo tasks (text-in-ROM model) --- - ; task A: entry TaskA, RAM bank 0, u-area WRAM bank 1 - ld hl, TaskA - ld b, 0 ; RAM bank + ; --- spawn the init task (pid 1); it fork()s the rest --- + ld hl, TaskInit + ld b, 0 ; cart-RAM bank 0 ld c, 1 ; WRAM u-area bank call ProcCreate - ld hl, TaskB - ld b, 1 - ld c, 2 - call ProcCreate - call SchedInit ; pick first task, set wCurProc ; interrupts: enable timer for preemption diff --git a/src/kdata.asm b/src/kdata.asm index efc780a..ba395f1 100644 --- a/src/kdata.asm +++ b/src/kdata.asm @@ -20,3 +20,14 @@ wTmpWramB:: DS 1 wTmpSlot:: DS 1 wTmpPid:: DS 1 wTmpSP:: DS 2 + +; cart-RAM bank allocator (bump; bank 0 taken by the init task) +wNextRamBank:: DS 1 + +; scratch used by sys_fork +wForkUserSP:: DS 2 ; parent user SP at fork entry (= Uafter) +wForkChildSlot:: DS 1 +wForkChildPid:: DS 1 +wForkParentBank::DS 1 +wForkChildBank:: DS 1 +wCopyBuf:: DS 256 ; bank-copy bounce buffer (WRAM0, always mapped) 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 diff --git a/src/syscall.asm b/src/syscall.asm index 71e1f2b..ca2857c 100644 --- a/src/syscall.asm +++ b/src/syscall.asm @@ -35,7 +35,7 @@ SyscallTrap:: ; ----------------------------------------------------------------------------- SyscallTable: dw sys_exit ; 0 - dw sys_nosys ; 1 FORK (TODO) + dw sys_fork ; 1 FORK dw sys_nosys ; 2 READ (TODO) dw sys_write ; 3 WRITE dw sys_nosys ; 4 OPEN (TODO) diff --git a/src/tasks.asm b/src/tasks.asm index 1f84142..459ccb6 100644 --- a/src/tasks.asm +++ b/src/tasks.asm @@ -1,33 +1,39 @@ ; ============================================================================= -; tasks.asm - two demo userland tasks (text-in-ROM model). -; Each loops: write its letter to the console, then yield. This exercises the -; whole pipeline: syscall trap, console, and the HRAM context switch swapping -; RAM banks / SVBK between tasks. +; tasks.asm - the init task demonstrates fork(). +; init forks once; the parent loops printing 'P', the child loops printing 'C'. +; Both run the same ROM text, diverging on fork()'s return value (0 in child). +; Exercises: syscall trap, fork (8 KiB bank copy + child frame), scheduler, +; and the HRAM context switch swapping cart-RAM banks per task. ; ============================================================================= INCLUDE "include/gbos.inc" SECTION "tasks", ROM0 -TaskA:: -.loop - ld de, .msg - ld b, 1 ; len +TaskInit:: + ld c, SYS_FORK + rst $30 ; A = child pid (parent) or 0 (child) + or a + jr z, .child + +.parent + ld de, .pmsg + ld b, 1 ld c, SYS_WRITE rst $30 ld c, SYS_YIELD rst $30 - jr .loop -.msg - db "A" + jr .parent -TaskB:: -.loop - ld de, .msg +.child + ld de, .cmsg ld b, 1 ld c, SYS_WRITE rst $30 ld c, SYS_YIELD rst $30 - jr .loop -.msg - db "B" + jr .child + +.pmsg + db "P" +.cmsg + db "C" |
