1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-27 12:18:07 +11:00

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 ld sp, hl
im 1 im 1
call aciaInit call aciaInit
call shellInit xor a
ld de, BLOCKDEV_GETC
call blkSel
call stdioInit
call shellInit
ei ei
jp shellLoop jp shellLoop
#include "core.asm" #include "core.asm"
.equ ACIA_RAMSTART RAMSTART .equ ACIA_RAMSTART RAMSTART
#include "acia.asm" #include "acia.asm"
.equ SHELL_RAMSTART ACIA_RAMEND .equ BLOCKDEV_RAMSTART ACIA_RAMEND
.define SHELL_GETC call aciaGetC .equ BLOCKDEV_COUNT 1
.define SHELL_PUTC call aciaPutC #include "blockdev.asm"
.define SHELL_IO_GETC call aciaGetC ; 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 .equ SHELL_EXTRA_CMD_COUNT 0
#include "shell.asm" #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. ; Runs a shell over a block device interface.
; Status: incomplete. As it is now, it spits a welcome prompt, wait for input ; The shell spits a welcome prompt, wait for input and compare the first 4 chars
; and compare the first 4 chars of the input with a command table and call the ; of the input with a command table and call the appropriate routine if it's
; appropriate routine if it's found, an error if it's not. ; 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. ; See constants below for error codes.
; ;
@ -59,7 +65,10 @@
; for the null-termination every time we write to it. Simpler that way. ; 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_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 *** ; *** CODE ***
shellInit: shellInit:
@ -67,11 +76,12 @@ shellInit:
ld (SHELL_MEM_PTR), a ld (SHELL_MEM_PTR), a
ld (SHELL_MEM_PTR+1), a ld (SHELL_MEM_PTR+1), a
ld (SHELL_BUF), a ld (SHELL_BUF), a
ld hl, noop
ld (SHELL_CMDHOOK), hl
; print welcome ; print welcome
ld hl, .welcome ld hl, .welcome
call printstr jp printstr ; returns
ret
.welcome: .welcome:
.db "Collapse OS", ASCII_CR, ASCII_LF, "> ", 0 .db "Collapse OS", ASCII_CR, ASCII_LF, "> ", 0
@ -154,6 +164,12 @@ shellParse:
; exhausted loop? not found ; exhausted loop? not found
ld a, SHELL_ERR_UNKNOWN_CMD 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 jr .error
.found: .found:
@ -171,7 +187,7 @@ shellParse:
; We're ready to parse args ; We're ready to parse args
call shellParseArgs call shellParseArgs
cp 0 or a ; cp 0
jr nz, .parseerror jr nz, .parseerror
ld hl, SHELL_CMD_ARGS ld hl, SHELL_CMD_ARGS
@ -182,7 +198,7 @@ shellParse:
push de \ pop ix push de \ pop ix
; Ready to roll! ; Ready to roll!
call callIX call callIX
cp 0 or a ; cp 0
jr nz, .error ; if A is non-zero, we have an error jr nz, .error ; if A is non-zero, we have an error
jr .end jr .end

View File

@ -26,7 +26,7 @@ jp aciaInt
#include "stdio.asm" #include "stdio.asm"
.equ SHELL_RAMSTART STDIO_RAMEND .equ SHELL_RAMSTART STDIO_RAMEND
.equ SHELL_EXTRA_CMD_COUNT 0 .equ SHELL_EXTRA_CMD_COUNT 0
#include "shell.asm" #include "shell.asm"
init: 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: Then, you insert your SD card in your SPI relay and go:
Collapse OS Collapse OS
> mptr 9000 > sdci
9000 > bsel 1
> sdci > fson
> bsel 1 > fls
> fson helo
> fls hello.txt
helo > helo
hello.txt Hello!
> fopn 0 helo >
> load 10
> peek 10 The `helo` command is a bit magical and is due to the hook implemented in
210690C3030048656C6C6F210D0A0000 `pgm.asm`: when an unknown command is typed, it looks in the currently mounted
> call 00 0000 filesystem for a file with the same name. If it finds it, it loads it in memory
Hello! 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 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 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 ; The RAM module is selected on A15, so it has the range 0x8000-0xffff
.equ RAMSTART 0x8000 .equ RAMSTART 0x8000
.equ RAMEND 0xffff .equ RAMEND 0xffff
.equ PGM_CODEADDR 0x9000
.equ ACIA_CTL 0x80 ; Control and status. RS off. .equ ACIA_CTL 0x80 ; Control and status. RS off.
.equ ACIA_IO 0x81 ; Transmit. RS on. .equ ACIA_IO 0x81 ; Transmit. RS on.
@ -46,10 +47,12 @@ jp aciaInt
.dw sdcInitializeCmd, blkBselCmd, blkSeekCmd .dw sdcInitializeCmd, blkBselCmd, blkSeekCmd
.dw fsOnCmd, flsCmd, fnewCmd, fdelCmd, fopnCmd .dw fsOnCmd, flsCmd, fnewCmd, fdelCmd, fopnCmd
.equ SDC_RAMSTART SHELL_RAMEND #include "pgm.asm"
.equ SDC_PORT_CSHIGH 6
.equ SDC_PORT_CSLOW 5 .equ SDC_RAMSTART SHELL_RAMEND
.equ SDC_PORT_SPI 4 .equ SDC_PORT_CSHIGH 6
.equ SDC_PORT_CSLOW 5
.equ SDC_PORT_SPI 4
#include "sdc.asm" #include "sdc.asm"
init: init:
@ -64,6 +67,8 @@ init:
call blkSel call blkSel
call stdioInit call stdioInit
call shellInit call shellInit
ld hl, pgmShellHook
ld (SHELL_CMDHOOK), hl
ei ei
jp shellLoop jp shellLoop

View File

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

View File

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