aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-16 11:10:45 +0200
committeruser <user@clank>2026-07-16 11:10:45 +0200
commit2d3597c3a02f821bb364b8ecc23fc5949fca738d (patch)
tree714cc7463eb36306f44fcfd67fcf52de0c13b3d1
parentshell: I/O redirection (>/<) and pipes (|); rewrite shell in C (diff)
downloadgbos-2d3597c3a02f821bb364b8ecc23fc5949fca738d.tar.gz
gbos-2d3597c3a02f821bb364b8ecc23fc5949fca738d.tar.xz
gbos-2d3597c3a02f821bb364b8ecc23fc5949fca738d.zip
kill + ps: implement SYS_KILL (pid 1 immortal) and a ps tool
- PCB gains PROC_PROG (running program id), set by exec, inherited on fork. - sys_kill(pid): reject pid 1 (immortal) and unknown/dead pids; mark the target zombie, reparent its children to init, wake its parent if blocked in wait; schedule away if a process kills itself. Reuses FindPcbByPid/ReparentToInit. - sys_ps(slot,buf)->{pid,state,prog}; sys_progname(id) via NameTable reverse lookup. NameTable gains sh/ps/kill. - libc: kill/psget/progname; tools ps (pid state cmd) and kill <pid>. - verified: ps lists sh(B)+ps(R); kill 1 refused; kill 42 fails.
-rw-r--r--Makefile2
-rw-r--r--c/gbos.h3
-rw-r--r--c/kill.c10
-rw-r--r--c/libc.s20
-rw-r--r--c/ps.c14
-rw-r--r--include/gbos.inc11
-rw-r--r--src/kdata.asm3
-rw-r--r--src/proc.asm18
-rw-r--r--src/programs.asm16
-rw-r--r--src/syscall.asm168
10 files changed, 260 insertions, 5 deletions
diff --git a/Makefile b/Makefile
index 9b871fa..e2a86eb 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ $(BUILD)/%.o: src/%.asm | $(BUILD)
# C programs: compiled with SDCC into ROM-bank blobs and INCBIN'd by programs.asm
CBLOBS := c/chello.bin c/echo.bin c/true.bin c/false.bin c/uname.bin \
c/pid.bin c/cat.bin c/wc.bin c/head.bin c/args.bin \
- c/ls.bin c/save.bin c/rm.bin c/sh.bin
+ c/ls.bin c/save.bin c/rm.bin c/sh.bin c/ps.bin c/kill.bin
$(BUILD)/programs.o: $(CBLOBS)
# C programs also depend on the shared libc sources
diff --git a/c/gbos.h b/c/gbos.h
index fa926fd..5f22a5a 100644
--- a/c/gbos.h
+++ b/c/gbos.h
@@ -38,4 +38,7 @@ unsigned char wait(void);
void setin(unsigned char fd); /* redirect stdin ($FF=console) */
void setout(unsigned char fd); /* redirect stdout ($FF=console) */
unsigned char lookup(const char *name); /* command name -> program id */
+unsigned char kill(unsigned char pid); /* terminate a pid (1 is immortal)*/
+unsigned char psget(unsigned char slot, unsigned char *buf); /* {pid,state,prog}*/
+void progname(unsigned char id, char *buf); /* id -> name */
#endif
diff --git a/c/kill.c b/c/kill.c
new file mode 100644
index 0000000..e524627
--- /dev/null
+++ b/c/kill.c
@@ -0,0 +1,10 @@
+#include "gbos.h"
+/* kill <pid>: terminate a process (pid 1 is immortal). */
+void main(void) {
+ char *argv[4];
+ unsigned char argc = argv_parse(argv, 4);
+ unsigned char pid;
+ if (argc < 1) { puts("usage: kill <pid>"); nl(); return; }
+ pid = atou(argv[0]);
+ if (kill(pid)) { puts("kill: no such pid (or immortal)"); nl(); }
+}
diff --git a/c/libc.s b/c/libc.s
index 857a16d..bcc8199 100644
--- a/c/libc.s
+++ b/c/libc.s
@@ -167,3 +167,23 @@ _lookup:
ld c, #19 ; SYS_LOOKUP
rst #0x30
ret
+
+ .globl _kill, _psget, _progname
+ ;; unsigned char kill(unsigned char pid /*A*/) -> A (0 ok, $FF fail)
+_kill:
+ ld b, a
+ ld c, #9 ; SYS_KILL
+ rst #0x30
+ ret
+ ;; unsigned char psget(unsigned char slot /*A*/, unsigned char *buf /*DE*/) -> A
+_psget:
+ ld b, a
+ ld c, #20 ; SYS_PS
+ rst #0x30
+ ret
+ ;; void progname(unsigned char id /*A*/, char *buf /*DE*/)
+_progname:
+ ld b, a
+ ld c, #21 ; SYS_PROGNAME
+ rst #0x30
+ ret
diff --git a/c/ps.c b/c/ps.c
new file mode 100644
index 0000000..7bfc0de
--- /dev/null
+++ b/c/ps.c
@@ -0,0 +1,14 @@
+#include "gbos.h"
+/* ps: list live processes (pid, state, command). */
+void main(void) {
+ unsigned char i, buf[3];
+ char name[9];
+ for (i = 0; i < 8; i++) {
+ if (psget(i, buf)) { /* buf = {pid, state, prog} */
+ putu(buf[0]); putc(' ');
+ putc("-RrBZ"[buf[1]]); putc(' ');
+ progname(buf[2], name);
+ puts(name); nl();
+ }
+ }
+}
diff --git a/include/gbos.inc b/include/gbos.inc
index 6aaaf73..30c3a50 100644
--- a/include/gbos.inc
+++ b/include/gbos.inc
@@ -69,8 +69,11 @@ DEF PROC_PARENT RB 1
DEF PROC_EXIT RB 1 ; exit code (valid when PS_ZOMBIE)
DEF PROC_STDIN RB 1 ; $FF = console, else an open-file fd
DEF PROC_STDOUT RB 1 ; $FF = console, else an open-file fd
+DEF PROC_PROG RB 1 ; program id currently running ($FF = none), for ps
DEF PROC_SIZE RB 0
+DEF KILL_CODE EQU 137 ; exit status of a killed process (128 + SIGKILL)
+
DEF STD_CONSOLE EQU $FF
; -----------------------------------------------------------------------------
@@ -96,7 +99,10 @@ DEF SYS_PUTC EQU 16 ; write a byte to stdout (E=byte)
DEF SYS_SETIN EQU 17 ; set current stdin fd (B=fd, $FF=console)
DEF SYS_SETOUT EQU 18 ; set current stdout fd (B=fd, $FF=console)
DEF SYS_LOOKUP EQU 19 ; program id for a command name (DE=name -> A=id/$FF)
-DEF SYS_MAX EQU 20
+DEF SYS_PS EQU 20 ; process table entry (B=slot, DE=buf3 -> A=1/0)
+DEF SYS_PROGNAME EQU 21 ; program id -> name (B=id, DE=namebuf)
+DEF SYS_MAX EQU 22
+; SYS_KILL (9) is now implemented (B=pid -> A=0/$FF; pid 1 is immortal)
; (SYS_OPEN=4 / SYS_CLOSE=5 are now implemented by the filesystem)
; -----------------------------------------------------------------------------
@@ -138,5 +144,8 @@ DEF PROG_ARGS EQU 12
DEF PROG_LS EQU 13
DEF PROG_SAVE EQU 14
DEF PROG_RM EQU 15
+DEF PROG_PS EQU 16
+DEF PROG_KILL EQU 17
+DEF PROG_SPIN EQU 18
ENDC
diff --git a/src/kdata.asm b/src/kdata.asm
index ea18756..b299021 100644
--- a/src/kdata.asm
+++ b/src/kdata.asm
@@ -46,3 +46,6 @@ wFsSlot:: DS 1
wFsByte:: DS 1
wFsOFPtr:: DS 2
wKChar:: DS 1 ; scratch for KPutc
+wKillParent::DS 1 ; scratch for sys_kill
+wPsBuf:: DS 2 ; scratch for sys_ps
+wPnStart:: DS 2 ; scratch for sys_progname
diff --git a/src/proc.asm b/src/proc.asm
index ab1c807..cd79160 100644
--- a/src/proc.asm
+++ b/src/proc.asm
@@ -224,7 +224,9 @@ ProcCreate::
ld [hl+], a ; PROC_EXIT
ld a, STD_CONSOLE
ld [hl+], a ; PROC_STDIN = console
- ld [hl], a ; PROC_STDOUT = console
+ ld [hl+], a ; PROC_STDOUT = console
+ ld a, $FF
+ ld [hl], a ; PROC_PROG = none
; build the initial stack frame in the task's RAM bank
ld a, [wTmpEntry]
@@ -453,7 +455,10 @@ sys_fork::
ld [hl+], a ; child STDIN = parent STDIN
inc de
ld a, [de]
- ld [hl], a ; child STDOUT = parent STDOUT
+ ld [hl+], a ; child STDOUT = parent STDOUT
+ inc de
+ ld a, [de]
+ ld [hl], a ; child PROC_PROG = parent PROC_PROG
; ---- return to the parent with child pid ----
ld a, [wForkUserSP]
@@ -484,6 +489,15 @@ sys_fork::
; our demo programs keep no pre-initialized RAM.
; =============================================================================
sys_exec::
+ ; record the running program id in the PCB (for ps)
+ ld a, [wCurProc]
+ add PROC_PROG
+ ld l, a
+ ld a, [wCurProc+1]
+ adc 0
+ ld h, a
+ ld a, b
+ ld [hl], a
; HL = &ProgramTable[B] = ProgramTable + B*4
ld hl, ProgramTable
ld a, b
diff --git a/src/programs.asm b/src/programs.asm
index 1a7092e..f21d40b 100644
--- a/src/programs.asm
+++ b/src/programs.asm
@@ -88,6 +88,12 @@ ProgSave:
SECTION "prog_rm", ROMX[$4000], BANK[17]
ProgRm:
INCBIN "c/rm.bin"
+SECTION "prog_ps", ROMX[$4000], BANK[18]
+ProgPs:
+ INCBIN "c/ps.bin"
+SECTION "prog_kill", ROMX[$4000], BANK[19]
+ProgKill:
+ INCBIN "c/kill.bin"
; -----------------------------------------------------------------------------
; PROG_SH (bank 3) - the shell, now written in C (c/sh.c): parses >/</| and
@@ -162,6 +168,10 @@ ProgramTable::
dw ProgSave
db LOW(BANK(ProgRm)), HIGH(BANK(ProgRm))
dw ProgRm
+ db LOW(BANK(ProgPs)), HIGH(BANK(ProgPs))
+ dw ProgPs
+ db LOW(BANK(ProgKill)), HIGH(BANK(ProgKill))
+ dw ProgKill
; -----------------------------------------------------------------------------
; NameTable (ROM0) - command name -> program id, searched by sys_lookup.
@@ -198,4 +208,10 @@ NameTable::
db PROG_SAVE
db "rm", 0
db PROG_RM
+ db "sh", 0
+ db PROG_SH
+ db "ps", 0
+ db PROG_PS
+ db "kill", 0
+ db PROG_KILL
db 0
diff --git a/src/syscall.asm b/src/syscall.asm
index f149a76..0b47c05 100644
--- a/src/syscall.asm
+++ b/src/syscall.asm
@@ -43,7 +43,7 @@ SyscallTable:
dw sys_exec ; 6 EXEC
dw sys_wait ; 7 WAIT
dw sys_getpid ; 8 GETPID
- dw sys_nosys ; 9 KILL (TODO)
+ dw sys_kill ; 9 KILL
dw sys_nosys ; 10 BRK (TODO)
dw sys_yield ; 11 YIELD
dw sys_getb ; 12 GETB
@@ -54,6 +54,8 @@ SyscallTable:
dw sys_setin ; 17 SETIN
dw sys_setout ; 18 SETOUT
dw sys_lookup ; 19 LOOKUP
+ dw sys_ps ; 20 PS
+ dw sys_progname ; 21 PROGNAME
; -----------------------------------------------------------------------------
sys_nosys:
@@ -388,3 +390,167 @@ sys_wait:
.nochild
ld a, $FF
ret
+
+; =============================================================================
+; sys_kill(B = pid) -> A = 0 ok / $FF fail. pid 1 (init/shell) is immortal.
+; Terminates the target: mark zombie, reparent its children to init, wake its
+; parent if blocked in wait. If we killed ourselves, schedule away forever.
+; =============================================================================
+sys_kill::
+ ld a, b
+ cp INIT_PID
+ jr z, .fail ; init is immortal
+ call FindPcbByPid ; A=pid -> DE=&PCB, CF if not found (B preserved=pid)
+ jr c, .fail
+ ld a, [de] ; state
+ cp PS_FREE
+ jr z, .fail
+ cp PS_ZOMBIE
+ jr z, .fail ; already dead
+ ; mark ZOMBIE
+ ld a, PS_ZOMBIE
+ ld [de], a
+ ; PROC_EXIT = KILL_CODE
+ ld a, e
+ add PROC_EXIT
+ ld l, a
+ ld a, d
+ adc 0
+ ld h, a
+ ld a, KILL_CODE
+ ld [hl], a
+ ; victim's parent pid
+ ld a, e
+ add PROC_PARENT
+ ld l, a
+ ld a, d
+ adc 0
+ ld h, a
+ ld a, [hl]
+ ld [wKillParent], a
+ ; reparent victim's children to init (B still = victim pid)
+ ld a, b
+ ld [wExitMyPid], a
+ call ReparentToInit
+ ; wake victim's parent if it is blocked in wait()
+ ld a, [wKillParent]
+ call FindPcbByPid
+ jr c, .maybe_self
+ ld a, [de]
+ cp PS_BLOCKED
+ jr nz, .maybe_self
+ ld a, PS_READY
+ ld [de], a
+.maybe_self
+ ; if the victim is the current process, don't return to it
+ ld a, [wCurProc]
+ add PROC_PID
+ ld l, a
+ ld a, [wCurProc+1]
+ adc 0
+ ld h, a
+ ld a, [wExitMyPid]
+ cp [hl]
+ jr nz, .ok
+.dead
+ call SchedYield
+ jr .dead
+.ok
+ xor a
+ ret
+.fail
+ ld a, $FF
+ ret
+
+; =============================================================================
+; sys_ps(B = slot, DE = buf[3]) -> A = 1 if used (buf = pid,state,prog), else 0
+; =============================================================================
+sys_ps::
+ ld a, e
+ ld [wPsBuf], a
+ ld a, d
+ ld [wPsBuf+1], a
+ ld a, b
+ cp MAX_PROCS
+ jr nc, .no
+ call PcbPtr ; DE = &PCB[slot]
+ ld a, [de] ; state
+ cp PS_FREE
+ jr z, .no
+ ld a, [wPsBuf]
+ ld l, a
+ ld a, [wPsBuf+1]
+ ld h, a ; HL = buf
+ push de ; PCB
+ ld a, e
+ add PROC_PID
+ ld e, a
+ ld a, d
+ adc 0
+ ld d, a
+ ld a, [de] ; pid
+ ld [hl+], a
+ pop de
+ push de
+ ld a, [de] ; state
+ ld [hl+], a
+ pop de
+ ld a, e
+ add PROC_PROG
+ ld e, a
+ ld a, d
+ adc 0
+ ld d, a
+ ld a, [de] ; prog id
+ ld [hl], a
+ ld a, 1
+ ret
+.no
+ xor a
+ ret
+
+; =============================================================================
+; sys_progname(B = id, DE = namebuf) - copy the command name for a program id.
+; =============================================================================
+sys_progname::
+ ld hl, NameTable
+.l
+ ld a, [hl]
+ or a
+ jr z, .none
+ ld a, l
+ ld [wPnStart], a
+ ld a, h
+ ld [wPnStart+1], a
+.sk
+ ld a, [hl+]
+ or a
+ jr nz, .sk ; HL now at the id byte
+ ld a, [hl]
+ cp b
+ jr z, .match
+ inc hl ; skip id -> next entry
+ jr .l
+.match
+ ld a, [wPnStart]
+ ld l, a
+ ld a, [wPnStart+1]
+ ld h, a
+ ld b, 8
+.cp
+ ld a, [hl+]
+ ld [de], a
+ inc de
+ or a
+ jr z, .done
+ dec b
+ jr nz, .cp
+.done
+ ret
+.none
+ ld a, $3F ; '?'
+ ld [de], a
+ inc de
+ xor a
+ ld [de], a
+ ret