aboutsummaryrefslogtreecommitdiffstats
path: root/src/programs.asm
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-16 00:31:27 +0200
committeruser <user@clank>2026-07-16 00:31:27 +0200
commitd5c06116d3d1257b46c476cf73b634cf089bcb82 (patch)
tree8912008c1ca483d9a8192961d703161dcf274411 /src/programs.asm
parentkernel: complete the process lifecycle (exit/wait/reap + bank free list) (diff)
downloadgbos-d5c06116d3d1257b46c476cf73b634cf089bcb82.tar.gz
gbos-d5c06116d3d1257b46c476cf73b634cf089bcb82.tar.xz
gbos-d5c06116d3d1257b46c476cf73b634cf089bcb82.zip
wip: sys_read + serial console shell (read-store bug under investigation)
- sys_read (SYS_READ): blocking console input via external-clock serial poll; yields while waiting. Verified working (direct-echo cat: 'hi' -> 'hi'). - shell (PROG_SH): prompt/read/parse/fork+exec+wait; worker+hello programs; StrEqual/SkipName helpers; command table. - init now execs the shell. - KNOWN BUG: storing the typed char to the line buffer at $A000 (cart RAM of an exec'd program) corrupts the following sys_read; sys_write reading $A000 also returns $3E. Kernel syscalls themselves are correct; isolating the cart-RAM / exec RAM-bank interaction next.
Diffstat (limited to '')
-rw-r--r--src/programs.asm166
1 files changed, 153 insertions, 13 deletions
diff --git a/src/programs.asm b/src/programs.asm
index b5e4d8e..df2b98d 100644
--- a/src/programs.asm
+++ b/src/programs.asm
@@ -1,36 +1,176 @@
; =============================================================================
-; programs.asm - userland program images (text-in-ROM model).
+; programs.asm - userland program images (text-in-ROM model) + the shell.
;
; Programs are linked to run from the ROMX window ($4000-$7FFF) and live in
-; their own ROM banks. exec() maps the bank and jumps to the entry. Programs
-; only talk to the kernel via `rst $30` (the trap + kernel live in ROM0, always
-; mapped), so they never reference kernel labels.
+; their own ROM banks. exec() maps the bank and jumps in. Programs talk to the
+; kernel only via `rst $30`.
; =============================================================================
INCLUDE "include/gbos.inc"
+; the shell keeps its line buffer + scratch at the bottom of its RAM bank
+DEF SH_LINEBUF EQU $A000 ; up to 32 bytes
+DEF SH_PROGID EQU $A040 ; chosen program id (survives fork via bank copy)
+DEF SH_MAXLINE EQU 32
+
; -----------------------------------------------------------------------------
-; PROG_WORKER: announce with 'w', then exit() with a status equal to our pid.
-; Exercises exec (runs from a ROM bank) + the exit/wait lifecycle.
+; PROG_WORKER (bank 2) - print a line, exit(0).
; -----------------------------------------------------------------------------
SECTION "prog_worker", ROMX[$4000], BANK[2]
ProgWorker:
ld de, .msg
- ld b, 1
+ ld b, .msgend - .msg
ld c, SYS_WRITE
rst $30
- ld c, SYS_GETPID
+ ld b, 0
+ ld c, SYS_EXIT
rst $30
- ld b, a ; exit status = pid (so reaped codes are distinct)
+.msg
+ db "worker!", $0D, $0A
+.msgend
+
+; -----------------------------------------------------------------------------
+; PROG_HELLO (bank 4) - print a line, exit(0).
+; -----------------------------------------------------------------------------
+SECTION "prog_hello", ROMX[$4000], BANK[4]
+ProgHello:
+ ld de, .msg
+ ld b, .msgend - .msg
+ ld c, SYS_WRITE
+ rst $30
+ ld b, 0
ld c, SYS_EXIT
- rst $30 ; never returns
+ rst $30
.msg
- db "w"
+ db "hello world!", $0D, $0A
+.msgend
+
+; -----------------------------------------------------------------------------
+; PROG_SH (bank 3) - a tiny shell.
+; prompt -> read a line (with echo) -> match a command -> fork+exec+wait.
+; -----------------------------------------------------------------------------
+SECTION "prog_sh", ROMX[$4000], BANK[3]
+ProgSh:
+.prompt
+ ld de, .ps1
+ ld b, 2
+ ld c, SYS_WRITE
+ rst $30
+
+ ; read a line into SH_LINEBUF, echoing as we go
+ ld hl, SH_LINEBUF
+.rd
+ ld c, SYS_READ
+ rst $30 ; A = input byte
+ cp $0D
+ jr z, .eol
+ cp $0A
+ jr z, .eol
+ ld [hl], a ; store in the line buffer (A preserved)
+ inc hl
+ ld [$FF01], a ; echo the char straight to serial
+ ld a, $81
+ ld [$FF02], a
+ jr .rd
+
+.eol
+ ld [hl], 0 ; NUL-terminate
+ ld de, .crlf
+ ld b, 2
+ ld c, SYS_WRITE
+ rst $30
+ ld a, [SH_LINEBUF]
+ or a
+ jr z, .prompt ; empty line
+
+ ; look the command up in the table
+ ld de, .cmdtab
+.scan
+ ld a, [de]
+ or a
+ jr z, .unknown ; empty name = end of table
+ push de
+ ld hl, SH_LINEBUF
+ call StrEqual ; Z set if equal
+ pop de
+ jr z, .match
+ call SkipName ; DE -> NUL after this name
+ inc de ; -> program id
+ inc de ; -> next entry
+ jr .scan
+
+.match
+ call SkipName
+ inc de ; DE -> program id
+ ld a, [de]
+ ld [SH_PROGID], a
+ ld c, SYS_FORK
+ rst $30
+ or a
+ jr z, .exec
+ ld c, SYS_WAIT ; parent: reap the command
+ rst $30
+ jr .prompt
+.exec
+ ld a, [SH_PROGID]
+ ld b, a
+ ld c, SYS_EXEC
+ rst $30 ; child becomes the command
+
+.unknown
+ ld de, .huh
+ ld b, 3
+ ld c, SYS_WRITE
+ rst $30
+ jr .prompt
+
+.ps1
+ db "$ "
+.crlf
+ db $0D, $0A
+.huh
+ db "?", $0D, $0A
+.cmdtab
+ db "worker", 0
+ db PROG_WORKER
+ db "hello", 0
+ db PROG_HELLO
+ db 0 ; terminator
+
+; -----------------------------------------------------------------------------
+; Shell string helpers (ROM0, always mapped).
+; -----------------------------------------------------------------------------
+SECTION "shell_lib", ROM0
+; StrEqual(HL, DE) - compare NUL-terminated strings. Z set if equal.
+; Clobbers A, B; advances HL and DE.
+StrEqual::
+.l
+ ld a, [de]
+ ld b, a
+ ld a, [hl]
+ cp b
+ ret nz
+ or a
+ ret z ; both NUL -> equal
+ inc hl
+ inc de
+ jr .l
+; SkipName(DE) - advance DE to its NUL terminator.
+SkipName::
+.l
+ ld a, [de]
+ or a
+ ret z
+ inc de
+ jr .l
; -----------------------------------------------------------------------------
-; Program table (ROM0). Indexed by exec()'s program id.
-; Entry: bank (2 bytes, MBC5 16-bit) then entry address (2 bytes).
+; Program table (ROM0). Index = program id. Entry: bank (2) + entry addr (2).
; -----------------------------------------------------------------------------
SECTION "progtab", ROM0
ProgramTable::
db LOW(BANK(ProgWorker)), HIGH(BANK(ProgWorker))
dw ProgWorker
+ db LOW(BANK(ProgSh)), HIGH(BANK(ProgSh))
+ dw ProgSh
+ db LOW(BANK(ProgHello)), HIGH(BANK(ProgHello))
+ dw ProgHello