| ------------------------------------------------------------------------------ | 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