; 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.
;
; Those GetC/PutC routines are hooked through defines and have this API:
;
; GetC: Blocks until a character is read from the device and return that
;       character in A.
;
; PutC: Write character specified in A onto the device.
;
; *** Accepted characters ***
;
; For now, we're in muddy waters in this regard. We try to stay close to ASCII.
; Anything over 0x7f is undefined. Both CR and LF are interpreted as "line end".
; Both BS and DEL mean "delete previous character".
;
; When outputting, newlines are marked by CR and LF. Outputting a character
; deletion is made through BS then space then BS.
;
; *** Defines ***
; STDIO_GETC: address of a GetC routine
; STDIO_PUTC: address of a PutC routine
;
; *** 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		0x40

; *** Variables ***
; Line buffer. We read types chars into this buffer until return is pressed
; This buffer is null-terminated.
.equ	STDIO_BUF	STDIO_RAMSTART

; Index where the next char will go in stdioGetC.
.equ	STDIO_RAMEND	@+STDIO_BUFSIZE

stdioGetC:
	jp	STDIO_GETC

stdioPutC:
	jp	STDIO_PUTC

; 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	STDIO_PUTC
	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	STDIO_PUTC
	inc	hl
	djnz	.loop

.end:
	pop	hl
	pop	bc
	ret

printcrlf:
	push	af
	ld	a, CR
	call	STDIO_PUTC
	ld	a, LF
	call	STDIO_PUTC
	pop	af
	ret

; 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.
; 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	STDIO_GETC
	; got it. Now, is it a CR or LF?
	cp	CR
	jr	z, .complete	; char is CR? buffer complete!
	cp	LF
	jr	z, .complete
	cp	DEL
	jr	z, .delchr
	cp	BS
	jr	z, .delchr

	; Echo the received character right away so that we see what we type
	call	STDIO_PUTC

	; Ok, gotta add it do the buffer
	ld	(hl), a
	inc	hl
	djnz	.loop
	; buffer overflow, complete line
.complete:
	; The line in our buffer is complete.
	; Let's null-terminate it and return.
	xor	a
	ld	(hl), a
	ld	hl, STDIO_BUF
	pop	bc
	ret

.delchr:
	; 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, BS
	call	STDIO_PUTC
	ld	a, ' '
	call	STDIO_PUTC
	ld	a, BS
	call	STDIO_PUTC
	jr	.loop