stdio: make stdioGetC and stdioReadline blocking
ref #64. Also, fix a bug in the shell where it would write outside the buffer's bounds when given a completely filled buffer without a space character in it.
This commit is contained in:
parent
16bf8e28c0
commit
2a513e6f57
@ -35,6 +35,10 @@
|
|||||||
; 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 length for shell commands. Should be confortably below stdio's
|
||||||
|
; readline buffer length.
|
||||||
|
.equ SHELL_MAX_CMD_LEN 0x10
|
||||||
|
|
||||||
; *** 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.
|
||||||
@ -67,12 +71,8 @@ shellInit:
|
|||||||
; Inifite loop that processes input. Because it's infinite, you should jump
|
; Inifite loop that processes input. Because it's infinite, you should jump
|
||||||
; to it rather than call it. Saves two precious bytes in the stack.
|
; to it rather than call it. Saves two precious bytes in the stack.
|
||||||
shellLoop:
|
shellLoop:
|
||||||
; Let's wait until a line is typed.
|
call stdioReadLine
|
||||||
call stdioReadC
|
|
||||||
jr nz, shellLoop ; not done? loop
|
|
||||||
; We're done. Process line.
|
|
||||||
call printcrlf
|
call printcrlf
|
||||||
call stdioGetLine
|
|
||||||
call shellParse
|
call shellParse
|
||||||
ld hl, .prompt
|
ld hl, .prompt
|
||||||
call printstr
|
call printstr
|
||||||
@ -86,7 +86,7 @@ shellParse:
|
|||||||
; first thing: is command empty?
|
; first thing: is command empty?
|
||||||
ld a, (hl)
|
ld a, (hl)
|
||||||
or a
|
or a
|
||||||
ret z ; empty, nthing to do
|
ret z ; empty, nothing to do
|
||||||
|
|
||||||
push af
|
push af
|
||||||
push bc
|
push bc
|
||||||
@ -104,6 +104,13 @@ shellParse:
|
|||||||
; no arg, (HL) is zero to facilitate processing later, add a second
|
; no arg, (HL) is zero to facilitate processing later, add a second
|
||||||
; null next to that one to indicate unambiguously that we have no args.
|
; null next to that one to indicate unambiguously that we have no args.
|
||||||
inc hl
|
inc hl
|
||||||
|
; Oh wait, before we proceed, is our cmd length within limits? cmd len
|
||||||
|
; is currently in A from findchar
|
||||||
|
cp SHELL_MAX_CMD_LEN
|
||||||
|
jr c, .hasArgs ; within limits
|
||||||
|
; outside limits
|
||||||
|
ld a, SHELL_ERR_UNKNOWN_CMD
|
||||||
|
jr .error
|
||||||
.hasArgs:
|
.hasArgs:
|
||||||
xor a
|
xor a
|
||||||
ld (hl), a
|
ld (hl), a
|
||||||
|
123
kernel/stdio.asm
123
kernel/stdio.asm
@ -4,6 +4,13 @@
|
|||||||
; 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.
|
||||||
;
|
;
|
||||||
|
; Those GetC/PutC routines are hooked in during stdioInit and have this API:
|
||||||
|
;
|
||||||
|
; GetC: Blocks until a character is read from the device and return that
|
||||||
|
; character in A.
|
||||||
|
;
|
||||||
|
; PutC: Write character specified onto the device.
|
||||||
|
;
|
||||||
; *** Consts ***
|
; *** Consts ***
|
||||||
; Size of the readline buffer. If a typed line reaches this size, the line is
|
; Size of the readline buffer. If a typed line reaches this size, the line is
|
||||||
; flushed immediately (same as pressing return).
|
; flushed immediately (same as pressing return).
|
||||||
@ -12,25 +19,20 @@
|
|||||||
; *** Variables ***
|
; *** 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 @+2
|
||||||
.equ STDIO_PUTC STDIO_GETC+2
|
.equ STDIO_PUTC @+2
|
||||||
|
|
||||||
; Line buffer. We read types chars into this buffer until return is pressed
|
; 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
|
; This buffer is null-terminated.
|
||||||
; for the null-termination every time we write to it. Simpler that way.
|
.equ STDIO_BUF @+2
|
||||||
.equ STDIO_BUF STDIO_PUTC+2
|
|
||||||
|
|
||||||
; Index where the next char will go in stdioGetC.
|
; Index where the next char will go in stdioGetC.
|
||||||
.equ STDIO_BUFIDX STDIO_BUF+STDIO_BUFSIZE
|
.equ STDIO_RAMEND @+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:
|
||||||
@ -105,21 +107,19 @@ printHexPair:
|
|||||||
pop af
|
pop af
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; Call stdioGetC and put the result in the buffer. Sets Z according to whether
|
; Repeatedly calls stdioGetC until a whole line was read, that is, when CR or
|
||||||
; the buffer is "complete", that is, whether CR or LF have been pressed or if
|
; LF is read or if the buffer is full. Sets HL to the beginning of the read
|
||||||
; the the buffer is full. Z is set if the line is "complete", unset if not.
|
; line, which is null-terminated.
|
||||||
; 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.
|
; This routine also takes care of echoing received characters back to the TTY.
|
||||||
;
|
; It also manages backspaces properly.
|
||||||
; This routine doesn't wait after a typed char. If nothing is typed, we return
|
stdioReadLine:
|
||||||
; immediately with Z flag unset.
|
push bc
|
||||||
;
|
ld hl, STDIO_BUF
|
||||||
; Note that this routine doesn't bother returning the typed character.
|
ld b, STDIO_BUFSIZE-1
|
||||||
stdioReadC:
|
.loop:
|
||||||
; Let's wait until something is typed.
|
; Let's wait until something is typed.
|
||||||
call stdioGetC
|
call stdioGetC
|
||||||
ret nz ; nothing typed? nothing to do
|
|
||||||
; got it. Now, is it a CR or LF?
|
; got it. Now, is it a CR or LF?
|
||||||
cp ASCII_CR
|
cp ASCII_CR
|
||||||
jr z, .complete ; char is CR? buffer complete!
|
jr z, .complete ; char is CR? buffer complete!
|
||||||
@ -134,62 +134,28 @@ stdioReadC:
|
|||||||
call stdioPutC
|
call stdioPutC
|
||||||
|
|
||||||
; Ok, gotta add it do the buffer
|
; Ok, gotta add it do the buffer
|
||||||
; save char for later
|
|
||||||
ex af, af'
|
|
||||||
ld a, (STDIO_BUFIDX)
|
|
||||||
push hl ; --> lvl 1
|
|
||||||
ld hl, STDIO_BUF
|
|
||||||
; make HL point to dest spot
|
|
||||||
call addHL
|
|
||||||
; Write our char down
|
|
||||||
ex af, af'
|
|
||||||
ld (hl), a
|
ld (hl), a
|
||||||
; follow up with a null char
|
|
||||||
inc hl
|
inc hl
|
||||||
xor a
|
djnz .loop
|
||||||
ld (hl), a
|
; buffer overflow, complete line
|
||||||
pop hl ; <-- lvl 1
|
|
||||||
; 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:
|
.complete:
|
||||||
; The line in our buffer is complete.
|
; The line in our buffer is complete.
|
||||||
; But before we do that, let's take care of a special case: the empty
|
; Let's null-terminate it and return.
|
||||||
; line. If we didn't add any character since the last "complete", then
|
xor a
|
||||||
; our buffer's content is the content from the last time. Let's set this
|
ld (hl), a
|
||||||
; to an empty string.
|
ld hl, STDIO_BUF
|
||||||
ld a, (STDIO_BUFIDX)
|
pop bc
|
||||||
or a
|
|
||||||
jr nz, .completeSkip
|
|
||||||
ld (STDIO_BUF), a
|
|
||||||
.completeSkip:
|
|
||||||
xor a ; sets Z
|
|
||||||
ld (STDIO_BUFIDX), a
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.delchr:
|
.delchr:
|
||||||
ld a, (STDIO_BUFIDX)
|
; Deleting is a tricky business. We have to decrease HL and increase B
|
||||||
or a
|
; so that everything stays consistent. We also have to make sure that
|
||||||
jp z, unsetZ ; buf empty? nothing to do
|
; We don't do buffer underflows.
|
||||||
; buffer not empty, let's go back one char and set a null char there.
|
ld a, b
|
||||||
dec a
|
cp STDIO_BUFSIZE-1
|
||||||
ld (STDIO_BUFIDX), a
|
jr z, .loop ; beginning of line, nothing to delete
|
||||||
push hl ;<|
|
dec hl
|
||||||
ld hl, STDIO_BUF ; |
|
inc b
|
||||||
; 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
|
; Char deleted in buffer, now send BS + space + BS for the terminal
|
||||||
; to clear its previous char
|
; to clear its previous char
|
||||||
ld a, ASCII_BS
|
ld a, ASCII_BS
|
||||||
@ -198,19 +164,4 @@ stdioReadC:
|
|||||||
call stdioPutC
|
call stdioPutC
|
||||||
ld a, ASCII_BS
|
ld a, ASCII_BS
|
||||||
call stdioPutC
|
call stdioPutC
|
||||||
jp unsetZ
|
jr .loop
|
||||||
|
|
||||||
|
|
||||||
; Make HL point to the line buffer. It is always null terminated.
|
|
||||||
stdioGetLine:
|
|
||||||
ld hl, STDIO_BUF
|
|
||||||
ret
|
|
||||||
|
|
||||||
; Repeatedly call stdioReadC until Z is set, then make HL point to the read
|
|
||||||
; buffer.
|
|
||||||
stdioReadLine:
|
|
||||||
call stdioReadC
|
|
||||||
jr nz, stdioReadLine
|
|
||||||
ld hl, STDIO_BUF
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user