collapseos/parts/shell.asm
Virgil Dupras 902c6a5dd3 shell: add count arg to peek
Also, fix seek reversed endianness.
2019-04-14 11:54:18 -04:00

269 lines
4.9 KiB
NASM

; shell
;
; Runs a shell over an asynchronous communication interface adapter (ACIA).
; for now, this unit is tightly coupled to acia.asm, but it will eventually be
; more general than that.
; 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.
;
; Commands, for now, are dummy.
;
; See constants below for error codes.
;
; All numerical values in the Collapse OS shell are represented and parsed in
; hexadecimal form, without prefix or suffix.
; *** CONSTS ***
; number of entries in shellCmdTbl
SHELL_CMD_COUNT .equ 2
; The command that was type isn't known to the shell
SHELL_ERR_UNKNOWN_CMD .equ 0x01
; Arguments for the command weren't properly formatted
SHELL_ERR_BAD_ARGS .equ 0x02
; *** VARIABLES ***
; Memory address that the shell is currently "pointing at" for peek and deek
; operations. Set with seek.
SHELL_MEM_PTR .equ SHELL_RAMSTART
; Used to store formatted hex values just before printing it.
SHELL_HEX_FMT .equ SHELL_MEM_PTR+2
SHELL_RAMEND .equ SHELL_HEX_FMT+2
; *** CODE ***
shellInit:
xor a
ld (SHELL_MEM_PTR), a
; print prompt
ld hl, .prompt
call printstr
call printcrlf
ret
.prompt:
.db "Collapse OS", 0
shellLoop:
; check if the input buffer is full or ends in CR or LF. If it does,
; prints it back and empty it.
call aciaBufPtr
cp 0
jr z, shellLoop ; BUFIDX is zero? nothing to check.
cp ACIA_BUFSIZE
jr z, .do ; if BUFIDX == BUFSIZE? do!
; our previous char is in BUFIDX - 1. Fetch this
dec hl
ld a, (hl) ; now, that's our char we have in A
inc hl ; put HL back where it was
cp ASCII_CR
jr z, .do ; char is CR? do!
cp ASCII_LF
jr z, .do ; char is LF? do!
; nothing matched? don't do anything
jr shellLoop
.do:
; terminate our string with 0
xor a
ld (hl), a
; reset buffer index
ld (ACIA_BUFIDX), a
; alright, let's go!
ld hl, ACIA_BUF
call shellParse
jr shellLoop
printcrlf:
ld a, ASCII_CR
call aciaPutC
ld a, ASCII_LF
call aciaPutC
ret
; Parse command (null terminated) at HL and calls it
shellParse:
push af
push bc
push de
push hl
ld de, shellCmdTbl
ld a, SHELL_CMD_COUNT
ld b, a
.loop:
ld a, 4 ; 4 chars to compare
call strncmp
jr z, .found
ld a, 6
call addDE
djnz .loop
; exhausted loop? not found
ld a, SHELL_ERR_UNKNOWN_CMD
call shellPrintErr
jr .end
.found:
; all right, we're almost ready to call the cmd. Let's just have DE
; point to the cmd jump line.
ld a, 4
call addDE
; Now, let's swap HL and DE because, welll because that's how we're set.
ex hl, de ; HL = jump line, DE = cmd str pointer
; Before we call our command, we want to set up the pointer to the arg
; list. Normally, it's DE+5 (DE+4 is the space) unless DE+4 is null,
; which means no arg.
ld a, 4
call addDE
ld a, (DE)
cp 0
jr z, .noarg ; char is null? we have no arg
inc de
.noarg:
; DE points to args, HL points to jump line. Ready to roll!
call jumpHL
.end:
pop hl
pop de
pop bc
pop af
ret
; Print the error code set in A (in hex)
shellPrintErr:
push af
push hl
ld hl, .str
call printstr
ld hl, SHELL_HEX_FMT
call fmtHexPair
ld a, 2
call printnstr
call printcrlf
pop hl
pop af
ret
.str:
.db "ERR ", 0
; *** COMMANDS ***
; When these commands are called, DE points to the first character of the
; command args.
; Set memory pointer to the specified address (word).
; Example: seek 01fe
shellSeek:
push af
push de
push hl
ex de, hl
call parseHexPair
jr c, .error
; z80 is little endian. in a "ld hl, (nn)" op, L is loaded from the
; first byte, H is loaded from the second
ld (SHELL_MEM_PTR+1), a
inc hl
inc hl
call parseHexPair
jr c, .error
ld (SHELL_MEM_PTR), a
jr .success
.error:
ld a, SHELL_ERR_BAD_ARGS
call shellPrintErr
jr .end
.success:
ld a, (SHELL_MEM_PTR+1)
ld hl, SHELL_HEX_FMT
call fmtHexPair
ld a, 2
call printnstr
ld a, (SHELL_MEM_PTR)
call fmtHexPair
ld a, 2
call printnstr
call printcrlf
.end:
pop hl
pop de
pop af
ret
; peek byte where memory pointer points to any display its value. If the
; optional numerical byte arg is supplied, this number of bytes will be printed
;
; Example: peek 2 (will print 2 bytes)
shellPeek:
push af
push bc
push de
push hl
ld b, 1 ; by default, we run the loop once
ld a, (de)
cp 0
jr z, .success ; no arg? don't try to parse
ex de, hl
call parseHexPair
jr c, .error
cp 0
jr z, .error ; zero isn't a good arg, error
ld b, a ; loop the number of times specified in arg
jr .success
.error:
ld a, SHELL_ERR_BAD_ARGS
call shellPrintErr
jr .end
.success:
ld hl, (SHELL_MEM_PTR)
.loop: ld a, (hl)
ex hl, de
ld hl, SHELL_HEX_FMT
call fmtHexPair
ld a, 2
call printnstr
ex hl, de
inc hl
djnz .loop
call printcrlf
.end:
pop hl
pop de
pop bc
pop af
ret
; Format: 4 bytes name followed by 2 bytes jump. fill names with zeroes
shellCmdTbl:
.db "seek"
jr shellSeek
.db "peek"
jr shellPeek