title 'Interrupt driven Motorola Keyboard I/O Module' ;---------------------------------------------------------------- ; I/O Drivers and Initializations ; ; This module provides all the I/O drivers for the Motorola ; keyboard. ; ; ; Written by Richard Holmes 27-05-87 ; Last Update by Richard Holmes 15-09-87 ; ; Changed to 1200 baud 10-09-87 ; Added bell warble initialize 15-09-87 ; Added bell warble setup code 28-09-87 ;---------------------------------------------------------------- ; maclib hdz80 maclib mkey maclib iomap ; public init$io public clr$wdt public int$rx,int$tx ; Interrupt drivers public cie,coe,cst public con$err public con$inp public con$out public con$ist public con$ost public aux$err,aux$inp,aux$out,aux$ist,aux$ost public kbd$rd,sch$rd public set$bel,sml$bel,clr$bel public clr$buf public wbl$on,wbl$off,wbl$bel ; Warble the bell ; public tog$tik public wr$col ; Write to column port ; extrn ini$kbd extrn bel$cnt ; Bell counter for int driven timed on extrn warble extrn bel$enb ; Bell enable ; true equ 0ffffh false equ not true buf$max equ 128 ; Buffer sizes bel$time equ 30 ; 25 ticksof the prt1 counter/timer on time wbl$time equ 45 smb$time equ 1 ; Small bell timer = 10 ms odd ; hd64180 equ true ; ;---------------------------------------------------------------- ; Initialize the hardware ;---------------------------------------------------------------- ; init$io: call clr$wdt ; Reset the watchdog timer. Stop him. ; ; Setup serial ports ; ; Setup the ASCI to be ; ; 1200 baud ; 8 bit ; 1 stop ; NO parity ; mvi a,0110$0100b outo cntla0,a ; Write to A control port outo cntla1,a ; B ; ; Setup the second ASCI control register ; mvi a,0000$00100b ; Prescale by total 2560. 1200 baud. outo cntlb0,a outo cntlb1,a ; ; Disable all wait states. Completely useless for static ram ; and 74HC series chips at 3mHz ino a,dcntl ; DMA / wait state controller ani 0000$1111b ; No waits on ram or I/O ori 0101$1111b ; Turn on 1 wait for mem. 2 for I/O outo dcntl,a ; Set it up ; ; Disable refreshing. Not needed with static ram xra a outo rcr,a ; Stop refresh controller. sta tik$byt ; ; ---- Now setup interrupts for the B channel ---- ; ino a,stat1 ; Channel status ori 0000$1000b ; Mask in interrupt enables outo stat1,a ; Done. Interrupts soon. ; ; ---------------- Clear buffers ---------------- ; clr$buf: call clr$wdt ; ; Setup interrupt serial I/O Rx and TX buffers. ; xra a sta rx$siz sta tx$siz ; Transmit and receive sizes. sta warble ; Clear bell warbler sta bel$enb ; Disable bell initially ; lxi h,rx$buf shld rx$ptr ; -> buffer shld rf$ptr lxi h,tx$buf shld tx$ptr shld ts$ptr ; ; ---- Exit via clearing the keyboard buffers ---- ; jmp ini$kbd ; page ;---------------------------------------------------------------- ; Bell Routines ; ; These routines manipulate the RTS line to switch the beeper ; on and off. ; The warble routine only has to set the warble bit and the ; interrupt service routine then toggles the bell on and off ; to produce the pulsed sound. ;----------------------------------------------------------------- ; ; -- This bell routine turns on the bell and warble flag ---- ; wbl$bel: push psw lda bel$enb ora a jz wb$end ; Not bell enabled = exit now. ; mvi a,1 sta warble ; mvi a,wbl$time sta bel$cnt ; wb$end: pop psw ret ; ; ---- Warble tone ON ---- ; wbl$on: push psw ino a,cntla0 ori 0001$0000b outo cntla0,a pop psw ret ; ; -- Disable the bell for the warbler -- ; wbl$off: push psw ino a,cntla0 ani 1110$1111b outo cntla0,a pop psw ret ; ; -- Turn on a continuous bell tone -- ; set$bel: push psw ; lda bel$enb ora a jz sb$end ; Not bell enabled = exit now. ; ino a,cntla0 ; Read existing setups on control port ori 0001$0000b ; Mask RTS- on to set the bell outo cntla0,a mvi a,bel$time sta bel$cnt ; Turn on bell for later turning off. ; sb$end: pop psw ret ; sml$bel: ino a,cntla0 ; Read existing setups on control port ori 0001$0000b ; Mask RTS- on to set the bell outo cntla0,a mvi a,smb$time ; SMALL bell time sta bel$cnt ; Turn on bell for later turning off. ret ; ;---------------------------------------------------------------- ; Toggle the system tick led on/off ;---------------------------------------------------------------- ; tog$tik: lda tik$byt xri 1000$0000b sta tik$byt out 0d0h ret ; ;---------------------------------------------------------------- ; Write (A) to the column port to allow keyboard scanning ;---------------------------------------------------------------- ; wr$col: push b push psw ; mov c,a ; Save input lda tik$byt ani 1000$0000b ; Leave ticker in ora c out 0d0H ; Write to latch sta tik$byt ; Save the byte again ; pop psw pop b ret ; ;---------------------------------------------------------------- ; This clears the RTS line to switch off the LEDS ;---------------------------------------------------------------- ; clr$bel: push psw ino a,cntla0 ; Read existing setups on control port ani 1110$1111b ; Mask RTS- OFF to clear the bell outo cntla0,a xra a sta bel$cnt ; Clear counter sta warble pop psw ret ; ;---------------------------------------------------------------- ; Read the keyboard matrix array. ; ; On Exit ; A = 00 and nothing there ; ELSE A = a the number of the key pessed. ;---------------------------------------------------------------- ; kbd$rd: xra a ret ; ; ;---------------------------------------------------------------- ; Read the input switch port ; ; On Entry ; Nothing required ; ; On Exit ; A = switch image array. ;---------------------------------------------------------------- ; sch$rd: in ip$rd ; Read input port ret ; ;---------------------------------------------------------------- ; Clear the watchdog. ;---------------------------------------------------------------- ; clr$wdt: out wdt out wdt out wdt out wdt out wdt out wdt out wdt out wdt out wdt ret ; ;---------------------------------------------------------------- ; Read the console serial port error bits. ; ; On Entry ; nothing required ; ; On Exit ; A = 0 for no errors ; else same errors as noted in data book. ;---------------------------------------------------------------- ; con$err: ino a,stat0 ; Status port read, channel 0 ani 0111$0000b ; Only look at error bits ret ; ;---------------------------------------------------------------- ; Return the console input status. ; ; The console is the main ASCI serial channel. ; ; On Entry ; nothing required ; ; On Exit ; A = 00 for nothing there ; A = 01 for a character waiting to be read. ; ; Because this is an interrupt driven routine, the software only ; has to check the contents of the buffer SIZE element. ;---------------------------------------------------------------- ; cst: con$ist: lda rx$siz ; Characters in the receive buffer ora a ; 00 = none there ret ; ; ;---------------------------------------------------------------- ; Return the console output status. ; ; The console is the first ASCI serial channel, channel 0 ; ; On Entry ; nothing required ; ; On Exit ; A = 00 for transmitter busy ; A = 01 for a character can be sent to transmitter ;---------------------------------------------------------------- ; con$ost: ino a,stat1 ; Read status ani 0000$0010b ; Look at tx-busy bit ora a ret ; page ; ;---------------------------------------------------------------- ; This is the received data interrupt driver. It must put any ; data into the receive buffer and adjust the pointers. ;---------------------------------------------------------------- ; int$rx: ; Interrupt driven receiver lda rx$siz cpi buf$max jnz irx$start ; ; -- Over-run of the buffer -- ino a,rdr1 ; Remove the character ; ; ---- As per the sped, This must send the buffer full f1 code out ---- ; call clr$buf ; Clear ALL buffers mvi a,0f1h call coe ; Send the F1 out ret ; irx$start: lhld rx$ptr ino a,rdr1 ; Read received data register mov m,a ; Save to buffer inx h shld rx$ptr ; Save new pointer ; ; Determine if the storage pointer has wrapped around to the ; start of the buffer ; ora a lxi d,rx$buf + buf$max ; DE => end of buffer + 1 dsbc d jc irx$ok ; Carry if HL not past end yet lxi h,rx$buf ; -> start again shld rx$ptr ; Reset the pointer irx$ok: lda rx$siz inr a sta rx$siz ; Add one to the buffer size ; ret ; ;---------------------------------------------------------------- ; Read a byte from the Console port ; ; The console is the first ASCI serial channel, channel 0 ; ; On Entry ; nothing required ; ; On Exit ; A = char for a character waiting to be read. ; ; Because this is an interrupt driven routine, the software ; must unpack the receive data buffer and adjust the pointers ; etc. ;---------------------------------------------------------------- ; cie: con$inp: call clr$wdt call con$ist jrz con$inp ; Wait for the key. ; ; Character ready. Pull it out of the received data buffer. ; push h push d di ; Stop interruputs from clashing ; Get the oldest character lhld rf$ptr mov a,m push psw ; Save data ; ; Adjust the fetch pointer inx h shld rf$ptr ; Update the fetch pointer ; ; Check if past end of buffer or not ora a lxi d,rx$buf + buf$max ; DE = end of buffer address dsbc d ; If HL > end f buffer ? jc cie$rf$ok ; lxi h,rx$buf shld rf$ptr ; Reset the fetch pointer ; ; One off the size now ; cie$rf$ok: lda rx$siz dcr a sta rx$siz pop psw ; Restore data etc ; ei ; pop d pop h ret ; ;---------------------------------------------------------------- ; Send the byte in A to the output port buffer. If the ; buffer is empty, send it to the buffer transmitter instead. ; ; On Entry ; A = data byte to send ; ; On Exit ; No changes ; ;---------------------------------------------------------------- ; con$out: coe: push h push d push psw ; Save data ; tx$buf$chk: di lda tx$siz ; Anything in the buffer ? ora a jz tx$empty ; cpi buf$max ; At maximum ? jz tx$full ; ;---------------------------------------------------------------- ; ---- Neither full nor empty, process the byte ---- ; ; Save the byte at stack top into the transmit buffer. ;---------------------------------------------------------------- ; tx$save: lhld ts$ptr ; Get the transmitter save pointer pop psw mov m,a ; Put character into buffer push psw ; ; Update pointers inx h ; -> next buffer location shld ts$ptr ora a lxi d,tx$buf + buf$max dsbc d jc tx$save$ok ; lxi h,tx$buf shld ts$ptr ; Point back to start of buffer ; tx$save$ok: lda tx$siz inr a sta tx$siz ; coe$end: ei pop psw pop d pop h ret ; ;---------------------------------------------------------------- ; If the transmitter is empty, send the byte to the data port ; and exit. ;---------------------------------------------------------------- ; tx$empty: ino a,stat1 ; Check if the chip is busy or not ani 0000$00010b ; Transmitter busy ? jz tx$save ; 0 = it is, save the byte to buffer ; ; Here the transmitter is not sending. Put the users byte ; into the transmitter and setup interrupts. ; pop psw outo tdr1,a ; Send to data port ; ; Get the chip to interrupt when this byte is done with push psw ino a,stat1 ori 0000$0001b ; Turn on TX interrupts outo stat1,a ; Enabled jmp coe$end ; Exit. Clear stack etc ; ;---------------------------------------------------------------- ; If the transmitter buffer is full, enter an idle loop to wait ; for some space. This is most simply done by clearing the ; watchdog and going back to the full test again. ;---------------------------------------------------------------- ; tx$full: ei ; Ensure interrupts are on ; ; -- Wait for a character space to become vacant -- ; tx$full$wait: call clrwdt ; Stop the watchdog lda tx$siz cpi buf$max jz tx$full$wait ; jmp tx$buf$chk ; ;---------------------------------------------------------------- ; This is the interrupt service routine for transmitter ; interrupts. ;---------------------------------------------------------------- ; int$tx: lda tx$siz ora a jz int$tx$stop ; No more data. Stop interrupts ; ; Send the byte to the transmit port lhld tx$ptr ; Address of the data byte mov a,m outo tdr1,a ; To the transmit data register inx h ; -> next byte shld tx$ptr ; ; Test for an over-run/end of buffer ora a lxi d,tx$buf + buf$max dsbc d jc int$tx$ei lxi h,tx$buf shld tx$ptr ; ; Setup interrupts now. ; int$tx$ei: lda tx$siz dcr a sta tx$siz ; One less byte in the buffer ; ino a,stat1 ori 0000$0001b ; Interrupts on outo stat1,a jmp int$tx$end ; int$tx$stop: ino a,stat1 ani 1111$1110b ; Turn off transmitter interrupts outo stat1,a ; Send out to disable ints. ; int$tx$end: ret ; page ;---------------------------------------------------------------- ; Read the Aux console serial port error bits. ; ; On Entry ; nothing required ; ; On Exit ; A = 0 for no errors ; else same errors as noted in data book. ;---------------------------------------------------------------- ; aux$err: ret ; ;---------------------------------------------------------------- ; Return the Aux console input status. ; ; The console is the first ASCI serial channel, channel 1 ; ; On Entry ; nothing required ; ; On Exit ; A = 00 for nothing there ; A = 01 for a character waiting to be read. ;---------------------------------------------------------------- ; aux$ist: ret ; ; ;---------------------------------------------------------------- ; Return the Aux console output status. ; ; The console is the first ASCI serial channel, channel 1 ; ; On Entry ; nothing required ; ; On Exit ; A = 00 for transmitter busy ; A = 01 for a character can be sent to transmitter ;---------------------------------------------------------------- ; aux$ost: ret ; ; ;---------------------------------------------------------------- ; Read a byte from the aux console port ; ; The console is the second ASCI serial channel, channel 1 ; ; On Entry ; nothing required ; ; On Exit ; A = char for a character waiting to be read. ; ; note that NO WATCHDOG clears will be done. Careful. ;---------------------------------------------------------------- ; aux$inp: ret ; ;---------------------------------------------------------------- ; Send the byte in A to the output port. Wait if necessary. ; ; On Entry ; A = data byte to send ; ; On Exit ; No changes ; ;---------------------------------------------------------------- ; aux$out: ret ; ; dseg ; tik$byt ds 1 rx$siz ds 1 ; Receive buffer size tx$siz ds 1 ; Transmit buffer size ; rx$ptr ds 2 ; Store pointers tx$ptr ds 2 ; rf$ptr ds 2 ; Receiver Fetch pointer ts$ptr ds 2 ; Transmitter save pointer ; rx$buf ds buf$max ; Buffer for rx characters tx$buf ds buf$max ; Buffer for rx characters ; end ; ;