mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-23 23:48:05 +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
|
ld sp, hl
|
||||||
im 1
|
im 1
|
||||||
call aciaInit
|
call aciaInit
|
||||||
|
xor a
|
||||||
|
ld de, BLOCKDEV_GETC
|
||||||
|
call blkSel
|
||||||
|
call stdioInit
|
||||||
call shellInit
|
call shellInit
|
||||||
ei
|
ei
|
||||||
jp shellLoop
|
jp shellLoop
|
||||||
@ -32,10 +36,16 @@ look like:
|
|||||||
#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
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.
|
; 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
|
||||||
|
|
||||||
|
@ -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:
|
Then, you insert your SD card in your SPI relay and go:
|
||||||
|
|
||||||
Collapse OS
|
Collapse OS
|
||||||
> mptr 9000
|
|
||||||
9000
|
|
||||||
> sdci
|
> sdci
|
||||||
> bsel 1
|
> bsel 1
|
||||||
> fson
|
> fson
|
||||||
> fls
|
> fls
|
||||||
helo
|
helo
|
||||||
hello.txt
|
hello.txt
|
||||||
> fopn 0 helo
|
> helo
|
||||||
> load 10
|
|
||||||
> peek 10
|
|
||||||
210690C3030048656C6C6F210D0A0000
|
|
||||||
> call 00 0000
|
|
||||||
Hello!
|
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
|
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
|
||||||
kernel that weights less than 3 kilobytes!
|
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
|
; 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,6 +47,8 @@ jp aciaInt
|
|||||||
.dw sdcInitializeCmd, blkBselCmd, blkSeekCmd
|
.dw sdcInitializeCmd, blkBselCmd, blkSeekCmd
|
||||||
.dw fsOnCmd, flsCmd, fnewCmd, fdelCmd, fopnCmd
|
.dw fsOnCmd, flsCmd, fnewCmd, fdelCmd, fopnCmd
|
||||||
|
|
||||||
|
#include "pgm.asm"
|
||||||
|
|
||||||
.equ SDC_RAMSTART SHELL_RAMEND
|
.equ SDC_RAMSTART SHELL_RAMEND
|
||||||
.equ SDC_PORT_CSHIGH 6
|
.equ SDC_PORT_CSHIGH 6
|
||||||
.equ SDC_PORT_CSLOW 5
|
.equ SDC_PORT_CSLOW 5
|
||||||
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user