; stdio ; ; 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. ; ; *** 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 ; 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: ld ix, (STDIO_GETC) jp (ix) stdioPutC: ld ix, (STDIO_PUTC) jp (ix) ; print null-terminated string pointed to by HL printstr: push af push hl .loop: ld a, (hl) ; load character to send or a ; is it zero? jr z, .end ; if yes, we're finished call stdioPutC inc hl jr .loop .end: pop hl pop af ret ; print B characters from string that HL points to printnstr: push bc push hl .loop: ld a, (hl) ; load character to send call stdioPutC inc hl djnz .loop .end: pop hl pop bc ret printcrlf: push af ld a, ASCII_CR call stdioPutC ld a, ASCII_LF call stdioPutC pop af ret ; Print the hex char in A printHex: push bc push hl ld hl, STDIO_HEX_FMT call fmtHexPair ld b, 2 call printnstr pop hl pop bc ret ; Print the hex pair in HL printHexPair: push af ld a, h call printHex ld a, l 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. ; ; 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: ; 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! 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 ; --> 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 .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 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 ; 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