A routine to conveniently read lines from TTY. Extracted from shell. Will be used in other places.pull/10/head
@@ -35,10 +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 | ||||
; Size of the shell command buffer. If a typed command reaches this size, the | |||||
; command is flushed immediately (same as pressing return). | |||||
.equ SHELL_BUFSIZE 0x20 | |||||
; *** VARIABLES *** | ; *** VARIABLES *** | ||||
; Memory address that the shell is currently "pointing at" for peek, load, call | ; Memory address that the shell is currently "pointing at" for peek, load, call | ||||
; operations. Set with mptr. | ; operations. Set with mptr. | ||||
@@ -48,13 +44,8 @@ | |||||
; written to after parsing. | ; written to after parsing. | ||||
.equ SHELL_CMD_ARGS SHELL_MEM_PTR+2 | .equ SHELL_CMD_ARGS SHELL_MEM_PTR+2 | ||||
; 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 | |||||
; for the null-termination every time we write to it. Simpler that way. | |||||
.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_CMD_ARGS+PARSE_ARG_MAXCOUNT | |||||
; Pointer to a routine to call at each shell loop interation | ; Pointer to a routine to call at each shell loop interation | ||||
.equ SHELL_LOOPHOOK SHELL_CMDHOOK+2 | .equ SHELL_LOOPHOOK SHELL_CMDHOOK+2 | ||||
@@ -65,14 +56,13 @@ shellInit: | |||||
xor a | xor a | ||||
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 hl, noop | ld hl, noop | ||||
ld (SHELL_CMDHOOK), hl | ld (SHELL_CMDHOOK), hl | ||||
ld (SHELL_LOOPHOOK), hl | ld (SHELL_LOOPHOOK), hl | ||||
; print welcome | ; print welcome | ||||
ld hl, .welcome | ld hl, .welcome | ||||
jp printstr ; returns | |||||
jp printstr | |||||
.welcome: | .welcome: | ||||
.db "Collapse OS", ASCII_CR, ASCII_LF, "> ", 0 | .db "Collapse OS", ASCII_CR, ASCII_LF, "> ", 0 | ||||
@@ -84,52 +74,12 @@ shellLoop: | |||||
ld ix, (SHELL_LOOPHOOK) | ld ix, (SHELL_LOOPHOOK) | ||||
call callIX | call callIX | ||||
; Then, let's wait until something is typed. | ; Then, let's wait until something is typed. | ||||
call stdioGetC | |||||
jr nz, shellLoop ; nothing typed? loop | |||||
; got it. Now, is it a CR or LF? | |||||
cp ASCII_CR | |||||
jr z, .do ; char is CR? do! | |||||
cp ASCII_LF | |||||
jr z, .do ; char is LF? do! | |||||
cp ASCII_DEL | |||||
jr z, .delchr | |||||
cp ASCII_BS | |||||
jr z, .delchr | |||||
; Echo the received character right away so that we see what we type | |||||
call stdioPutC | |||||
; Ok, gotta add it do the buffer | |||||
; save char for later | |||||
ex af, af' | |||||
ld hl, SHELL_BUF | |||||
xor a ; look for null | |||||
call findchar ; HL points to where we need to write | |||||
; A is the number of chars in the buf | |||||
cp SHELL_BUFSIZE-1 ; -1 is because we always want to keep our | |||||
; last char at zero. | |||||
jr z, .do ; end of buffer reached? buffer is full. do! | |||||
; bring the char back in A | |||||
ex af, af' | |||||
; Buffer not full, not CR or LF. Let's put that char in our buffer and | |||||
; read again. | |||||
ld (hl), a | |||||
; Now, write a zero to the next byte to properly terminate our string. | |||||
inc hl | |||||
xor a | |||||
ld (hl), a | |||||
jr shellLoop | |||||
.do: | |||||
call stdioReadC | |||||
jr nz, shellLoop ; not done? loop | |||||
; We're done. Process line. | |||||
call printcrlf | call printcrlf | ||||
ld hl, SHELL_BUF | |||||
call stdioGetLine | |||||
call shellParse | call shellParse | ||||
; empty our buffer by writing a zero to its first char | |||||
xor a | |||||
ld (hl), a | |||||
ld hl, .prompt | ld hl, .prompt | ||||
call printstr | call printstr | ||||
jr shellLoop | jr shellLoop | ||||
@@ -137,28 +87,6 @@ shellLoop: | |||||
.prompt: | .prompt: | ||||
.db "> ", 0 | .db "> ", 0 | ||||
.delchr: | |||||
ld hl, SHELL_BUF | |||||
ld a, (hl) | |||||
or a ; cp 0 | |||||
jr z, shellLoop ; buffer empty? nothing to do | |||||
; buffer not empty, let's delete | |||||
xor a ; look for null | |||||
call findchar ; HL points to end of buf | |||||
dec hl ; the char before it | |||||
xor a | |||||
ld (hl), a ; set to zero | |||||
; Char deleted in buffer, now send BS + space + BS for the terminal | |||||
; to clear its previous char | |||||
ld a, ASCII_BS | |||||
call stdioPutC | |||||
ld a, ' ' | |||||
call stdioPutC | |||||
ld a, ASCII_BS | |||||
call stdioPutC | |||||
jr shellLoop | |||||
; Parse command (null terminated) at HL and calls it | ; Parse command (null terminated) at HL and calls it | ||||
shellParse: | shellParse: | ||||
push af | push af | ||||
@@ -1,20 +1,36 @@ | |||||
; stdio | ; stdio | ||||
; | ; | ||||
; Allows other modules to print to "standard out", and get data from "stamdard | |||||
; Allows other modules to print to "standard out", and get data from "standard | |||||
; in", that is, the console through which the user is connected in a decoupled | ; in", that is, the console through which the user is connected in a decoupled | ||||
; manner. | ; manner. | ||||
; | ; | ||||
; *** VARIABLES *** | |||||
; *** Consts *** | |||||
; Size of the readline buffer. If a typed line reaches this size, the line is | |||||
; flushed immediately (same as pressing return). | |||||
.equ STDIO_BUFSIZE 0x20 | |||||
; *** Variables *** | |||||
; Used to store formatted hex values just before printing it. | ; Used to store formatted hex values just before printing it. | ||||
.equ STDIO_HEX_FMT STDIO_RAMSTART | .equ STDIO_HEX_FMT STDIO_RAMSTART | ||||
.equ STDIO_GETC STDIO_HEX_FMT+2 | .equ STDIO_GETC STDIO_HEX_FMT+2 | ||||
.equ STDIO_PUTC STDIO_GETC+2 | .equ STDIO_PUTC STDIO_GETC+2 | ||||
.equ STDIO_RAMEND STDIO_PUTC+2 | |||||
; Line 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 | |||||
; for the null-termination every time we write to it. Simpler that way. | |||||
.equ STDIO_BUF STDIO_PUTC+2 | |||||
; Index where the next char will go in stdioGetC. | |||||
.equ STDIO_BUFIDX STDIO_BUF+STDIO_BUFSIZE | |||||
.equ STDIO_RAMEND STDIO_BUFIDX+1 | |||||
; Sets GetC to the routine where HL points to and PutC to DE. | ; Sets GetC to the routine where HL points to and PutC to DE. | ||||
stdioInit: | stdioInit: | ||||
ld (STDIO_GETC), hl | ld (STDIO_GETC), hl | ||||
ld (STDIO_PUTC), de | ld (STDIO_PUTC), de | ||||
xor a | |||||
ld (STDIO_BUF), a | |||||
ld (STDIO_BUFIDX), a | |||||
ret | ret | ||||
stdioGetC: | stdioGetC: | ||||
@@ -88,3 +104,92 @@ printHexPair: | |||||
call printHex | call printHex | ||||
pop af | pop af | ||||
ret | ret | ||||
; Call stdioGetC and put the result in the buffer. Sets Z according to whether | |||||
; the buffer is "complete", that is, whether CR or LF have been pressed or if | |||||
; the the buffer is full. Z is set if the line is "complete", unset if not. | |||||
; The next call to stdioReadC after a completed line will start a new line. | |||||
; | |||||
; This routine also takes care of echoing received characters back to the TTY. | |||||
; | |||||
; Note that this routine doesn't bother returning the typed character. | |||||
stdioReadC: | |||||
; Let's wait until something is typed. | |||||
call stdioGetC | |||||
jr nz, stdioReadC ; nothing typed? loop | |||||
; got it. Now, is it a CR or LF? | |||||
cp ASCII_CR | |||||
jr z, .complete ; char is CR? buffer complete! | |||||
cp ASCII_LF | |||||
jr z, .complete | |||||
cp ASCII_DEL | |||||
jr z, .delchr | |||||
cp ASCII_BS | |||||
jr z, .delchr | |||||
; Echo the received character right away so that we see what we type | |||||
call stdioPutC | |||||
; Ok, gotta add it do the buffer | |||||
; save char for later | |||||
ex af, af' | |||||
ld a, (STDIO_BUFIDX) | |||||
push hl ;<| | |||||
ld hl, STDIO_BUF ; | | |||||
; make HL point to dest spot | | |||||
call addHL ; | | |||||
; Write our char down | | |||||
ex af, af' ; | | |||||
ld (hl), a ; | | |||||
; follow up with a null char | | |||||
inc hl ; | | |||||
xor a ; | | |||||
ld (hl), a ; | | |||||
pop hl ;<| | |||||
; inc idx, which still is in AF' | |||||
ex af, af' | |||||
inc a | |||||
cp STDIO_BUFSIZE-1 ; -1 is because we always want to keep our | |||||
; last char at zero. | |||||
jr z, .complete ; end of buffer reached? buffer is full. | |||||
; not complete. save idx back | |||||
ld (STDIO_BUFIDX), a | |||||
; Z already unset | |||||
ret | |||||
.complete: | |||||
; The line in our buffer is complete. | |||||
xor a ; sets Z | |||||
ld (STDIO_BUFIDX), a | |||||
ret | |||||
.delchr: | |||||
ld a, (STDIO_BUFIDX) | |||||
or a | |||||
jp z, unsetZ ; buf empty? nothing to do | |||||
; buffer not empty, let's go back one char and set a null char there. | |||||
dec a | |||||
ld (STDIO_BUFIDX), a | |||||
push hl ;<| | |||||
ld hl, STDIO_BUF ; | | |||||
; make HL point to dest spot | | |||||
call addHL ; | | |||||
xor a ; | | |||||
ld (hl), a ; | | |||||
pop hl ;<| | |||||
; Char deleted in buffer, now send BS + space + BS for the terminal | |||||
; to clear its previous char | |||||
ld a, ASCII_BS | |||||
call stdioPutC | |||||
ld a, ' ' | |||||
call stdioPutC | |||||
ld a, ASCII_BS | |||||
call stdioPutC | |||||
jp unsetZ | |||||
; Make HL point to the line buffer. It is always null terminated. | |||||
stdioGetLine: | |||||
ld hl, STDIO_BUF | |||||
ret |