; ============================================================================= ; 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_fork ; 1 FORK 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