view level1/modules/sc6551dragon.asm @ 3246:be3446f758ca

Updated dwread.asm and dwwrite.asm so that the 6551 routines could have specially defined flags so that some of the addresses could be overridden by the make files if needed.
author David Ladd <drencor-xeen@users.sourceforge.net>
date Sun, 11 Mar 2018 01:30:30 -0600
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