Separate shell and acia input buffers

They serve a different purpose. The goal of the ACIA buffer is to ensure
that we don't miss an input. The goal of the shell buffer is to wait
until the user presses return.

The ACIA buffer has been moved to shell and replaced with a circular
buffer, a more appropriate data structure for this kind of purpose.

Also, introduce `aciaGetC`.
This commit is contained in:
Virgil Dupras 2019-04-14 13:56:04 -04:00
parent 902c6a5dd3
commit 8ccddbcb0e
3 changed files with 123 additions and 49 deletions

View File

@ -14,23 +14,29 @@
; RAM.
; *** CONSTS ***
; size of the input buffer. If our input goes over this size, we echo
; immediately.
; size of the input buffer. If our input goes over this size, we start losing
; data.
ACIA_BUFSIZE .equ 0x20
; *** VARIABLES ***
; Our input buffer starts there
; Our input buffer starts there. This is a circular buffer.
ACIA_BUF .equ ACIA_RAMSTART
; index, in the buffer, where our next character will go. 0 when the buffer is
; empty, BUFSIZE-1 when it's almost full.
ACIA_BUFIDX .equ ACIA_BUF+ACIA_BUFSIZE
ACIA_RAMEND .equ ACIA_BUFIDX+1
; The "read" index of the circular buffer. It points to where the next char
; should be read. If rd == wr, the buffer is empty. Not touched by the
; interrupt.
ACIA_BUFRDIDX .equ ACIA_BUF+ACIA_BUFSIZE
; The "write" index of the circular buffer. Points to where the next char
; should be written. Should only be touched by the interrupt. if wr == rd-1,
; the interrupt will *not* write in the buffer until some space has been freed.
ACIA_BUFWRIDX .equ ACIA_BUFRDIDX+1
ACIA_RAMEND .equ ACIA_BUFWRIDX+1
aciaInit:
; initialize variables
xor a
ld (ACIA_BUFIDX), a ; starts at 0
ld (ACIA_BUFRDIDX), a ; starts at 0
ld (ACIA_BUFWRIDX), a
; setup ACIA
; CR7 (1) - Receive Interrupt enabled
@ -41,6 +47,16 @@ aciaInit:
out (ACIA_CTL), a
ret
; Increase the circular buffer index in A, properly considering overflow.
; returns value in A.
aciaIncIndex:
inc a
cp ACIA_BUFSIZE
ret nz ; not equal? nothing to do
; equal? reset
xor a
ret
; read char in the ACIA and put it in the read buffer
aciaInt:
push af
@ -51,18 +67,27 @@ aciaInt:
bit 0, a ; is our ACIA rcv buffer full?
jr z, .end ; no? a interrupt was triggered for nothing.
call aciaBufPtr ; HL set, A set
; is our input buffer full? If yes, we don't read anything. Something
; is wrong: we don't process data fast enough.
cp ACIA_BUFSIZE
jr z, .end ; if BUFIDX == BUFSIZE, our buffer is full.
; Load both read and write indexes so we can compare them. To do so, we
; perform a "fake" read increase and see if it brings it to the same
; value as the write index.
ld a, (ACIA_BUFRDIDX)
call aciaIncIndex
ld l, a
ld a, (ACIA_BUFWRIDX)
cp l
jr z, .end ; Equal? buffer is full
; Alrighty, buffer not full. let's write.
ld de, ACIA_BUF
; A already contains our write index, add it to DE
call addDE
; increase our buf ptr while we still have it in A
inc a
ld (ACIA_BUFIDX), a
call aciaIncIndex
ld (ACIA_BUFWRIDX), a
; And finially, fetch the value and write it.
in a, (ACIA_IO)
ld (hl), a
ld (de), a
.end:
pop hl
@ -70,18 +95,30 @@ aciaInt:
ei
reti
; Set current buffer pointer in HL. The buffer pointer is where our *next* char
; will be written. A is set to the value of (BUFIDX)
aciaBufPtr:
push bc
; Read a character from the input buffer. If the buffer is empty, loop until
; there something to fetch. Returns value in A.
aciaGetC:
push de
ld a, (ACIA_BUFIDX)
ld hl, ACIA_BUF
xor b
ld c, a
add hl, bc ; hl now points to INPTBUF + BUFIDX
.loop:
ld a, (ACIA_BUFWRIDX)
ld e, a
ld a, (ACIA_BUFRDIDX)
cp e
jr z, .loop ; equal? buffer empty, wait.
pop bc
; Alrighty, buffer not empty. let's read.
ld de, ACIA_BUF
; A already contains our read index, add it to DE
call addDE
; increase our buf ptr while we still have it in A
call aciaIncIndex
ld (ACIA_BUFRDIDX), a
; And finially, fetch the value.
ld a, (de)
pop de
ret
; spits character in A in port SER_OUT

View File

@ -18,6 +18,26 @@ addDE:
ld e, a
ret
; Increase HL until the memory address it points to is null for a maximum of
; 0xff bytes. Returns the new HL value as well as the number of bytes iterated
; in A.
findnull:
push bc
ld a, 0xff
ld b, a
.loop: ld a, (hl)
cp 0
jr z, .end
inc hl
djnz .loop
.end:
; We ran 0xff-B loops. That's the result that goes in A.
ld a, 0xff
sub a, b
pop bc
ret
; Format the lower nibble of A into a hex char and stores the result in A.
fmtHex:
and a, 0xf
@ -191,3 +211,4 @@ upcase:
; 'a' - 'A' == 0x20
sub 0x20
ret

View File

@ -26,18 +26,29 @@ SHELL_ERR_UNKNOWN_CMD .equ 0x01
; Arguments for the command weren't properly formatted
SHELL_ERR_BAD_ARGS .equ 0x02
; Size of the shell command buffer. If a typed command reaches this size, the
; command is flushed immediately (same as pressing return).
SHELL_BUFSIZE .equ 0x20
; *** VARIABLES ***
; Memory address that the shell is currently "pointing at" for peek and deek
; operations. Set with seek.
SHELL_MEM_PTR .equ SHELL_RAMSTART
; Used to store formatted hex values just before printing it.
SHELL_HEX_FMT .equ SHELL_MEM_PTR+2
SHELL_RAMEND .equ SHELL_HEX_FMT+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.
SHELL_BUF .equ SHELL_HEX_FMT+2
SHELL_RAMEND .equ SHELL_BUF+SHELL_BUFSIZE
; *** CODE ***
shellInit:
xor a
ld (SHELL_MEM_PTR), a
ld (SHELL_BUF), a
; print prompt
ld hl, .prompt
@ -49,37 +60,42 @@ shellInit:
.db "Collapse OS", 0
shellLoop:
; check if the input buffer is full or ends in CR or LF. If it does,
; prints it back and empty it.
call aciaBufPtr
cp 0
jr z, shellLoop ; BUFIDX is zero? nothing to check.
cp ACIA_BUFSIZE
jr z, .do ; if BUFIDX == BUFSIZE? do!
; our previous char is in BUFIDX - 1. Fetch this
dec hl
ld a, (hl) ; now, that's our char we have in A
inc hl ; put HL back where it was
; First, let's wait until something is typed.
call aciaGetC
; 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!
; nothing matched? don't do anything
jr shellLoop
.do:
; terminate our string with 0
; Ok, gotta add it do the buffer
; save char for later
ex af, af'
ld hl, SHELL_BUF
call findnull ; HL points to where we need to write
; A is the number of chars in the buf
cp SHELL_BUFSIZE
jr z, .do ; A == bufsize? then our 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
; reset buffer index
ld (ACIA_BUFIDX), a
; alright, let's go!
ld hl, ACIA_BUF
jr shellLoop
.do:
ld hl, SHELL_BUF
call shellParse
; empty our buffer by writing a zero to its first char
xor a
ld (hl), a
jr shellLoop
printcrlf: