Mercurial > hg > Members > kono > nitros9-code
view level1/modules/sc6551dragon.asm @ 2705:79d307ddf47f lwtools-port
wrapped around IFNDEF
author | Boisy Pitre <boisy.pitre@nuance.com> |
---|---|
date | Fri, 20 Jul 2012 16:14:05 -0500 |
parents | d3b51489cb58 |
children |
line wrap: on
line source
* * Dragon 64/Alpha 6551 serial port driver * * Disassembled 2005/04/25 00:12:02 by Disasm v1.5 (C) 1988 by RML * * The Dragon 64 and Dragon Alpha have a hardware serial port driven * by a Rockwell 6551, mapped from $FF04-$FF07. * * Communication between the read/write routines and the ACIA, is buffered * using a pair of ring buffers, when disassembling labels have been assigned * so that bytes are placed into the Rx/Tx queue Tail, and removed from the * head. When the queues become full the calling process is put to sleep * while it awiaits Rx/Tx from the remote device. * * 2005-05-01, P.Harvey-Smith. * Initial disassembly. * * 2005-10-24, P.Harvey-Smith. * Code clean up/commenting. * nam sc6551 ttl os9 device driver ifp1 use defsfile endc * Following definitions borrowed from sc6551.asm * Status bit definitions Stat.IRQ equ %10000000 IRQ occurred Stat.DSR equ %01000000 DSR level (clear = active) Stat.DCD equ %00100000 DCD level (clear = active) Stat.TxE equ %00010000 Tx data register Empty Stat.RxF equ %00001000 Rx data register Full Stat.Ovr equ %00000100 Rx data Overrun error Stat.Frm equ %00000010 Rx data Framing error Stat.Par equ %00000001 Rx data Parity error Stat.Err equ Stat.Ovr!Stat.Frm!Stat.Par Status error bits Stat.Flp equ $00 all Status bits active when set Stat.Msk equ Stat.IRQ!Stat.RxF active IRQs * Control bit definitions Ctl.Stop equ %10000000 stop bits (set=two, clear=one) Ctl.DBit equ %01100000 see data bit table below Ctl.RxCS equ %00010000 Rx clock source (set=baud rate, clear=external) Ctl.Baud equ %00001111 see baud rate table below * data bit table DB.8 equ %00000000 eight data bits per character DB.7 equ %00100000 seven data bits per character DB.6 equ %01000000 six data bits per character DB.5 equ %01100000 five data bits per character * baud rate table org $00 BR.ExClk rmb 1 16x external clock (not supported) org $11 BR.00050 rmb 1 50 baud (not supported) BR.00075 rmb 1 75 baud (not supported) BR.00110 rmb 1 109.92 baud BR.00135 rmb 1 134.58 baud (not supported) BR.00150 rmb 1 150 baud (not supported) BR.00300 rmb 1 300 baud BR.00600 rmb 1 600 baud BR.01200 rmb 1 1200 baud BR.01800 rmb 1 1800 baud (not supported) BR.02400 rmb 1 2400 baud BR.03600 rmb 1 3600 baud (not supported) BR.04800 rmb 1 4800 baud BR.07200 rmb 1 7200 baud (not supported) BR.09600 rmb 1 9600 baud BR.19200 rmb 1 19200 baud * Command bit definitions Cmd.Par equ %11100000 see parity table below Cmd.Echo equ %00010000 local echo (set=activated) Cmd.TIRB equ %00001100 see Tx IRQ/RTS/Break table below Cmd.RxI equ %00000010 Rx IRQ (set=disabled) Cmd.DTR equ %00000001 DTR output (set=enabled) * parity table Par.None equ %00000000 Par.Odd equ %00100000 Par.Even equ %01100000 Par.Mark equ %10100000 Par.Spac equ %11100000 * Tx IRQ/RTS/Break table TIRB.Off equ %00000000 RTS & Tx IRQs disabled TIRB.On equ %00000100 RTS & Tx IRQs enabled TIRB.RTS equ %00001000 RTS enabled, Tx IRQs disabled TIRB.Brk equ %00001100 RTS enabled, Tx IRQs disabled, Tx line Break * V.ERR bit definitions DCDLstEr equ %00100000 DCD lost error OvrFloEr equ %00000100 Rx data overrun or Rx buffer overflow error FrmingEr equ %00000010 Rx data framing error ParityEr equ %00000001 Rx data parity error * FloCtlRx bit definitions FCRxSend equ %10000000 send flow control character FCRxSent equ %00010000 Rx disabled due to XOFF sent FCRxDTR equ %00000010 Rx disabled due to DTR FCRxRTS equ %00000001 Rx disabled due to RTS * FloCtlTx bit definitions FCTxXOff equ %10000000 due to XOFF received FCTxBrk equ %00000010 due to currently transmitting Break * Wrk.Type bit definitions Parity equ %11100000 parity bits MdmKill equ %00010000 modem kill option RxSwFlow equ %00001000 Rx data software (XON/XOFF) flow control TxSwFlow equ %00000100 Tx data software (XON/XOFF) flow control RTSFlow equ %00000010 CTS/RTS hardware flow control DSRFlow equ %00000001 DSR/DTR hardware flow control * Wrk.Baud bit definitions StopBits equ %10000000 number of stop bits code WordLen equ %01100000 word length code BaudRate equ %00001111 baud rate code * Wrk.XTyp bit definitions SwpDCDSR equ %10000000 swap DCD+DSR bits (valid for 6551 only) ForceDTR equ %01000000 don't drop DTR in term routine RxBufPag equ %00001111 input buffer page count * End of borrowed stuff :) tylg set Drivr+Objct atrv set ReEnt+rev rev set $01 mod eom,name,tylg,atrv,start,size RxQueueTailOffset rmb 1 ; Tail of Rx queue, Rx inturrupt inserts here RxQueueHeadOffset rmb 1 ; Head of Rx queue, read call fetches from here u001F rmb 1 TxQueueTailOffset rmb 1 ; Tail of Tx queue, write call inserts here TxQueueHeadOffset rmb 1 ; Head of Tx queue, Tx inturrupt fetches here u0022 rmb 1 u0023 rmb 1 ; something to do with XON/XOFF u0024 rmb 2 SavedDSRDCD rmb 1 ; Saved DSR and DCD ststus RxQueue rmb 80 ; Rx Queue RxQueueLen EQU *-RxQueue ; Rx queue length TxQueue rmb 140 ; Tx Queue TxQueueLen EQU *-TxQueue ; Tx Queue length size equ . fcb $03 name equ * fcs /sc6551/ fcb $04 start equ * lbra Init lbra Read lbra Write lbra GetSta lbra SetSta lbra Term IRQPkt FCB $00 ; Normal bits (flip byte) FCB $80 ; Bit 1 is interrupt request flag (Mask byte) FCB $0A ; Priority byte * Init * * Entry: * Y = address of device descriptor * U = address of device memory area * * Exit: * CC = carry set on error * B = error code * Init LDX V.PORT,U ; 1,U Get port address $FF04 stb AciaStat,x ; Write to status reg, this resets ACIA ldb #$02 stb <u0022,u ldd <IT.PAR,y ; Get parity & baud rate <$26,y andb #$0F leax <BaudRateTable,pcr ; Calculate baud rate values for Acia ldb b,x anda #$F0 sta V.TYPE,u ; Save parity bits for later use ldx V.PORT,u ; Get port address $FF04 std AciaCmd,x ; Setup Command (A), Control (B,Baud rates). lda ,x lda ,x tst AciaStat,x ; Get status lbmi ErrorExit ; Error if int occoured clra ; Init some static storage clrb std <RxQueueTailOffset,u ; Init Rx queue std <TxQueueTailOffset,u ; Init Tx queue sta <u0023,u sta <u001F,u std <u0024,u ldd V.PORT,u ; Get port address $FF04 addd #$0001 ; Setup V$IRQ on status reg changes leax >IRQPkt,pcr ; Point to packet leay >IRQService,pcr ; Point to handler os9 F$IRQ ; Install it ! bcs InitExit ; Error : Exit ldx V.PORT,u ; Get port address $FF04 ldb V.TYPE,u ; Get device parity settings orb #Cmd.DTR ; SET DTR, flag us as ready stb AciaCmd,x clrb ; Flag no error InitExit rts ; ; Baud rate table, all baud rates use external clock. ; BaudRateTable fcb Ctl.RxCS+BR.00110 fcb Ctl.RxCS+BR.00300 fcb Ctl.RxCS+BR.00600 fcb Ctl.RxCS+BR.01200 fcb Ctl.RxCS+BR.02400 fcb Ctl.RxCS+BR.04800 fcb Ctl.RxCS+BR.09600 fcb Ctl.RxCS+BR.19200 PutProcToSleep bsr DoPutProcToSleep * * Input U = Address of device static data storage * Y = Address of path descriptor module * * Output * A = Character read * CC = carry set on error, clear on none * B = error code if CC.C set. * Read lda <u0023,u ble L00A1 ldb <u001F,u cmpb #$0A bhi L00A1 ldb V.XON,u orb #$80 stb <u0023,u ldb V.TYPE,u ; Get prity settings orb #TIRB.On+Cmd.DTR ; Enable tranmitter inturrupt & DTR ($05) ldx V.PORT,u ; Get port address $FF04 stb AciaCmd,x ; Write to ACIA L00A1 tst <u0024,u bne ErrorExit ldb <RxQueueHeadOffset,u ; Get queue head ptr leax <RxQueue,u ; Get Rx Queue address orcc #$50 ; Disable Inturrupts cmpb <RxQueueTailOffset,u ; Is Head=Tail, and therefore queue empty ? beq PutProcToSleep ; Yes : sleep and await input from remote device abx ; Calculate pos in queue for next char lda ,x ; Get byte from read queue dec <u001F,u incb cmpb #RxQueueLen-1 ; Reached end of queue area ? bls L00BF ; no : continue clrb ; Wrap tail pointer to the beginning of queue space L00BF stb <RxQueueHeadOffset,u ; save new queue pointer clrb ldb V.ERR,u beq L00CF ; No error : exit stb <$3A,y clr V.ERR,u comb ; Flag and return error ldb #$F4 L00CF andcc #$AF ; Enable inturrupts rts ErrorExit comb ; Flag error & return ldb #$F6 rts ; ; Put calling process to sleep while we await input or output from remote device. ; DoPutProcToSleep pshs x,b,a lda V.BUSY,u ; Get busy process sta V.WAKE,u ; Store in proc to wake andcc #$AF ; Enable inturrupts ldx #$0000 ; Sleep indefinatly os9 F$Sleep ; Put caller to sleep ldx <D.Proc ; Get current proces descriptor addr ldb <P$Signal,x ; Get signal code of proc beq L00EF cmpb #$03 bls L00F8 L00EF clra lda P$State,x ; Get process state bita #Condem ; Process condemed ? (being killed ?) bne L00F8 ; yes : error, exit puls pc,x,b,a ; Return L00F8 leas $06,s coma rts L00FC bsr DoPutProcToSleep * * Input U = Address of device static data storage * Y = Address of path descriptor module * A = Character to write * * Output * CC = carry set on error, clear on none * B = error code if CC.C set. * Write leax <TxQueue,u ; Get pointer to transmit queue ldb <TxQueueTailOffset,u ; Get offset of end of TX queue abx ; Calculate next free queue slot sta ,x ; Put byte to transmit in queue incb ; Increment queue tail ptr cmpb #TxQueueLen-1 ; End of Queue area ? bls L010D ; no, continue clrb ; Point at begining of queue area L010D orcc #$50 ; Disable inturrupts cmpb <TxQueueHeadOffset,u ; is Head=Tail therefore queue full ? beq L00FC ; Yes : sleep process until room in queue stb <TxQueueTailOffset,u ; Re-save tail pointer lda <u0022,u beq L012B anda #$FD sta <u0022,u bne L012B lda V.TYPE,u ; Get parity bits ora #TIRB.On+Cmd.DTR ; Enable tranmitter inturrupt & DTR ($05) ldx V.PORT,u ; Get port address $FF04 sta AciaCmd,x ; Write to ACIA L012B andcc #$AF ; Enable Inturrupts L012D clrb ; Flag no error rts * * Input U = Address of device static data storage * Y = Address of path descriptor module * A = Status code * * Output * Depends on status code. * GetSta cmpa #SS.Ready ; Device ready ? ($01) bne L013E ldb <u001F,u beq ErrorExit ldx $06,y stb $02,x L013C clrb rts L013E cmpa #SS.EOF ; EOF ? ($06) beq L012D L0142 comb ; Flag error ldb #$D0 rts * * Input U = Address of device static data storage * Y = Address of path descriptor module * A = Status code * * Output * Depends on status code. * SetSta cmpa #SS.SSig ; Send signal on data ready ? ($1A) bne L0161 lda PD.CPR,y ; Get caller's process id ldx PD.RGS,y ; Get caller's Regs ldb $05,x ; Get lower half of X ???? orcc #$50 ; Disable inturrupts tst <u001F,u bne L015C std <u0024,u bra L012B L015C andcc #$AF lbra L01F8 L0161 cmpa #SS.Relea ; Release device ? ($1B) bne L0142 lda PD.CPR,y ; Get calling process ID cmpa <u0024,u ; Same process ? bne L013C ; no ! clr <u0024,u ; Yes : release rts L0170 lbsr DoPutProcToSleep * * Input U = Address of device static data storage * * Output * CC = carry set on error, clear on none * B = error code if CC.C set. * Term ldx <D.Proc ; Get current process descriptor addr lda P$ID,x ; Get process ID sta V.BUSY,u ; Save it in busy and last processs sta V.LPRC,u ldb <TxQueueTailOffset,u ; Check we have sent all bytes ? orcc #$50 cmpb <TxQueueHeadOffset,u bne L0170 ; Still bytes left to send, wait to send them lda V.TYPE,u ; Get Parity settings ldx V.PORT,u ; Get port address $FF04 sta AciaCmd,x ; Set parity in ACIA andcc #$AF ; Enable inturrupts ldx #$0000 ; Remove IRQ handler os9 F$IRQ rts * * F$IRQ handler, * * Input : * A = Status byte XOR flip * U = our data area * * In this case, since flip byte is zero, and any value XOR zero * remains uncahanged, A contains the contents of the ACIA status * register ($FF05) * IRQService ldx V.PORT,u ; Get port address $FF04 tfr a,b ; Take a copy of status andb #Stat.DSR+Stat.DCD ; Mask all but DSR & DCD ($60) cmpb <SavedDSRDCD,u ; Compare to saved beq L01AB ; not changed, check other bits stb <SavedDSRDCD,u ; Save DSR & DCD values bitb #$60 ; Was either set ??? lbne L02AE ; yes lbra L029C L01AB bita #Stat.RxF ; Rx register full ? ($08) bne L01FD ; yes lda <u0023,u bpl L01C4 anda #$7F sta ,x eora V.XON,u sta <u0023,u lda <u0022,u bne L01EA clrb rts L01C4 leay <TxQueue,u ; Point to transmit queue ldb <TxQueueHeadOffset,u ; Check that there are bytes to transmit cmpb <TxQueueTailOffset,u beq L01E2 ; no : skip clra lda d,y ; Get byte to transmit incb ; Increment head offset ptr cmpb #TxQueueLen-1 ; Head at end of Queue area ? bls L01D8 clrb ; Yes : point it at beginning L01D8 stb <TxQueueHeadOffset,u ; Save it sta AciaData,x ; Transmit byte cmpb <TxQueueTailOffset,u ; Head=Tail therefore Tx queue empty ? bne L01F0 ; no : skip ahead L01E2 lda <u0022,u ora #$02 sta <u0022,u L01EA ldb V.TYPE,u ; Get parity settings orb #Cmd.DTR ; Enable DTR, ready stb AciaCmd,x ; Write to ACIA L01F0 ldb #S$Wake ; Wake up calling process lda V.WAKE,u ; Get proc ID to wake L01F4 beq L01FB clr V.WAKE,u ; Clear saved wake proc ID L01F8 os9 F$Send ; send wakeup signal L01FB clrb ; Flag no error rts L01FD bita #Stat.Par+Stat.Frm+Stat.Ovr ; Check for Parity/Framing/Overrun errors ($07) beq L0213 ; No Error detected, do read tfr a,b ; Copy status tst ,x anda #$07 ora V.ERR,u sta V.ERR,u lda $02,x sta $01,x sta $02,x bra L01FB L0213 lda ,x ; Read byte from ACIA beq L022E ; zero, branch ahead cmpa V.INTR,u ; Inturrupt char ? beq L028B cmpa V.QUIT,u ; Quit char ? beq L028F cmpa V.PCHR,u ; Pause char ? beq L0283 cmpa V.XON,u ; Xon char ? beq L029C cmpa <V.XOFF,u ; Xoff char ? lbeq L02AE ; ; If we reach here, char is nothing special, so just put it in queue ; L022E leax <RxQueue,u ; Point to receive queue ldb <RxQueueTailOffset,u ; Get tail offset abx ; Calculate address sta ,x ; Put char in queue incb ; increment tail ptr cmpb #RxQueueLen-1 ; End of queue area ? bls L023D ; no : continue clrb ; point to begining of Rx queue area L023D cmpb <RxQueueHeadOffset,u ; Same as head of queue ? bne L024A ; no : ldb #$04 orb V.ERR,u ; accumulated errors stb V.ERR,u bra L01F0 L024A stb <RxQueueTailOffset,u ; Save tail ptr inc <u001F,u tst <u0024,u beq L025D ldd <u0024,u clr <u0024,u bra L01F8 L025D lda <V.XOFF,u beq L01F0 ldb <u001F,u cmpb #$46 bcs L01F0 ldb <u0023,u bne L01F0 anda #$7F sta <V.XOFF,u ora #$80 sta <u0023,u ldb V.TYPE,u ; Get parity settings orb #TIRB.On+Cmd.DTR ; Enable tranmitter inturrupt & DTR ($05) ldx V.PORT,u ; Get port address $FF04 stb AciaCmd,x ; Write to acia lbra L01F0 L0283 ldx V.DEV2,u beq L022E sta $08,x bra L022E L028B ldb #$03 bra L0291 L028F ldb #$02 L0291 pshs a lda V.LPRC,u ; Get last active proc ID lbsr L01F4 ; Wake process puls a bra L022E L029C lda <u0022,u anda #$FE sta <u0022,u bne L02AC lda V.TYPE,u ; Get parity settings ora #TIRB.On+Cmd.DTR ; Enable tranmitter inturrupt & DTR ($05) sta AciaCmd,x ; Write to ACIA L02AC clrb ; Flag no error rts L02AE lda <u0022,u bne L02B9 ldb V.TYPE,u ; Get parity settings orb #Cmd.DTR ; Enable DTR stb AciaCmd,x ; Write to ACIA L02B9 ora #$01 sta <u0022,u clrb rts emod eom equ * end