| ------------------------------------------------------------------------------ | serintr.s -- MIDAS-VII serial I/O subroutines | Version 4 -- 1988-12-13 -- D.N. Lynx Crowe | These subroutines replace those in bios.s in order to add support for | MIDAS-VII foot pedal and pulse inputs, and pulse outputs. | WARNING: The code below uses addresses in the bios RAM area. These addresses | correspond to those in PROMS dated 1988-06-20 et seq. If the bios is changed, | the addresses marked with <== may have to be changed. | ------------------------------------------------------------------------------ .text .xdef setsio .xdef foot1 .xdef foot2 .xdef pulse1 .xdef pulse2 .xdef serintr .xdef serint .xdef midint .xdef wrapin .xdef wrapout .xdef serput .xdef midput .xdef rtschk .xdef rtson .xdef rtsoff | ============================================================================== | The following addresses, marked by <==, are bios version dependent: RAM = 0x00000400 | Beginning of system RAM area <== SR1IOREC = RAM+0x0AB0 | Serial-1 iorec structure <== SR2IOREC = RAM+0x0AD8 | Serial-2 iorec structure <== MC1IOREC = RAM+0x0B00 | MIDI-1 iorec structure <== MC2IOREC = RAM+0x0B28 | MIDI-2 iorec structure <== | End of bios version dependent addresses. | ============================================================================== .page SERVECT = 0x000074 | Level 5 interrupt autovector address IPL7 = 0x0700 | IPL 7 value for sr | ACIA I/O Addresses: | ------------------- SR1ACIA = 0x3A8001 | Serial-1 ACIA base address SR2ACIA = 0x3A8009 | Serial-2 ACIA base address MC1ACIA = 0x3AC001 | MIDI-1 ACIA base address MC2ACIA = 0x3AC009 | MIDI-2 ACIA base address | ACIA Register offsets: | ---------------------- ACIA_IER = 0 | ACIA IER offset ACIA_ISR = 0 | ACIA ISR offset ACIA_CSR = 2 | ACIA CSR offset ACIA_CFR = 2 | ACIA CFR offset ACIA_TBR = 4 | ACIA TBR offset ACIA_TDR = 6 | ACIA TDR offset ACIA_RDR = 6 | ACIA RDR offset | iorec structure definitions: | ---------------------------- IORECLN = 40 | Length of an iorec structure ibuf = 0 | Input buffer base address ibufsize = 4 | Input buffer size (bytes) ibufhd = 6 | Input buffer head index ibuftl = 8 | Input buffer tail index ibuflow = 10 | Input buffer low water mark ibufhi = 12 | Input buffer high water mark obuf = 14 | Output buffer base address obufsize = 18 | Output buffer size (bytes) obufhd = 20 | Output buffer head index obuftl = 22 | Output buffer tail index obuflow = 24 | Output buffer low water mark obufhi = 26 | Output buffer high water mark cfr0 = 28 | ACIA CFR, MS bit = 0 cfr1 = 29 | ACIA CFR, MS bit = 1 flagxon = 30 | XON flag (non-zero = XOFF sent) flagxoff = 31 | XOFF flag (non-zero = active) linedisc = 32 | Line discipline flags erbyte = 33 | Last error byte isr = 34 | ACIA ISR on interrupt csr = 35 | ACIA CSR on interrupt errct = 36 | Error count (FRM/OVR/BRK) ibfct = 38 | Input buffer full count .page | serintr -- Serial (Serial-1, Serial-2, MIDI-1, MIDI-2) interrupt handler | ------- ------------------------------------------------------------- serintr: movem.l d0-d3/a0-a2,-(a7) | Save registers lea SR1IOREC,a0 | Point at Serial-1 iorec lea SR1ACIA,a1 | Point at Serial-1 ACIA movea.l foot1,a2 | Point at foot sw. 1 processor bsr serint | Go process (possible) int. lea SR2IOREC,a0 | Point at Serial-2 iorec lea SR2ACIA,a1 | Point at Serial-2 ACIA movea.l foot2,a2 | Point at foot sw. 2 processor bsr serint | Go process (possible) int. lea MC1IOREC,a0 | Point at MIDI-1 iorec lea MC1ACIA,a1 | Point at MIDI-1 ACIA movea.l pulse1,a2 | Point at pulse 1 processor bsr midint | Go process (possible) int. lea MC2IOREC,a0 | Point at MIDI-2 iorec lea MC2ACIA,a1 | Point at MIDI-2 ACIA movea.l pulse2,a2 | Point at pulse 2 processor bsr midint | Go process (possible) int. movem.l (a7)+,d0-d3/a0-a2 | Restore registers rte | Return from exception .page | serint -- Process an interrupt from Serial-1 or Serial-2 | ------ ---------------------------------------------- serint: move.b ACIA_ISR(a1),isr(a0) | Get and save ISR move.b ACIA_CSR(a1),csr(a0) | Get and save CSR btst.b #7,isr(a0) | Was int for this device ? beq serintx | Jump if not serchk: btst.b #1,isr(a0) | FRM/OVR/BRK error ? bne sererr | Jump if so btst.b #0,isr(a0) | Receiver interrupt ? bne serrx | Jump if so sertxq: btst.b #6,isr(a0) | Transmitter interrupt ? bne sertx | Jump if so serctq: btst.b #5,isr(a0) | CTS interrupt ? bne sercts | Jump if so serintx: btst.b #4,isr(a0) | DCD interrupt ? bne calldcd | Jump if so serdone: rts | Return to caller calldcd: move.b csr(a0),d0 | Get CSR interrupt status btst.l #4,d0 | Check DCD input (0 = active) bne calldcd0 | Jump if line was inactive moveq.l #1,d0 | Set footswitch status to TRUE bra calldcd1 | ... calldcd0: moveq.l #0,d0 | Set footswitch status to FALSE calldcd1: move.w d0,-(a7) | Call the footswitch processor jsr (a2) | ... (*footX)(status) tst.w (a7)+ | ... rts | Return to caller .page | Handle serial I/O port error sererr: addq.w #1,errct(a0) | Update error count move.b ACIA_RDR(a1),erbyte(a0) | Get error byte rts | Return to caller | Handle CTS interupt sercts: btst.b #1,linedisc(a0) | RTS/CTS mode ? beq serintx | Ignore if not btst.b #5,csr(a0) | CTS set ? beq serintx | Ignore if not sercts1: btst.b #6,isr(a0) | TDRE set ? beq sercts1 | Loop until it is (!) move.w obufhd(a0),d2 | Head index to d2 cmp.w obuftl(a0),d2 | Compare to tail index beq serintx | Done if buffer empty bsr wrapout | Adjust pointer for wraparound move.l obuf(a0),a2 | Get buffer base in a2 move.b 0(a2,d2),ACIA_TDR(a1) | Send byte on its way move.w d2,obufhd(a0) | Save updated head index bra serintx | Done .page | Handle receiver interrupt serrx: btst.b #1,linedisc(a0) | RTS/CTS mode set ? beq serrx1 | Jump if not bsr rtsoff | Turn off RTS serrx1: move.b ACIA_RDR(a1),d0 | Read data from ACIA btst.b #1,linedisc(a0) | RTS/CTS mode set ? bne serrx3 | Jump if so btst.b #0,linedisc(a0) | XON/XOFF mode set ? beq serrx3 | Jump if not cmpi.b #0x11,d0 | Is this an XON ? bne serrx2 | Jump if not move.b #0x00,flagxoff(a0) | Clear flagxoff bra sertxq | Done serrx2: cmpi.b #0x13,d0 | Is this an XOFF ? bne serrx3 | Jump if not move.b #0xFF,flagxoff(a0) | Set flagxoff bra sertxq | Done serrx3: move.w ibuftl(a0),d1 | Get tail index in d1 bsr wrapin | Adjust for wraparound cmp.w ibufhd(a0),d1 | Head = tail ? beq seribf | If so, we drop the character .page move.l ibuf(a0),a2 | Get buffer address move.b d0,0(a2,d1) | Stash byte in buffer move.w d1,ibuftl(a0) | Save updated tail index move.w ibuftl(a0),d2 | Tail index to d2 move.w ibufhd(a0),d3 | Head index to d3 cmp.w d3,d2 | Head > Tail ? bhi rsi_1 | Jump if not add.w ibufsize(a0),d2 | Add buffer size to tail index rsi_1: sub.w d3,d2 | Length = (adjusted)Tail - Head cmp.w ibufhi(a0),d2 | Hit high water mark ? bne serrx4 | Jump if not btst.b #1,linedisc(a0) | RTS/CTS mode set ? bne sertxq | Done if so btst.b #0,linedisc(a0) | XON/XOFF mode set ? beq serrx4 | Jump if not tst.b flagxon(a0) | XOFF already sent ? bne serrx4 | Jump if so move.b #0xFF,flagxon(a0) | Set the flag move.b #0x13,d1 | Send an XOFF bsr serput | ... serrx4: btst #1,linedisc(a0) | RTS/CTS mode set ? beq sertxq | Done if not bsr rtson | Turn on RTS bra sertxq | Done .page | Handle transmitter interrupt sertx: btst.b #1,linedisc(a0) | RTS/CTS mode set ? bne sertx2 | If so, go check CTS btst.b #0,linedisc(a0) | XON/XOFF mode set ? beq sertx1 | Jump if not tst.b flagxoff(a0) | Check flagxoff bne serctq | Done if set sertx1: move.w obufhd(a0),d2 | Head index to d2 cmp.w obuftl(a0),d2 | Compare to tail index beq serctq | Done if buffer empty bsr wrapout | Adjust pointer for wraparound move.l obuf(a0),a2 | Get buffer base address move.b 0(a2,d2),ACIA_TDR(a1) | Send byte on its way move.w d2,obufhd(a0) | Save updated head index bra serctq | Done sertx2: btst.b #5,csr(a0) | CTS set in csr ? beq serctq | If not, go check for CTS int bra sertx1 | CTS was set, go transmit seribf: move.b d0,erbyte(a0) | Log dropped character addq.w #1,ibfct(a0) | ... bra sertxq | Go check Tx interrupt .page | midint -- Process an interrupt from MIDI-1 or MIDI-2 | ------ ------------------------------------------ midint: move.b ACIA_ISR(a1),isr(a0) | Get and save ISR move.b ACIA_CSR(a1),csr(a0) | Get and save CSR btst.b #7,isr(a0) | Was int for this device ? beq midintx | Jump if not midchk: btst.b #1,isr(a0) | FRM/OVR/BRK error ? bne miderr | Jump if so btst.b #0,isr(a0) | Receiver interrupt ? bne midrx | Jump if so midtxq: btst.b #6,isr(a0) | Transmitter interrupt ? bne midtx | Jump if so midintx: btst #4,isr(a0) | DCD interrupt ? bne mididcd | Jump if so mididone: rts | Return to caller mididcd: jmp (a2) | Exit through the DCD processor miderr: addq.w #1,errct(a0) | Update error count move.b ACIA_RDR(a1),erbyte(a0) | Get error byte rts | Handle receiver interrupt midrx: move.b ACIA_RDR(a1),d0 | Read data from ACIA move.w ibuftl(a0),d1 | Get tail index in d1 bsr wrapin | Adjust for wraparound cmp.w ibufhd(a0),d1 | Head = tail ? beq midibf | If so, we drop the character move.l ibuf(a0),a2 | Get buffer address move.b d0,0(a2,d1) | Stash byte in buffer move.w d1,ibuftl(a0) | Save updated tail index bra midtxq | Done (go check tx int) .page | Handle transmitter interrupt midtx: move.w obufhd(a0),d2 | Head index to d2 cmp.w obuftl(a0),d2 | Compare to tail index beq midintx | Done if buffer empty bsr wrapout | Adjust pointer for wraparound move.l obuf(a0),a2 | Get buffer base address move.b 0(a2,d2),ACIA_TDR(a1) | Send byte on its way move.w d2,obufhd(a0) | Save updated head index bra midintx | Done midibf: move.b d0,erbyte(a0) | Log dropped character addq.w #1,ibfct(a0) | ... bra midtxq | Go check Tx interrupt .page | serput -- Output a character to a serial port | ------ ----------------------------------- serput: move.w sr,-(a7) | Save status register ori.w #IPL7,sr | DISABLE INTERRUPTS move.b ACIA_ISR(a1),isr(a0) | Get ACIA isr move.b ACIA_CSR(a1),csr(a0) | Get ACIA csr btst #0,linedisc(a0) | XON/XOFF mode ? beq serpt_1 | Jump if not tst.b flagxoff(a0) | XON active ? bne serpt_2 | Jump if so serpt_1: btst.b #6,isr(a0) | Is ACIA still sending ? beq serpt_2 | Jump if so move.w obufhd(a0),d2 | Head index to d2 cmp.w obuftl(a0),d2 | Compare to tail index bne serpt_2 | Jump if buffer not empty move.b d1,ACIA_TDR(a1) | Give byte to ACIA to send bra serpt_3 | Go deal with RTS/CTS if needed serpt_2: move.w obuftl(a0),d2 | Tail index to d2 bsr wrapout | Adjust for wraparound cmp.w obufhd(a0),d2 | Compare to head index beq serpt_4 | Jump if buffer full move.l obuf(a0),a2 | Get buffer base address in a2 move.b d1,0(a2,d2) | Put character in buffer move.w d2,obuftl(a0) | Update buffer tail index serpt_3: bsr serchk | Check status on our way out bsr rtschk | Handle RTS protocol move.w (a7)+,sr | RESTORE INTERRUPTS andi #0xFFFE,sr | Clear carry flag = OK rts | Return to caller serpt_4: bsr serchk | Check status on our way out bsr rtschk | Handle RTS protocol move.w (a7)+,sr | RESTORE INTERRUPTS ori #0x0001,sr | Set carry flag = buffer full rts | Return to caller .page | midput -- Output to MIDI | ------ -------------- midput: move.w sr,-(a7) | Save status register ori.w #IPL7,sr | DISABLE INTERRUPTS move.b ACIA_ISR(a1),isr(a0) | Get ACIA isr move.b ACIA_CSR(a1),csr(a0) | Get ACIA csr btst.b #6,isr(a0) | Is ACIA still sending ? beq midpt_2 | Jump if so move.w obufhd(a0),d2 | Head index to d2 cmp.w obuftl(a0),d2 | Compare to tail index bne midpt_2 | Jump if buffer not empty move.b d1,ACIA_TDR(a1) | Give byte to ACIA to send bra midpt_3 | Go set final status and exit midpt_2: move.w obuftl(a0),d2 | Tail index to d2 bsr wrapout | Adjust for wraparound cmp.w obufhd(a0),d2 | Compare to head index beq midpt_4 | Jump if buffer full move.l obuf(a0),a2 | Get buffer base address in a2 move.b d1,0(a2,d2) | Put character in buffer move.w d2,obuftl(a0) | Update buffer tail index midpt_3: bsr midchk | Check status on our way out move.w (a7)+,sr | RESTORE INTERRUPTS andi #0xFFFE,sr | Clear carry flag = OK rts | Return to caller midpt_4: bsr midchk | Check status on our way out move.w (a7)+,sr | RESTORE INTERRUPTS ori #0x0001,sr | Set carry flag = buffer full rts | Return to caller .page | rtschk -- Check RTS mode and turn on RTS if it's enabled | ------ ---------------------------------------------- rtschk: btst #1,linedisc(a0) | RTS/CTS mode set ? beq rtsexit | Jump to exit if not | rtson -- Turn on RTS line | ----- ---------------- rtson: move.b cfr1(a0),d0 | Pick up CFR1 image bclr #0,d0 | Turn on RTS line (0 = on) bra rtscmn | Join common RTS code below | rtsoff -- Turn off RTS line | ------ ----------------- rtsoff: move.b cfr1(a0),d0 | Pick up CFR1 image bset #0,d0 | Turn off RTS line (1 = off) rtscmn: bset #7,d0 | Make sure MS bit is set move.b d0,cfr1(a0) | Update CFR1 image move.b d0,ACIA_CFR(a1) | Send CFR to hardware rtsexit: rts | Return to caller .page | wrapin -- Check input pointer for wraparound | ------ ---------------------------------- wrapin: add.w #1,d1 | Head index +1 cmp.w ibufsize(a0),d1 | = buffer size ? bcs wrapin1 | Jump if not moveq.l #0,d1 | Wraparound wrapin1: rts | Return to caller | wrapout -- Check output pointer for wraparound | ------- ----------------------------------- wrapout: addq.w #1,d2 | Tail index +1 cmp.w obufsize(a0),d2 | = buffer size ? bcs wrapout1 | Jump if not moveq.l #0,d2 | Wrap around if so wrapout1: rts | Return to caller .page | setsio -- setsio() -- initialize serial I/O vectors and DCD interrupts | ------ ------------------------------------------------------------ setsio: move.w sr,-(a7) | Preserve status register ori.w #IPL7,sr | DISABLE INTERRUPTS lea nulsiox,a0 | Get null return address move.l a0,foot1 | Initialize foot1 vector move.l a0,foot2 | Initialize foot2 vector move.l a0,pulse1 | Initialize pulse1 vector move.l a0,pulse2 | Initialize pulse2 vector lea SR1ACIA,a1 | Point at Serial-1 ACIA move.b #0x90,ACIA_IER(a1) | Enable DCD interrupts lea SR2ACIA,a1 | Point at Serial-2 ACIA move.b #0x90,ACIA_IER(a1) | Enable DCD interrupts lea MC1ACIA,a1 | Point at MIDI-1 ACIA move.b #0x90,ACIA_IER(a1) | Enable DCD interrupts lea MC2ACIA,a1 | Point at MIDI-2 ACIA move.b #0x90,ACIA_IER(a1) | Enable DCD interrupts lea serintr,a0 | Initialize interrupt vector move.l a0,SERVECT | ... in processor RAM move.w (a7)+,sr | RESTORE INTERRUPTS nulsiox: rts | Return to caller .bss | DCD interrupt processor vectors | ------------------------------- foot1: .ds.l 1 | short (*foot1)(); foot2: .ds.l 1 | short (*foot2)(); pulse1: .ds.l 1 | short (*pulse1)(); pulse2: .ds.l 1 | short (*pulse2)(); .end