2019-04-14 06:01:20 +10:00
|
|
|
; 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.
|
|
|
|
|
2019-04-14 12:39:28 +10:00
|
|
|
; 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.
|
2019-04-14 06:01:20 +10:00
|
|
|
|
|
|
|
; *** CONSTS ***
|
|
|
|
CR .equ 0x0d
|
|
|
|
LF .equ 0x0a
|
|
|
|
|
2019-04-14 12:39:28 +10:00
|
|
|
; 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
|
|
|
|
|
|
|
|
; *** CODE ***
|
2019-04-14 06:01:20 +10:00
|
|
|
shellInit:
|
|
|
|
; print prompt
|
2019-04-14 12:39:28 +10:00
|
|
|
ld hl, .prompt
|
2019-04-14 06:01:20 +10:00
|
|
|
call printstr
|
|
|
|
call printcrlf
|
|
|
|
ret
|
|
|
|
|
2019-04-14 12:39:28 +10:00
|
|
|
.prompt:
|
|
|
|
.db "Collapse OS", 0
|
|
|
|
|
2019-04-14 06:01:20 +10:00
|
|
|
shellLoop:
|
|
|
|
call chkbuf
|
|
|
|
jr shellLoop
|
|
|
|
|
|
|
|
; print null-terminated string pointed to by HL
|
|
|
|
printstr:
|
2019-04-14 12:39:28 +10:00
|
|
|
push af
|
|
|
|
push hl
|
|
|
|
|
|
|
|
.loop:
|
2019-04-14 06:01:20 +10:00
|
|
|
ld a, (hl) ; load character to send
|
|
|
|
or a ; is it zero?
|
2019-04-14 12:39:28 +10:00
|
|
|
jr z, .end ; if yes, we're finished
|
2019-04-14 06:01:20 +10:00
|
|
|
call aciaPutC
|
|
|
|
inc hl
|
2019-04-14 12:39:28 +10:00
|
|
|
jr .loop
|
|
|
|
|
|
|
|
.end:
|
|
|
|
pop hl
|
|
|
|
pop af
|
|
|
|
ret
|
2019-04-14 06:01:20 +10:00
|
|
|
|
|
|
|
printcrlf:
|
|
|
|
ld a, CR
|
|
|
|
call aciaPutC
|
|
|
|
ld a, LF
|
|
|
|
call aciaPutC
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
; check if the input buffer is full or ends in CR or LF. If it does, prints it
|
|
|
|
; back and empty it.
|
|
|
|
chkbuf:
|
|
|
|
call aciaBufPtr
|
|
|
|
cp 0
|
|
|
|
ret z ; 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 CR
|
|
|
|
jr z, .do ; char is CR? do!
|
|
|
|
cp LF
|
|
|
|
jr z, .do ; char is LF? do!
|
|
|
|
|
|
|
|
; nothing matched? don't do anything
|
|
|
|
ret
|
|
|
|
|
|
|
|
.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
|
2019-04-14 12:39:28 +10:00
|
|
|
call shellParse
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Compares strings pointed to by HL and DE up to A count of characters. If
|
|
|
|
; equal, Z is set. If not equal, Z is reset.
|
|
|
|
strncmp:
|
|
|
|
push bc
|
|
|
|
push hl
|
|
|
|
push de
|
|
|
|
|
|
|
|
ld b, a
|
|
|
|
.loop:
|
|
|
|
ld a, (de)
|
|
|
|
cp (hl)
|
|
|
|
jr nz, .end ; not equal? break early
|
|
|
|
inc hl
|
|
|
|
inc de
|
|
|
|
djnz .loop
|
|
|
|
|
|
|
|
.end:
|
|
|
|
pop de
|
|
|
|
pop hl
|
|
|
|
pop bc
|
|
|
|
; Because we don't call anything else than CP that modify the Z flag,
|
|
|
|
; our Z value will be that of the last cp (reset if we broke the loop
|
|
|
|
; early, set otherwise)
|
|
|
|
ret
|
|
|
|
|
|
|
|
; add the value of A into DE
|
|
|
|
addDE:
|
|
|
|
add a, e
|
|
|
|
jr nc, .end ; no carry? skip inc
|
|
|
|
inc d
|
|
|
|
.end:
|
|
|
|
ld e, a
|
|
|
|
ret
|
|
|
|
|
|
|
|
; jump to the location pointed to by HL. This allows us to call HL instead of
|
|
|
|
; just jumping it.
|
|
|
|
jumpHL:
|
|
|
|
jp hl
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Parse command (null terminated) at HL and calls it
|
|
|
|
shellParse:
|
|
|
|
push af
|
|
|
|
push bc
|
|
|
|
push de
|
|
|
|
|
|
|
|
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:
|
|
|
|
ld a, 4
|
|
|
|
call addDE
|
|
|
|
ex hl, de
|
|
|
|
call jumpHL
|
|
|
|
ex hl, de
|
|
|
|
|
|
|
|
.end:
|
|
|
|
pop de
|
|
|
|
pop bc
|
|
|
|
pop af
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Print the error code set in A (doesn't work for codes > 9 yet...)
|
|
|
|
shellPrintErr:
|
|
|
|
push af
|
|
|
|
push hl
|
|
|
|
|
|
|
|
ld hl, .str
|
2019-04-14 06:01:20 +10:00
|
|
|
call printstr
|
2019-04-14 12:39:28 +10:00
|
|
|
|
|
|
|
; ascii for '0' is 0x30
|
|
|
|
add a, 0x30
|
|
|
|
call aciaPutC
|
2019-04-14 06:01:20 +10:00
|
|
|
call printcrlf
|
2019-04-14 12:39:28 +10:00
|
|
|
|
|
|
|
pop hl
|
|
|
|
pop af
|
|
|
|
ret
|
|
|
|
|
|
|
|
.str:
|
|
|
|
.db "ERR ", 0
|
|
|
|
|
|
|
|
; *** COMMANDS ***
|
|
|
|
shellSeek:
|
|
|
|
ld hl, .str
|
|
|
|
call printstr
|
2019-04-14 06:01:20 +10:00
|
|
|
ret
|
2019-04-14 12:39:28 +10:00
|
|
|
.str:
|
|
|
|
.db "seek called", CR, LF, 0
|
|
|
|
|
|
|
|
shellPeek:
|
|
|
|
ld hl, .str
|
|
|
|
call printstr
|
|
|
|
ret
|
|
|
|
.str:
|
|
|
|
.db "peek called", CR, LF, 0
|
|
|
|
|
|
|
|
; Format: 4 bytes name followed by 2 bytes jump. fill names with zeroes
|
|
|
|
shellCmdTbl:
|
|
|
|
.db "seek"
|
|
|
|
jr shellSeek
|
|
|
|
.db "peek"
|
|
|
|
jr shellPeek
|
2019-04-14 06:01:20 +10:00
|
|
|
|