mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-27 12:18:07 +11:00
Make userspace parse args the same way the shell does
This commit is contained in:
parent
57356e47b4
commit
f8bd8eeaaf
@ -7,3 +7,14 @@ be run.
|
|||||||
|
|
||||||
That doesn't mean that you can't include that code in your kernel though, but
|
That doesn't mean that you can't include that code in your kernel though, but
|
||||||
you will typically not want to do that.
|
you will typically not want to do that.
|
||||||
|
|
||||||
|
## Userspace convention
|
||||||
|
|
||||||
|
We execute a userspace application by calling the address it's loaded into. This
|
||||||
|
means: a userspace application is expected to return.
|
||||||
|
|
||||||
|
Whatever calls the userspace app (usually, it will be the shell), should set
|
||||||
|
HL to a pointer to unparsed arguments in string form, null terminated.
|
||||||
|
|
||||||
|
The userspace application is expected to set A on return. 0 means success,
|
||||||
|
non-zero means error.
|
||||||
|
@ -37,11 +37,13 @@
|
|||||||
; fsSeek
|
; fsSeek
|
||||||
; fsTell
|
; fsTell
|
||||||
; cpHLDE
|
; cpHLDE
|
||||||
|
; parseArgs
|
||||||
; FS_HANDLE_SIZE
|
; FS_HANDLE_SIZE
|
||||||
|
|
||||||
; *** Variables ***
|
; *** Variables ***
|
||||||
|
|
||||||
#include "user.h"
|
#include "user.h"
|
||||||
|
#include "err.h"
|
||||||
.org USER_CODE
|
.org USER_CODE
|
||||||
|
|
||||||
jp zasmMain
|
jp zasmMain
|
||||||
|
@ -15,15 +15,28 @@
|
|||||||
.equ ZASM_ORG ZASM_CTX_PC+2
|
.equ ZASM_ORG ZASM_CTX_PC+2
|
||||||
.equ ZASM_RAMEND ZASM_ORG+2
|
.equ ZASM_RAMEND ZASM_ORG+2
|
||||||
|
|
||||||
; Read file through blockdev ID in H and outputs its upcodes through blockdev
|
; Takes 2 byte arguments, blkdev in and blkdev out, expressed as IDs.
|
||||||
; ID in L. HL is set to the last lineno to be read.
|
; Read file through blkdev in and outputs its upcodes through blkdev out.
|
||||||
|
; HL is set to the last lineno to be read.
|
||||||
; Sets Z on success, unset on error. On error, A contains an error code (ERR_*)
|
; Sets Z on success, unset on error. On error, A contains an error code (ERR_*)
|
||||||
zasmMain:
|
zasmMain:
|
||||||
|
; Parse args. HL points to string already
|
||||||
|
; We don't allocate memory just to hold this. Because this happens
|
||||||
|
; before initialization, we don't really care where those args are
|
||||||
|
; parsed.
|
||||||
|
ld de, .argspecs
|
||||||
|
ld ix, ZASM_RAMSTART
|
||||||
|
call parseArgs
|
||||||
|
ld a, SHELL_ERR_BAD_ARGS
|
||||||
|
ret nz
|
||||||
|
|
||||||
|
; HL now points to parsed args
|
||||||
; Init I/O
|
; Init I/O
|
||||||
ld a, h
|
ld a, (ZASM_RAMSTART) ; blkdev in ID
|
||||||
ld de, IO_IN_GETC
|
ld de, IO_IN_GETC
|
||||||
call blkSel
|
call blkSel
|
||||||
ld a, l
|
inc hl
|
||||||
|
ld a, (ZASM_RAMSTART+1) ; blkdev out ID
|
||||||
ld de, IO_OUT_GETC
|
ld de, IO_OUT_GETC
|
||||||
call blkSel
|
call blkSel
|
||||||
|
|
||||||
@ -47,6 +60,9 @@ zasmMain:
|
|||||||
.end:
|
.end:
|
||||||
jp ioLineNo ; --> HL, --> DE, returns
|
jp ioLineNo ; --> HL, --> DE, returns
|
||||||
|
|
||||||
|
.argspecs:
|
||||||
|
.db 0b001, 0b001, 0
|
||||||
|
|
||||||
; Sets Z according to whether we're in first pass.
|
; Sets Z according to whether we're in first pass.
|
||||||
zasmIsFirstPass:
|
zasmIsFirstPass:
|
||||||
ld a, (ZASM_FIRST_PASS)
|
ld a, (ZASM_FIRST_PASS)
|
||||||
|
11
kernel/err.h
Normal file
11
kernel/err.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
; Error codes used throughout the kernel
|
||||||
|
|
||||||
|
; The command that was type isn't known to the shell
|
||||||
|
.equ SHELL_ERR_UNKNOWN_CMD 0x01
|
||||||
|
|
||||||
|
; Arguments for the command weren't properly formatted
|
||||||
|
.equ SHELL_ERR_BAD_ARGS 0x02
|
||||||
|
|
||||||
|
; IO routines (GetC, PutC) returned an error in a load/save command
|
||||||
|
.equ SHELL_ERR_IO_ERROR 0x05
|
||||||
|
|
105
kernel/parse.asm
105
kernel/parse.asm
@ -1,3 +1,10 @@
|
|||||||
|
; *** Consts ***
|
||||||
|
; maximum number of bytes to receive as args in all commands. Determines the
|
||||||
|
; size of the args variable.
|
||||||
|
.equ PARSE_ARG_MAXCOUNT 3
|
||||||
|
|
||||||
|
; *** Code ***
|
||||||
|
|
||||||
; Parse the hex char at A and extract it's 0-15 numerical value. Put the result
|
; Parse the hex char at A and extract it's 0-15 numerical value. Put the result
|
||||||
; in A.
|
; in A.
|
||||||
;
|
;
|
||||||
@ -61,3 +68,101 @@ parseHexPair:
|
|||||||
.end:
|
.end:
|
||||||
pop bc
|
pop bc
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
; TODO: make parseArgs not expect a leading space.
|
||||||
|
; Parse arguments at (HL) with specifiers at (DE) into (IX).
|
||||||
|
;
|
||||||
|
; Args specifiers are a series of flag for each arg:
|
||||||
|
; Bit 0 - arg present: if unset, we stop parsing there
|
||||||
|
; Bit 1 - is word: this arg is a word rather than a byte. Because our
|
||||||
|
; destination are bytes anyway, this doesn't change much except
|
||||||
|
; for whether we expect a space between the hex pairs. If set,
|
||||||
|
; you still need to have a specifier for the second part of
|
||||||
|
; the multibyte.
|
||||||
|
; Bit 2 - optional: If set and not present during parsing, we don't error out
|
||||||
|
; and write zero
|
||||||
|
;
|
||||||
|
; Bit 3 - String argument: If set, this argument is a string. A pointer to the
|
||||||
|
; read string, null terminated (max 0x20 chars) will
|
||||||
|
; be placed in the next two bytes. This has to be the
|
||||||
|
; last argument of the list and it stops parsing.
|
||||||
|
; Sets A to nonzero if there was an error during parsing, zero otherwise.
|
||||||
|
parseArgs:
|
||||||
|
push bc
|
||||||
|
push de
|
||||||
|
push hl
|
||||||
|
push ix
|
||||||
|
|
||||||
|
ld b, PARSE_ARG_MAXCOUNT
|
||||||
|
xor c
|
||||||
|
.loop:
|
||||||
|
; init the arg value to a default 0
|
||||||
|
xor a
|
||||||
|
ld (ix), a
|
||||||
|
|
||||||
|
ld a, (hl)
|
||||||
|
; is this the end of the line?
|
||||||
|
or a ; cp 0
|
||||||
|
jr z, .endofargs
|
||||||
|
|
||||||
|
; do we have a proper space char?
|
||||||
|
cp ' '
|
||||||
|
jr z, .hasspace ; We're fine
|
||||||
|
|
||||||
|
; is our previous arg a multibyte? (argspec still in C)
|
||||||
|
bit 1, c
|
||||||
|
jr z, .error ; bit not set? error
|
||||||
|
dec hl ; offset the "inc hl" below
|
||||||
|
|
||||||
|
.hasspace:
|
||||||
|
; Get the specs
|
||||||
|
ld a, (de)
|
||||||
|
bit 0, a ; do we have an arg?
|
||||||
|
jr z, .error ; not set? then we have too many args
|
||||||
|
ld c, a ; save the specs for the next loop
|
||||||
|
inc hl ; (hl) points to a space, go next
|
||||||
|
bit 3, a ; is our arg a string?
|
||||||
|
jr z, .notAString
|
||||||
|
; our arg is a string. Let's place HL in our next two bytes and call
|
||||||
|
; it a day. Little endian, remember
|
||||||
|
ld (ix), l
|
||||||
|
ld (ix+1), h
|
||||||
|
jr .success ; directly to success: skip endofargs checks
|
||||||
|
.notAString:
|
||||||
|
call parseHexPair
|
||||||
|
jr c, .error
|
||||||
|
; we have a good arg and we need to write A in (IX).
|
||||||
|
ld (ix), a
|
||||||
|
|
||||||
|
; Good! increase counters
|
||||||
|
inc de
|
||||||
|
inc ix
|
||||||
|
inc hl ; get to following char (generally a space)
|
||||||
|
djnz .loop
|
||||||
|
; If we get here, it means that our next char *has* to be a null char
|
||||||
|
ld a, (hl)
|
||||||
|
cp 0
|
||||||
|
jr z, .success ; zero? great!
|
||||||
|
jr .error
|
||||||
|
|
||||||
|
.endofargs:
|
||||||
|
; We encountered our null char. Let's verify that we either have no
|
||||||
|
; more args or that they are optional
|
||||||
|
ld a, (de)
|
||||||
|
cp 0
|
||||||
|
jr z, .success ; no arg? success
|
||||||
|
bit 2, a
|
||||||
|
jr nz, .success ; if set, arg is optional. success
|
||||||
|
jr .error
|
||||||
|
|
||||||
|
.success:
|
||||||
|
xor a
|
||||||
|
jr .end
|
||||||
|
.error:
|
||||||
|
inc a
|
||||||
|
.end:
|
||||||
|
pop ix
|
||||||
|
pop hl
|
||||||
|
pop de
|
||||||
|
pop bc
|
||||||
|
ret
|
||||||
|
130
kernel/shell.asm
130
kernel/shell.asm
@ -20,6 +20,8 @@
|
|||||||
; hexadecimal form, without prefix or suffix.
|
; hexadecimal form, without prefix or suffix.
|
||||||
|
|
||||||
; *** REQUIREMENTS ***
|
; *** REQUIREMENTS ***
|
||||||
|
; err
|
||||||
|
; core
|
||||||
; parse
|
; parse
|
||||||
; stdio
|
; stdio
|
||||||
|
|
||||||
@ -33,19 +35,6 @@
|
|||||||
; number of entries in shellCmdTbl
|
; number of entries in shellCmdTbl
|
||||||
.equ SHELL_CMD_COUNT 6+SHELL_EXTRA_CMD_COUNT
|
.equ SHELL_CMD_COUNT 6+SHELL_EXTRA_CMD_COUNT
|
||||||
|
|
||||||
; maximum number of bytes to receive as args in all commands. Determines the
|
|
||||||
; size of the args variable.
|
|
||||||
.equ SHELL_CMD_ARGS_MAXSIZE 3
|
|
||||||
|
|
||||||
; The command that was type isn't known to the shell
|
|
||||||
.equ SHELL_ERR_UNKNOWN_CMD 0x01
|
|
||||||
|
|
||||||
; Arguments for the command weren't properly formatted
|
|
||||||
.equ SHELL_ERR_BAD_ARGS 0x02
|
|
||||||
|
|
||||||
; IO routines (GetC, PutC) returned an error in a load/save command
|
|
||||||
.equ SHELL_ERR_IO_ERROR 0x05
|
|
||||||
|
|
||||||
; Size of the shell command buffer. If a typed command reaches this size, the
|
; Size of the shell command buffer. If a typed command reaches this size, the
|
||||||
; command is flushed immediately (same as pressing return).
|
; command is flushed immediately (same as pressing return).
|
||||||
.equ SHELL_BUFSIZE 0x20
|
.equ SHELL_BUFSIZE 0x20
|
||||||
@ -62,7 +51,7 @@
|
|||||||
; Command buffer. We read types chars into this buffer until return is pressed
|
; Command buffer. We read types chars into this buffer until return is pressed
|
||||||
; This buffer is null-terminated and we don't keep an index around: we look
|
; This buffer is null-terminated and we don't keep an index around: we look
|
||||||
; 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+PARSE_ARG_MAXCOUNT
|
||||||
|
|
||||||
; Pointer to a hook to call when a cmd name isn't found
|
; Pointer to a hook to call when a cmd name isn't found
|
||||||
.equ SHELL_CMDHOOK SHELL_BUF+SHELL_BUFSIZE
|
.equ SHELL_CMDHOOK SHELL_BUF+SHELL_BUFSIZE
|
||||||
@ -185,14 +174,15 @@ shellParse:
|
|||||||
call addDE
|
call addDE
|
||||||
|
|
||||||
; We're ready to parse args
|
; We're ready to parse args
|
||||||
call shellParseArgs
|
ld ix, SHELL_CMD_ARGS
|
||||||
|
call parseArgs
|
||||||
or a ; cp 0
|
or a ; cp 0
|
||||||
jr nz, .parseerror
|
jr nz, .parseerror
|
||||||
|
|
||||||
ld hl, SHELL_CMD_ARGS
|
|
||||||
; Args parsed, now we can load the routine address and call it.
|
; Args parsed, now we can load the routine address and call it.
|
||||||
; let's have DE point to the jump line
|
; let's have DE point to the jump line
|
||||||
ld a, SHELL_CMD_ARGS_MAXSIZE
|
ld hl, SHELL_CMD_ARGS
|
||||||
|
ld a, PARSE_ARG_MAXCOUNT
|
||||||
call addDE
|
call addDE
|
||||||
push de \ pop ix
|
push de \ pop ix
|
||||||
; Ready to roll!
|
; Ready to roll!
|
||||||
@ -230,115 +220,13 @@ shellPrintErr:
|
|||||||
.str:
|
.str:
|
||||||
.db "ERR ", 0
|
.db "ERR ", 0
|
||||||
|
|
||||||
; Parse arguments at (HL) with specifiers at (DE) into (SHELL_CMD_ARGS).
|
|
||||||
; (HL) should point to the character *just* after the name of the command
|
|
||||||
; because we verify, in the case that we have args, that we have a space there.
|
|
||||||
;
|
|
||||||
; Args specifiers are a series of flag for each arg:
|
|
||||||
; Bit 0 - arg present: if unset, we stop parsing there
|
|
||||||
; Bit 1 - is word: this arg is a word rather than a byte. Because our
|
|
||||||
; destination are bytes anyway, this doesn't change much except
|
|
||||||
; for whether we expect a space between the hex pairs. If set,
|
|
||||||
; you still need to have a specifier for the second part of
|
|
||||||
; the multibyte.
|
|
||||||
; Bit 2 - optional: If set and not present during parsing, we don't error out
|
|
||||||
; and write zero
|
|
||||||
;
|
|
||||||
; Bit 3 - String argument: If set, this argument is a string. A pointer to the
|
|
||||||
; read string, null terminated (max 0x20 chars) will
|
|
||||||
; be placed in the next two bytes. This has to be the
|
|
||||||
; last argument of the list and it stops parsing.
|
|
||||||
; Sets A to nonzero if there was an error during parsing, zero otherwise.
|
|
||||||
; If there was an error during parsing, carry is set.
|
|
||||||
shellParseArgs:
|
|
||||||
push bc
|
|
||||||
push de
|
|
||||||
push hl
|
|
||||||
push ix
|
|
||||||
|
|
||||||
ld ix, SHELL_CMD_ARGS
|
|
||||||
ld a, SHELL_CMD_ARGS_MAXSIZE
|
|
||||||
ld b, a
|
|
||||||
xor c
|
|
||||||
.loop:
|
|
||||||
; init the arg value to a default 0
|
|
||||||
xor a
|
|
||||||
ld (ix), a
|
|
||||||
|
|
||||||
ld a, (hl)
|
|
||||||
; is this the end of the line?
|
|
||||||
cp 0
|
|
||||||
jr z, .endofargs
|
|
||||||
|
|
||||||
; do we have a proper space char?
|
|
||||||
cp ' '
|
|
||||||
jr z, .hasspace ; We're fine
|
|
||||||
|
|
||||||
; is our previous arg a multibyte? (argspec still in C)
|
|
||||||
bit 1, c
|
|
||||||
jr z, .error ; bit not set? error
|
|
||||||
dec hl ; offset the "inc hl" below
|
|
||||||
|
|
||||||
.hasspace:
|
|
||||||
; Get the specs
|
|
||||||
ld a, (de)
|
|
||||||
bit 0, a ; do we have an arg?
|
|
||||||
jr z, .error ; not set? then we have too many args
|
|
||||||
ld c, a ; save the specs for the next loop
|
|
||||||
inc hl ; (hl) points to a space, go next
|
|
||||||
bit 3, a ; is our arg a string?
|
|
||||||
jr z, .notAString
|
|
||||||
; our arg is a string. Let's place HL in our next two bytes and call
|
|
||||||
; it a day. Little endian, remember
|
|
||||||
ld (ix), l
|
|
||||||
ld (ix+1), h
|
|
||||||
jr .success ; directly to success: skip endofargs checks
|
|
||||||
.notAString:
|
|
||||||
call parseHexPair
|
|
||||||
jr c, .error
|
|
||||||
; we have a good arg and we need to write A in (IX).
|
|
||||||
ld (ix), a
|
|
||||||
|
|
||||||
; Good! increase counters
|
|
||||||
inc de
|
|
||||||
inc ix
|
|
||||||
inc hl ; get to following char (generally a space)
|
|
||||||
djnz .loop
|
|
||||||
; If we get here, it means that our next char *has* to be a null char
|
|
||||||
ld a, (hl)
|
|
||||||
cp 0
|
|
||||||
jr z, .success ; zero? great!
|
|
||||||
jr .error
|
|
||||||
|
|
||||||
.endofargs:
|
|
||||||
; We encountered our null char. Let's verify that we either have no
|
|
||||||
; more args or that they are optional
|
|
||||||
ld a, (de)
|
|
||||||
cp 0
|
|
||||||
jr z, .success ; no arg? success
|
|
||||||
bit 2, a
|
|
||||||
jr nz, .success ; if set, arg is optional. success
|
|
||||||
jr .error
|
|
||||||
|
|
||||||
.success:
|
|
||||||
xor a
|
|
||||||
jr .end
|
|
||||||
.error:
|
|
||||||
inc a
|
|
||||||
.end:
|
|
||||||
pop ix
|
|
||||||
pop hl
|
|
||||||
pop de
|
|
||||||
pop bc
|
|
||||||
ret
|
|
||||||
|
|
||||||
; *** COMMANDS ***
|
; *** COMMANDS ***
|
||||||
; A command is a 4 char names, followed by a SHELL_CMD_ARGS_MAXSIZE bytes of
|
; A command is a 4 char names, followed by a PARSE_ARG_MAXCOUNT bytes of
|
||||||
; argument specs, followed by the routine. Then, a simple table of addresses
|
; argument specs, followed by the routine. Then, a simple table of addresses
|
||||||
; is compiled in a block and this is what is iterated upon when we want all
|
; is compiled in a block and this is what is iterated upon when we want all
|
||||||
; available commands.
|
; available commands.
|
||||||
;
|
;
|
||||||
; Format: 4 bytes name followed by SHELL_CMD_ARGS_MAXSIZE bytes specifiers,
|
; Format: 4 bytes name followed by PARSE_ARG_MAXCOUNT bytes specifiers,
|
||||||
; followed by 3 bytes jump. fill names with zeroes
|
; followed by 3 bytes jump. fill names with zeroes
|
||||||
;
|
;
|
||||||
; When these commands are called, HL points to the first byte of the
|
; When these commands are called, HL points to the first byte of the
|
||||||
|
@ -54,7 +54,7 @@ updatebootstrap: $(ZASMBIN)
|
|||||||
.PHONY: rescue
|
.PHONY: rescue
|
||||||
rescue:
|
rescue:
|
||||||
scas -o zasm/kernel.bin -I $(KERNEL) zasm/glue.asm
|
scas -o zasm/kernel.bin -I $(KERNEL) zasm/glue.asm
|
||||||
scas -o zasm/zasm.bin -I $(APPS) $(APPS)/zasm/glue.asm
|
scas -o zasm/zasm.bin -I $(APPS) -I $(KERNEL) $(APPS)/zasm/glue.asm
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
jp printstr
|
jp printstr
|
||||||
|
|
||||||
#include "core.asm"
|
#include "core.asm"
|
||||||
|
#include "err.h"
|
||||||
#include "parse.asm"
|
#include "parse.asm"
|
||||||
|
|
||||||
.equ BLOCKDEV_RAMSTART RAMSTART
|
.equ BLOCKDEV_RAMSTART RAMSTART
|
||||||
|
@ -27,8 +27,10 @@ jp fsGetC
|
|||||||
jp fsSeek
|
jp fsSeek
|
||||||
jp fsTell
|
jp fsTell
|
||||||
jp cpHLDE
|
jp cpHLDE
|
||||||
|
jp parseArgs
|
||||||
|
|
||||||
#include "core.asm"
|
#include "core.asm"
|
||||||
|
#include "err.h"
|
||||||
#include "parse.asm"
|
#include "parse.asm"
|
||||||
.equ BLOCKDEV_RAMSTART RAMSTART
|
.equ BLOCKDEV_RAMSTART RAMSTART
|
||||||
.equ BLOCKDEV_COUNT 3
|
.equ BLOCKDEV_COUNT 3
|
||||||
@ -50,12 +52,14 @@ init:
|
|||||||
ld de, BLOCKDEV_GETC
|
ld de, BLOCKDEV_GETC
|
||||||
call blkSel
|
call blkSel
|
||||||
call fsOn
|
call fsOn
|
||||||
ld h, 0 ; input blkdev
|
ld hl, .zasmArgs
|
||||||
ld l, 1 ; output blkdev
|
|
||||||
call USER_CODE
|
call USER_CODE
|
||||||
; signal the emulator we're done
|
; signal the emulator we're done
|
||||||
halt
|
halt
|
||||||
|
|
||||||
|
.zasmArgs:
|
||||||
|
.db " 0 1", 0
|
||||||
|
|
||||||
; *** I/O ***
|
; *** I/O ***
|
||||||
emulGetC:
|
emulGetC:
|
||||||
in a, (STDIO_PORT)
|
in a, (STDIO_PORT)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
.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.
|
||||||
|
|
||||||
|
#include "err.h"
|
||||||
#include "core.asm"
|
#include "core.asm"
|
||||||
#include "parse.asm"
|
#include "parse.asm"
|
||||||
.equ ACIA_RAMSTART RAMSTART
|
.equ ACIA_RAMSTART RAMSTART
|
||||||
|
@ -21,7 +21,9 @@
|
|||||||
.equ fsSeek 0x30
|
.equ fsSeek 0x30
|
||||||
.equ fsTell 0x33
|
.equ fsTell 0x33
|
||||||
.equ cpHLDE 0x36
|
.equ cpHLDE 0x36
|
||||||
|
.equ parseArgs 0x39
|
||||||
|
|
||||||
|
#include "err.h"
|
||||||
#include "zasm/const.asm"
|
#include "zasm/const.asm"
|
||||||
#include "zasm/util.asm"
|
#include "zasm/util.asm"
|
||||||
.equ IO_RAMSTART USER_RAMSTART
|
.equ IO_RAMSTART USER_RAMSTART
|
||||||
|
Loading…
Reference in New Issue
Block a user