From 2a513e6f571f7b66ba8649f6079a28f404229e07 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Sun, 3 Nov 2019 20:32:27 -0500 Subject: [PATCH] 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. --- kernel/shell.asm | 19 +++++--- kernel/stdio.asm | 123 ++++++++++++++--------------------------------- 2 files changed, 50 insertions(+), 92 deletions(-) diff --git a/kernel/shell.asm b/kernel/shell.asm index 4c3ad56..60d8729 100644 --- a/kernel/shell.asm +++ b/kernel/shell.asm @@ -35,6 +35,10 @@ ; number of entries in shellCmdTbl .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 *** ; Memory address that the shell is currently "pointing at" for peek, load, call ; operations. Set with mptr. @@ -67,12 +71,8 @@ shellInit: ; 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. shellLoop: - ; Let's wait until a line is typed. - call stdioReadC - jr nz, shellLoop ; not done? loop - ; We're done. Process line. + call stdioReadLine call printcrlf - call stdioGetLine call shellParse ld hl, .prompt call printstr @@ -86,7 +86,7 @@ shellParse: ; first thing: is command empty? ld a, (hl) or a - ret z ; empty, nthing to do + ret z ; empty, nothing to do push af push bc @@ -104,6 +104,13 @@ shellParse: ; 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. 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: xor a ld (hl), a diff --git a/kernel/stdio.asm b/kernel/stdio.asm index 82f58f3..f408802 100644 --- a/kernel/stdio.asm +++ b/kernel/stdio.asm @@ -4,6 +4,13 @@ ; in", that is, the console through which the user is connected in a decoupled ; 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 *** ; Size of the readline buffer. If a typed line reaches this size, the line is ; flushed immediately (same as pressing return). @@ -12,25 +19,20 @@ ; *** Variables *** ; Used to store formatted hex values just before printing it. .equ STDIO_HEX_FMT STDIO_RAMSTART -.equ STDIO_GETC STDIO_HEX_FMT+2 -.equ STDIO_PUTC STDIO_GETC+2 +.equ STDIO_GETC @+2 +.equ 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 +; This buffer is null-terminated. +.equ STDIO_BUF @+2 ; Index where the next char will go in stdioGetC. -.equ STDIO_BUFIDX STDIO_BUF+STDIO_BUFSIZE -.equ STDIO_RAMEND STDIO_BUFIDX+1 +.equ STDIO_RAMEND @+STDIO_BUFSIZE ; Sets GetC to the routine where HL points to and PutC to DE. stdioInit: ld (STDIO_GETC), hl ld (STDIO_PUTC), de - xor a - ld (STDIO_BUF), a - ld (STDIO_BUFIDX), a ret stdioGetC: @@ -105,21 +107,19 @@ printHexPair: pop af 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. +; Repeatedly calls stdioGetC until a whole line was read, that is, when CR or +; LF is read or if the buffer is full. Sets HL to the beginning of the read +; line, which is null-terminated. ; ; This routine also takes care of echoing received characters back to the TTY. -; -; This routine doesn't wait after a typed char. If nothing is typed, we return -; immediately with Z flag unset. -; -; Note that this routine doesn't bother returning the typed character. -stdioReadC: +; It also manages backspaces properly. +stdioReadLine: + push bc + ld hl, STDIO_BUF + ld b, STDIO_BUFSIZE-1 +.loop: ; Let's wait until something is typed. call stdioGetC - ret nz ; nothing typed? nothing to do ; got it. Now, is it a CR or LF? cp ASCII_CR jr z, .complete ; char is CR? buffer complete! @@ -134,62 +134,28 @@ stdioReadC: call stdioPutC ; 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 - ; follow up with a null char inc hl - xor a - ld (hl), a - 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 - + djnz .loop + ; buffer overflow, complete line .complete: ; The line in our buffer is complete. - ; But before we do that, let's take care of a special case: the empty - ; line. If we didn't add any character since the last "complete", then - ; our buffer's content is the content from the last time. Let's set this - ; to an empty string. - ld a, (STDIO_BUFIDX) - or a - jr nz, .completeSkip - ld (STDIO_BUF), a -.completeSkip: - xor a ; sets Z - ld (STDIO_BUFIDX), a + ; Let's null-terminate it and return. + xor a + ld (hl), a + ld hl, STDIO_BUF + pop bc 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 ;<| + ; Deleting is a tricky business. We have to decrease HL and increase B + ; so that everything stays consistent. We also have to make sure that + ; We don't do buffer underflows. + ld a, b + cp STDIO_BUFSIZE-1 + jr z, .loop ; beginning of line, nothing to delete + dec hl + inc b ; Char deleted in buffer, now send BS + space + BS for the terminal ; to clear its previous char ld a, ASCII_BS @@ -198,19 +164,4 @@ stdioReadC: 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 - -; 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 - + jr .loop