aboutsummaryrefslogtreecommitdiffstats
path: root/src/syscall.asm
blob: fd1b213a88ea3a41e5b164778339a0e1c90b33e5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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_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_exec                 ; 6  EXEC
    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