aboutsummaryrefslogtreecommitdiffstats
path: root/src/syscall.asm
diff options
context:
space:
mode:
Diffstat (limited to 'src/syscall.asm')
-rw-r--r--src/syscall.asm117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/syscall.asm b/src/syscall.asm
new file mode 100644
index 0000000..71e1f2b
--- /dev/null
+++ b/src/syscall.asm
@@ -0,0 +1,117 @@
+; =============================================================================
+; syscall.asm - the trap entry, dispatch table, and handlers.
+;
+; ABI: user loads C = syscall number, args in DE/B/HL, then `rst $30`.
+; return value in A. ($30 vector jp's here - see boot.asm)
+; =============================================================================
+INCLUDE "include/gbos.inc"
+
+SECTION "syscall", ROM0
+
+; -----------------------------------------------------------------------------
+SyscallTrap::
+ ld a, c
+ cp SYS_MAX
+ jr nc, .bad
+ ; index the table WITHOUT touching DE/B (those are syscall args).
+ ld hl, SyscallTable
+ ld a, c
+ add a ; A = C*2 (word entries; C < SYS_MAX)
+ add l
+ ld l, a
+ jr nc, .nocarry
+ inc h
+.nocarry
+ ld a, [hl+]
+ ld h, [hl]
+ ld l, a
+ jp hl ; tail-call; handler's `ret` returns to user
+.bad
+ ld a, $FF ; ENOSYS-ish
+ ret
+
+; -----------------------------------------------------------------------------
+; Dispatch table (indexed by syscall number)
+; -----------------------------------------------------------------------------
+SyscallTable:
+ dw sys_exit ; 0
+ dw sys_nosys ; 1 FORK (TODO)
+ dw sys_nosys ; 2 READ (TODO)
+ dw sys_write ; 3 WRITE
+ dw sys_nosys ; 4 OPEN (TODO)
+ dw sys_nosys ; 5 CLOSE (TODO)
+ dw sys_nosys ; 6 EXEC (TODO)
+ dw sys_nosys ; 7 WAIT (TODO)
+ dw sys_getpid ; 8 GETPID
+ dw sys_nosys ; 9 KILL (TODO)
+ dw sys_nosys ; 10 BRK (TODO)
+ dw sys_yield ; 11 YIELD
+
+; -----------------------------------------------------------------------------
+sys_nosys:
+ ld a, $FF
+ ret
+
+; -----------------------------------------------------------------------------
+; sys_write(fd=B, buf=DE, len=B?) -- scaffold: fd ignored, DE=buf, B=len.
+; Writes to the serial console. Returns A = bytes written.
+; -----------------------------------------------------------------------------
+sys_write:
+ ld a, b
+ or a
+ ret z ; len 0
+ ld c, b ; C = remaining
+.loop
+ ld a, [de]
+ call PutChar
+ inc de
+ dec c
+ jr nz, .loop
+ ld a, b ; return len
+ ret
+
+; -----------------------------------------------------------------------------
+; sys_getpid() -> A = current pid
+; -----------------------------------------------------------------------------
+sys_getpid:
+ ld a, [wCurProc]
+ add PROC_PID
+ ld l, a
+ ld a, [wCurProc+1]
+ adc 0
+ ld h, a
+ ld a, [hl]
+ ret
+
+; -----------------------------------------------------------------------------
+; sys_yield()
+; -----------------------------------------------------------------------------
+sys_yield:
+ call SchedYield
+ ret
+
+; -----------------------------------------------------------------------------
+; sys_exit(code=B) -- mark zombie and schedule away forever.
+; -----------------------------------------------------------------------------
+sys_exit:
+ ; mark current PCB as zombie, store exit code
+ ld a, [wCurProc]
+ ld l, a
+ ld a, [wCurProc+1]
+ ld h, a ; HL = &PCB
+ ld a, PS_ZOMBIE
+ ld [hl], a ; PROC_STATE
+ ; PROC_EXIT is at offset PROC_EXIT
+ ld a, [wCurProc]
+ add PROC_EXIT
+ ld l, a
+ ld a, [wCurProc+1]
+ adc 0
+ ld h, a
+ ld a, b
+ ld [hl], a ; exit code
+ ; never return to this task
+ call SchedYield
+ ; if we somehow come back (no other ready task), spin
+.halt
+ jr .halt