; 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