|
|
@@ -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 |