From e01ee170cbf1bd5058598fa26f753f6f7e6bcf6d Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Sat, 13 Jul 2019 09:19:01 -0400 Subject: [PATCH] stdio: add stdioReadC A routine to conveniently read lines from TTY. Extracted from shell. Will be used in other places. --- kernel/shell.asm | 84 +++-------------------------------- kernel/stdio.asm | 111 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 114 insertions(+), 81 deletions(-) diff --git a/kernel/shell.asm b/kernel/shell.asm index b27a83d..dd597b0 100644 --- a/kernel/shell.asm +++ b/kernel/shell.asm @@ -35,10 +35,6 @@ ; number of entries in shellCmdTbl .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 *** ; Memory address that the shell is currently "pointing at" for peek, load, call ; operations. Set with mptr. @@ -48,13 +44,8 @@ ; written to after parsing. .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 -.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 .equ SHELL_LOOPHOOK SHELL_CMDHOOK+2 @@ -65,14 +56,13 @@ shellInit: xor a ld (SHELL_MEM_PTR), a ld (SHELL_MEM_PTR+1), a - ld (SHELL_BUF), a ld hl, noop ld (SHELL_CMDHOOK), hl ld (SHELL_LOOPHOOK), hl ; print welcome ld hl, .welcome - jp printstr ; returns + jp printstr .welcome: .db "Collapse OS", ASCII_CR, ASCII_LF, "> ", 0 @@ -84,52 +74,12 @@ shellLoop: ld ix, (SHELL_LOOPHOOK) call callIX ; 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 - ld hl, SHELL_BUF + call stdioGetLine call shellParse - ; empty our buffer by writing a zero to its first char - xor a - ld (hl), a - ld hl, .prompt call printstr jr shellLoop @@ -137,28 +87,6 @@ shellLoop: .prompt: .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 shellParse: push af diff --git a/kernel/stdio.asm b/kernel/stdio.asm index 8b79d27..9189265 100644 --- a/kernel/stdio.asm +++ b/kernel/stdio.asm @@ -1,20 +1,36 @@ ; 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 ; 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. .equ STDIO_HEX_FMT STDIO_RAMSTART .equ STDIO_GETC STDIO_HEX_FMT+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. stdioInit: ld (STDIO_GETC), hl ld (STDIO_PUTC), de + xor a + ld (STDIO_BUF), a + ld (STDIO_BUFIDX), a ret stdioGetC: @@ -88,3 +104,92 @@ printHexPair: call printHex 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. +; +; 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