1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-08 09:58:06 +11:00
collapseos/kernel/acia.asm
2019-11-04 14:44:43 -05:00

137 lines
3.4 KiB
NASM

; acia
;
; Manage I/O from an asynchronous communication interface adapter (ACIA).
; provides "aciaPutC" to put c char on the ACIA as well as an input buffer.
; You have to call "aciaInt" on interrupt for this module to work well.
;
; "aciaInit" also has to be called on boot, but it doesn't call "ei" and "im 1",
; which is the responsibility of the main asm file, but is needed.
; *** DEFINES ***
; ACIA_CTL: IO port for the ACIA's control registers
; ACIA_IO: IO port for the ACIA's data registers
; ACIA_RAMSTART: Address at which ACIA-related variables should be stored in
; RAM.
; *** CONSTS ***
; size of the input buffer. If our input goes over this size, we start losing
; data.
.equ ACIA_BUFSIZE 0x20
; *** VARIABLES ***
; Our input buffer starts there. This is a circular buffer.
.equ ACIA_BUF ACIA_RAMSTART
; 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.
.equ ACIA_BUFRDIDX 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.
.equ ACIA_BUFWRIDX ACIA_BUFRDIDX+1
.equ ACIA_RAMEND ACIA_BUFWRIDX+1
aciaInit:
; initialize variables
xor a
ld (ACIA_BUFRDIDX), a ; starts at 0
ld (ACIA_BUFWRIDX), a
; setup ACIA
; CR7 (1) - Receive Interrupt enabled
; CR6:5 (00) - RTS low, transmit interrupt disabled.
; CR4:2 (101) - 8 bits + 1 stop bit
; CR1:0 (10) - Counter divide: 64
ld a, 0b10010110
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
push hl
; Read our character from ACIA into our BUFIDX
in a, (ACIA_CTL)
bit 0, a ; is our ACIA rcv buffer full?
jr z, .end ; no? a interrupt was triggered for nothing.
; 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
push de ; <|
; Alrighty, buffer not full|. let's write.
ld de, ACIA_BUF ; |
; A already contains our wr|ite index, add it to DE
call addDE ; |
; increase our buf ptr whil|e we still have it in A
call aciaIncIndex ; |
ld (ACIA_BUFWRIDX), a ;
; |
; And finally, fetch the va|lue and write it.
in a, (ACIA_IO) ; |
ld (de), a ; |
pop de ; <|
.end:
pop hl
pop af
ei
reti
; *** STDIO ***
; These function below follow the stdio API.
aciaGetC:
push de
.loop:
ld a, (ACIA_BUFWRIDX)
ld e, a
ld a, (ACIA_BUFRDIDX)
cp e
jr z, .loop ; equal? nothing to read. loop
; 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 finally, fetch the value.
ld a, (de)
pop de
ret
; spits character in A in port SER_OUT
aciaPutC:
push af
.stwait:
in a, (ACIA_CTL) ; get status byte from SER
bit 1, a ; are we still transmitting?
jr z, .stwait ; if yes, wait until we aren't
pop af
out (ACIA_IO), a ; push current char
ret