diff --git a/parts/acia.asm b/parts/acia.asm index 520a9a5..3aade58 100644 --- a/parts/acia.asm +++ b/parts/acia.asm @@ -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 diff --git a/parts/core.asm b/parts/core.asm index 27339af..acd89b1 100644 --- a/parts/core.asm +++ b/parts/core.asm @@ -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 + diff --git a/parts/shell.asm b/parts/shell.asm index 9ff790b..77ffca8 100644 --- a/parts/shell.asm +++ b/parts/shell.asm @@ -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: