pgm: new kernel module

The pgm module implements a shell hook so that when an unknown command
is typed, we look into the mounted filesystem and look for a file with
the same name as the command. If we find one, we load it in memory and
run it.
This commit is contained in:
Virgil Dupras 2019-05-31 14:50:43 -04:00
parent 7c191fd978
commit 2e8af376e3
8 changed files with 141 additions and 39 deletions

View File

@ -25,17 +25,27 @@ look like:
ld sp, hl
im 1
call aciaInit
call shellInit
xor a
ld de, BLOCKDEV_GETC
call blkSel
call stdioInit
call shellInit
ei
jp shellLoop
jp shellLoop
#include "core.asm"
.equ ACIA_RAMSTART RAMSTART
#include "acia.asm"
.equ SHELL_RAMSTART ACIA_RAMEND
.define SHELL_GETC call aciaGetC
.define SHELL_PUTC call aciaPutC
.define SHELL_IO_GETC call aciaGetC
.equ BLOCKDEV_RAMSTART ACIA_RAMEND
.equ BLOCKDEV_COUNT 1
#include "blockdev.asm"
; List of devices
.dw aciaGetC, aciaPutC, 0, 0
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
#include "stdio.asm"
.equ SHELL_RAMSTART STDIO_RAMEND
.equ SHELL_EXTRA_CMD_COUNT 0
#include "shell.asm"

61
kernel/pgm.asm Normal file
View File

@ -0,0 +1,61 @@
; pgm - execute programs loaded from filesystem
;
; Implements a shell hook that searches the filesystem for a file with the same
; name as the cmd, loads that file in memory and executes it, sending the
; program a pointer to *unparsed* arguments in HL.
;
; We expect the loaded program to return a status code in A. 0 means success,
; non-zero means error. Programs should avoid having error code overlaps with
; the shell so that we know where the error comes from.
;
; *** Defines ***
; PGM_CODEADDR: Memory address where to place the code we load.
; Routine suitable to plug into SHELL_CMDHOOK. HL points to the full cmdline.
; We can mutate it because the shell doesn't do anything with it afterwards.
pgmShellHook:
call fsIsOn
jr nz, .noFile
; first first space and replace it with zero so that we have something
; suitable for fsFindFN.
push hl ; remember beginning
ld a, ' '
call findchar
jr nz, .noarg ; if we have no arg, we want DE to point to the
; null char. Also, we have no replacement to
; make
; replace space with nullchar
xor a
ld (hl), a
inc hl ; make HL point to the beginning of args
.noarg:
ex de, hl ; DE now points to the beginning of args or to \0 if
; no args
pop hl ; HL points to cmdname, properly null-terminated
call fsFindFN
jr nz, .noFile
; We have a file! Load it and run it.
jp pgmRun
.noFile:
ld a, SHELL_ERR_IO_ERROR
ret
; Loads code in file that FS_PTR is currently pointing at and place it in
; PGM_CODEADDR. Then, jump to PGM_CODEADDR.
pgmRun:
call fsIsValid
jr nz, .ioError
ld ix, FS_HANDLES
call fsOpen
ld hl, PGM_CODEADDR
.loop:
call fsGetC ; we use Z at end of loop
ld (hl), a ; Z preserved
inc hl ; Z preserved in 16-bit
jr z, .loop
; ready to jump!
jp PGM_CODEADDR
.ioError:
ld a, SHELL_ERR_IO_ERROR
ret

View File

@ -2,11 +2,17 @@
;
; Runs a shell over a block device interface.
; Status: incomplete. As it is now, it spits a welcome prompt, wait for input
; and compare the first 4 chars of the input with a command table and call the
; appropriate routine if it's found, an error if it's not.
; The shell spits a welcome prompt, wait for input and compare the first 4 chars
; of the input with a command table and call the appropriate routine if it's
; found, an error if it's not.
;
; Commands, for now, are partially implemented.
; To determine the correct routine to call we first go through cmds in
; shellCmdTbl. This means that we first go through internal cmds, then cmds
; "grafted" by glue code.
;
; If the command isn't found, SHELL_CMDHOOK is called, which should set A to
; zero if it executes something. Otherwise, SHELL_ERR_UNKNOWN_CMD will be
; returned.
;
; See constants below for error codes.
;
@ -59,7 +65,10 @@
; for the null-termination every time we write to it. Simpler that way.
.equ SHELL_BUF SHELL_CMD_ARGS+SHELL_CMD_ARGS_MAXSIZE
.equ SHELL_RAMEND SHELL_BUF+SHELL_BUFSIZE
; Pointer to a hook to call when a cmd name isn't found
.equ SHELL_CMDHOOK SHELL_BUF+SHELL_BUFSIZE
.equ SHELL_RAMEND SHELL_CMDHOOK+2
; *** CODE ***
shellInit:
@ -67,11 +76,12 @@ shellInit:
ld (SHELL_MEM_PTR), a
ld (SHELL_MEM_PTR+1), a
ld (SHELL_BUF), a
ld hl, noop
ld (SHELL_CMDHOOK), hl
; print welcome
ld hl, .welcome
call printstr
ret
jp printstr ; returns
.welcome:
.db "Collapse OS", ASCII_CR, ASCII_LF, "> ", 0
@ -154,6 +164,12 @@ shellParse:
; exhausted loop? not found
ld a, SHELL_ERR_UNKNOWN_CMD
; Before erroring out, let's try SHELL_HOOK.
ld ix, (SHELL_CMDHOOK)
call callIX
jr z, .end ; oh, not an error!
; still an error. Might be different than SHELL_ERR_UNKNOWN_CMD though.
; maybe a routine was called, but errored out.
jr .error
.found:
@ -171,7 +187,7 @@ shellParse:
; We're ready to parse args
call shellParseArgs
cp 0
or a ; cp 0
jr nz, .parseerror
ld hl, SHELL_CMD_ARGS
@ -182,7 +198,7 @@ shellParse:
push de \ pop ix
; Ready to roll!
call callIX
cp 0
or a ; cp 0
jr nz, .error ; if A is non-zero, we have an error
jr .end

View File

@ -26,7 +26,7 @@ jp aciaInt
#include "stdio.asm"
.equ SHELL_RAMSTART STDIO_RAMEND
.equ SHELL_EXTRA_CMD_COUNT 0
.equ SHELL_EXTRA_CMD_COUNT 0
#include "shell.asm"
init:

View File

@ -107,22 +107,21 @@ filesystem into the `sdcard.cfs` file. That can be mounted by Collapse OS!
Then, you insert your SD card in your SPI relay and go:
Collapse OS
> mptr 9000
9000
> sdci
> bsel 1
> fson
> fls
helo
hello.txt
> fopn 0 helo
> load 10
> peek 10
210690C3030048656C6C6F210D0A0000
> call 00 0000
Hello!
>
Collapse OS
> sdci
> bsel 1
> fson
> fls
helo
hello.txt
> helo
Hello!
>
The `helo` command is a bit magical and is due to the hook implemented in
`pgm.asm`: when an unknown command is typed, it looks in the currently mounted
filesystem for a file with the same name. If it finds it, it loads it in memory
at a predefined place (in our case, `0x9000`) and executes it.
Now let that sink in for a minute. You've just mounted a filesystem on a SD
card, loaded a file from it in memory and executed that file, all that on a

View File

@ -2,6 +2,7 @@
; The RAM module is selected on A15, so it has the range 0x8000-0xffff
.equ RAMSTART 0x8000
.equ RAMEND 0xffff
.equ PGM_CODEADDR 0x9000
.equ ACIA_CTL 0x80 ; Control and status. RS off.
.equ ACIA_IO 0x81 ; Transmit. RS on.
@ -46,10 +47,12 @@ jp aciaInt
.dw sdcInitializeCmd, blkBselCmd, blkSeekCmd
.dw fsOnCmd, flsCmd, fnewCmd, fdelCmd, fopnCmd
.equ SDC_RAMSTART SHELL_RAMEND
.equ SDC_PORT_CSHIGH 6
.equ SDC_PORT_CSLOW 5
.equ SDC_PORT_SPI 4
#include "pgm.asm"
.equ SDC_RAMSTART SHELL_RAMEND
.equ SDC_PORT_CSHIGH 6
.equ SDC_PORT_CSLOW 5
.equ SDC_PORT_SPI 4
#include "sdc.asm"
init:
@ -64,6 +67,8 @@ init:
call blkSel
call stdioInit
call shellInit
ld hl, pgmShellHook
ld (SHELL_CMDHOOK), hl
ei
jp shellLoop

View File

@ -4,7 +4,9 @@
.org 0x9000
ld hl, sHello
jp printstr ; return
call printstr
xor a ; success
ret
sHello:
.db "Hello!", 0x0d, 0x0a, 0

View File

@ -1,6 +1,7 @@
; named shell_.asm to avoid infinite include loop.
.equ RAMSTART 0x4000
.equ RAMEND 0x5000
.equ KERNEL_RAMEND 0x5000
.equ USERCODE 0x9000
.equ STDIO_PORT 0x00
.equ FS_DATA_PORT 0x01
.equ FS_SEEKL_PORT 0x02
@ -9,6 +10,9 @@
jp init
; *** JUMP TABLE ***
jp printstr
#include "core.asm"
#include "parse.asm"
@ -36,10 +40,13 @@
#include "shell.asm"
.dw blkBselCmd, blkSeekCmd, fsOnCmd, flsCmd, fnewCmd, fdelCmd, fopnCmd
.equ PGM_CODEADDR USERCODE
#include "pgm.asm"
init:
di
; setup stack
ld hl, RAMEND
ld hl, KERNEL_RAMEND
ld sp, hl
xor a
ld de, BLOCKDEV_GETC
@ -54,6 +61,8 @@ init:
ld de, BLOCKDEV_GETC
call blkSel
call shellInit
ld hl, pgmShellHook
ld (SHELL_CMDHOOK), hl
jp shellLoop
emulGetC: