mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-23 20:18:04 +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:
parent
7c191fd978
commit
2e8af376e3
@ -25,6 +25,10 @@ look like:
|
||||
ld sp, hl
|
||||
im 1
|
||||
call aciaInit
|
||||
xor a
|
||||
ld de, BLOCKDEV_GETC
|
||||
call blkSel
|
||||
call stdioInit
|
||||
call shellInit
|
||||
ei
|
||||
jp shellLoop
|
||||
@ -32,10 +36,16 @@ look like:
|
||||
#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
61
kernel/pgm.asm
Normal 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
|
@ -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
|
||||
|
||||
|
@ -108,22 +108,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
|
||||
> 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
|
||||
kernel that weights less than 3 kilobytes!
|
||||
|
@ -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,6 +47,8 @@ jp aciaInt
|
||||
.dw sdcInitializeCmd, blkBselCmd, blkSeekCmd
|
||||
.dw fsOnCmd, flsCmd, fnewCmd, fdelCmd, fopnCmd
|
||||
|
||||
#include "pgm.asm"
|
||||
|
||||
.equ SDC_RAMSTART SHELL_RAMEND
|
||||
.equ SDC_PORT_CSHIGH 6
|
||||
.equ SDC_PORT_CSLOW 5
|
||||
@ -64,6 +67,8 @@ init:
|
||||
call blkSel
|
||||
call stdioInit
|
||||
call shellInit
|
||||
ld hl, pgmShellHook
|
||||
ld (SHELL_CMDHOOK), hl
|
||||
|
||||
ei
|
||||
jp shellLoop
|
||||
|
@ -4,7 +4,9 @@
|
||||
.org 0x9000
|
||||
|
||||
ld hl, sHello
|
||||
jp printstr ; return
|
||||
call printstr
|
||||
xor a ; success
|
||||
ret
|
||||
|
||||
sHello:
|
||||
.db "Hello!", 0x0d, 0x0a, 0
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user