view level1/modules/scdwt.asm @ 2249:8cbd8013fe24

rel: now we clear $00-$1F (direct page) in Level 2 scdwt: changes made to driver by Boisy Pitre
author boisy
date Thu, 24 Dec 2009 16:37:49 +0000
parents 2a889a511f0a
children 8f641411c2fa
line wrap: on
line source

********************************************************************
* scdwt.asm - CoCo DriveWire Virtual Serial Driver
*
* many parts copied or only slightly modified from other nitros-9 modules or the DriveWire project  
*
* serreadm working, finally
* 
* Aaron Wolfe
* v0.8 11/30/09
*

         	nam   	scdwt
         	ttl   	CoCo DriveWire Virtual Serial Driver

         	ifp1
         	use   	defsfile
         	use   	dwdefs.d
         	endc


tylg     	set   	Drivr+Objct   
atrv     	set   	ReEnt+Rev
rev      	set   	$00
edition  	set   	1

* Device memory area: offset from U
         	org   	V.SCF      	;V.SCF: free memory for driver to use

DWTAdr		rmb		2			;pointer to instance with irq handler       

* these are only used in the primary instance

* For ISR
*VIRQPckt 	rmb   	5			;VIRQ packet Counter(2),Reset(2),Status(1) bytes

* port status variables...
* none yet
FlowCtrl	rmb		1			;flow control flags

regWbuf    	rmb   	2			;substitute for regW

* input buffer redesign to support multiball, used per instance
* heavily borrowed from sc6551
RxDatLen   rmb   1              ;current length of data in Rx buffer
RxBufSiz   rmb   1              ;Rx buffer size
RxBufEnd   rmb   2              ;end of Rx buffer
RxBufGet   rmb   2              ;Rx buffer output pointer
RxBufPut   rmb   2              ;Rx buffer input pointer
RxGrab     rmb   1              ;bytes to grab in multiread
RxBufPtr   rmb   2              ;pointer to Rx buffer
RxBufDSz   equ   256-.          ;default Rx buffer gets remainder of page...
*RxBufDSz	equ		20
RxBuff     rmb   RxBufDSz       ;default Rx buffer

memsize     equ   	.

         	mod   	eom,name,tylg,atrv,start,memsize

* module info         	
         	fcb   	READ.+WRITE.	;driver access modes
name     	fcs   	/scdwt/		;driver name
         	fcb   	edition   	;driver edition 


* irq         	
IRQPckt   	fcb  	$00,$01,$0A 	;IRQ packet Flip(1),Mask(1),Priority(1) bytes

            
* dispatch calls            
start    	equ   	*
         	lbra  	Init
         	lbra  	Read
         	lbra  	Write
         	lbra  	GetStt
         	lbra  	SetStt
	 	

***********************************************************************
* Term
*
* shut down the driver.
* should close only the correct port, tell server to close the port,
* and remove irq handler when no ports are left 
*
* Entry:
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code   
* NEEDS WORK
Term     	equ   	*

			lda		<V.PORT+1,u		;get our port #

			* put setstat args on stack for later
			ldb		#255
			pshs 	d				;port #, 255 (term) on stack
			* clear statics table entry
			ldx   	#D.DWSTATS
			clr		a,x				;clear out
			* tell server
			lda     #OP_SERSETSTAT ; load command
			pshs   	a      		; command store on stack
			leax    ,s     		; point X to stack 
			ldy     #3          ; 3 bytes to send 
    
            pshs    u

			IFGT  Level-1
			ldu   	<D.DWSUB
			ELSE
			ldu   	>D.DWSUB
			ENDC
			
    		jsr     6,u      	; call DWrite

            puls    u
			
    		leas	3,s			; clean 3 DWsub args from stack 
			
* Check if we need to clean up IRQ
			bsr     CheckStats
    		beq		DumpVIRQ	;no more ports, lets bail
    		clrb
			rts
			
* no more ports open.. are we the primary instance?
DumpVIRQ   	
			ldy     #D.DWVIRQPkt
         	ldx   	#$0000		;code to delete VIRQ entry
         	os9   	F$VIRQ		;remove from VIRQ polling
         	bcs   	Term.Err	;go report error...
DumpIRQ
			ldx     #D.DWVIRQPkt
         	leax  	Vi.Stat,x	;fake VIRQ status register
         	tfr   	x,d			;copy address...
         	ldx   	#$0000		;code to remove IRQ entry
			ldu     #D.DWSTATS  ;ISR doesn't use static storage, so set to $0000
         	leay  	IRQSvc,pc	;IRQ service routine
         	os9   	F$IRQ
Term.Err    rts


***********************************************************************
* CheckStats - Check if the D.DWSTATS table is empty
* Entry: None
* Exit:  B=0, stat table is empty; B!=0, stat table is not empty
CheckStats
         	pshs  	x
			ldx     #D.DWSTATS
			ldb     #7
CheckLoop   tst     ,x+
			bne     CheckExit
			decb
			bne     CheckLoop
CheckExit   puls    x,pc
         	
***********************************************************************
* Init
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
            
Init		equ		*
			pshs  cc        save IRQ/Carry status

* link to subroutine module
         	pshs	u				;preserve u since os9 link is coming up

			IFGT  	Level-1
         	ldx   	<D.Proc
         	pshs  	x
         	ldx   	<D.SysPrc
         	stx   	<D.Proc
         	ENDC
         	clra
         		
         	leax  	dw3name,pcr
         	os9   	F$Link
         	IFGT  	Level-1
         	puls  	x
         	stx   	<D.Proc
         	ENDC
         	lbcs   	InitEx
         	IFGT  	Level-1
         	sty   	<D.DWSUB
         	ELSE
         	sty   	>D.DWSUB
         	ENDC
         	jsr   	,y			call DW init routine
       	
         	puls	u				;restore u
      	
* Setup D.DWTAdr - install IRQ handler if we are the first port
DWSetOk		equ		*
         	* the value seems to remain in memory through a reset, so see if any ports active
         	* should be >0 only if the IRQ is installed
         	* trying local byte..
         	bsr     CheckStats
         	bne		IRQok
         	
* If here, we must install ISR
     

* Install the IRQ/VIRQ entry 
InstIRQ
			ldx     #D.DWVIRQPkt
		    leax  	Vi.Stat,x		;fake VIRQ status register
         	lda   	#$80			;VIRQ flag clear, repeated VIRQs
         	sta   	,x				;set it while we're here...
         	tfr   	x,d				;copy fake VIRQ status register address
         	leax  	IRQPckt,pcr		;IRQ polling packet
         	leay  	IRQSvc,pcr  	;IRQ service entry
            pshs    u
			ldu     #D.DWSTATS
         	os9   	F$IRQ			;install
			puls    u
         	bcs   	InitEx   		;exit with error
         	ldd   	#$0003     		;lets try every 6 ticks (0.1 seconds) -- testing 3, gives better response in interactive things
			ldx     #D.DWVIRQPkt
         	std   	Vi.Rst,x		;reset count
			tfr     x,y             ;move VIRQ software packet to Y
         	ldx   	#$0001     		;code to install new VIRQ
         	os9   	F$VIRQ			;install
         	bcc   	IRQok1st   		;no error, continue
*         	bsr   	DumpIRQ    		;go remove from IRQ polling - is this necessary? according to OS9 dev ref, if I return an error it calls Term for me?  vrn.asm does this though
         	puls  	cc,pc			;recover error info and exit
         	
IRQok1st	
IRQok
         	ldx   	#D.DWSTATS
			tfr     u,d
	        ldb		<V.PORT+1,u		;get our port #
			sta		b,x				;store in table
	
			* set up local buffer
			ldb   	#RxBufDSz      	;default Rx buffer size
			leax  	RxBuff,u       	;default Rx buffer address
			stb   	RxBufSiz,u      	;save Rx buffer size
			stx   	RxBufPtr,u      	;save Rx buffer address
			stx   	RxBufGet,u      	;set initial Rx buffer input address
			stx   	RxBufPut,u      	;set initial Rx buffer output address
			abx  						;add buffer size to buffer start..
			stx   	RxBufEnd,u      	;save Rx buffer end address

			* tell DW we've got a new port opening
			ldb		<V.PORT+1,u		; get our port #			
			pshs    b
			lda     #OP_SERSETSTAT 	; command 
			pshs   	d      			; command + port # on stack
			leax    ,s     			; point X to stack 
			ldy     #3          	; 2 bytes to send
			
			IFGT  Level-1
			ldu   	<D.DWSUB
			ELSE
			ldu   	>D.DWSUB
			ENDC
    		jsr     6,u      		; call DWrite
    		
			*for now setstat doesn't respond    		
    		leas	3,s				;clean dw args off stack
    		
InitEx		equ		*
			puls	cc,pc


* drivewire info
dw3name  	fcs  	/dw3/



***********************************************************************
* Interrupt handler  - Much help from Darren Atkinson

			
IRQMulti3   pshs    a			;save port #
         	cmpb	RxGrab,u	;compare room in buffer to server's byte
           	bhs		IRQM06		;room left >= server's bytes, no problem
  					
           	stb		RxGrab,u	;else replace with room left in our buffer
  			
           	* also limit to end of buffer
IRQM06		ldd		RxBufEnd,u	;end addr of buffer
			subd	RxBufPut,u	;subtract current write pointer, result is # bytes left going forward in buff.

IRQM05		cmpb	RxGrab,u	;compare b (room left) to grab bytes  
			bhs		IRQM03		;branch if we have room for grab bytes
			
			stb		RxGrab,u	;else set grab to room left
			
			* send multiread req
IRQM03		puls    a			;port # is on stack
			ldb		RxGrab,u

			pshs	u
						
			* setup DWsub command
			pshs	d			; (a port, b bytes)
			lda     #OP_SERREADM ; load command
			pshs   	a      		; command store on stack
			leax    ,s     		; point X to stack 
			ldy     #3          ; 3 bytes to send
    
			IFGT  Level-1
			ldu   	<D.DWSUB
			ELSE
			ldu   	>D.DWSUB
			ENDC
    		jsr     6,u      	; call DWrite
    		
    		leas	3,s			; clean 3 DWsub args from stack 
    		
    		ldx		,s			; pointer to this port's area (from U prior), leave it on stack
    		ldb		RxGrab,x	; set B to grab bytes
    		clra				; 0 in high byte		
    		tfr		d,y			;set # bytes for DW
    		
    		ldx    RxBufPut,x	; point X to insert position in this port's buffer
    		* receive response
    		jsr     3,u    		; call DWRead
			* handle errors?
			
			
			puls	u
			ldb		RxGrab,u	; our grab bytes

			* set new RxBufPut
			ldx 	RxBufPut,u	;current write pointer
			abx					;add b (# bytes) to RxBufPut
			cmpx  	RxBufEnd,u 	;end of Rx buffer?
			blo   	IRQM04		;no, go keep laydown pointer
			ldx   	RxBufPtr,u 	;get Rx buffer start address
IRQM04   	stx   	RxBufPut,u 	;set new Rx data laydown pointer

			* set new RxDatLen
			ldb		RxDatLen,u
			addb	RxGrab,u
			stb		RxDatLen,u	;store new value
			
			rts
			
IRQMulti			
           	* initial grab bytes
           	stb		RxGrab,u	
           	
  			* limit server bytes to bufsize - datlen
  			ldb		RxBufSiz,u	;size of buffer
           	subb	RxDatLen,u	;current bytes in buffer
           	bne		IRQMulti3	;continue, we have some space in buffer
  			* no room in buffer
  			tstb
  			bne		CkSuspnd
  			bra		IRQExit
  			

**** IRQ ENTRY POINT
IRQSvc		equ		*
			pshs  	cc,dp 		;save system cc,DP
			orcc	#$50		;mask interrupts
			
			* mark VIRQ handled
			ldx     #D.DWVIRQPkt
			lda   	Vi.Stat,x	;VIRQ status register
			anda  	#^Vi.IFlag 	;clear flag in VIRQ status register
			sta   	Vi.Stat,x	;save it...
			
			* poll server for incoming serial data
 			
			* send request
			lda     #OP_SERREAD ; load command
			pshs   	a      		; command store on stack
			leax    ,s     		; point X to stack 
			ldy     #1          ; 1 byte to send
    
			IFGT  Level-1
			ldu   	<D.DWSUB
			ELSE
			ldu   	>D.DWSUB
			ENDC
    		jsr     6,u      	; call DWrite
    		
    		* receive response
    		leas	-1,s		;one more byte to fit response
			leax    ,s   		; point X to stack head
			ldy     #2    		; 2 bytes to retrieve
			jsr     3,u    		; call DWRead
			beq     IRQSvc2		; branch if no error
			leas    2,s     	; error, cleanup stack 2
			bra		IRQExit2	; don't reset error count on the way out
			
			* process response	
IRQSvc2
     		ldd     ,s++     	; pull returned status byte into A,data into B (set Z if zero, N if multiread)
  			beq   	IRQExit  	; branch if D = 0 (nothing to do)

* save back D on stack and build our U
            pshs    d
  			anda    #$07		;mask first 5 bits, a is now port #+1
  			deca				;we pass +1 to use 0 for no data
* here we set U to the static storage area of the device we are working with
			ldx     #D.DWSTATS
			lda     a,x
			tfr     a,dp		;put in DP
			clrb
			tfr     d,u

     		ldd     ,s++     	; pull returned status byte into A,data into B (set Z if zero, N if multiread)
  			
  			bmi		IRQMulti	; branch for multiread
  			
* put byte B in port A's buffer - optimization help from Darren Atkinson       
IRQPutCh   	ldx     RxBufPut,u	;point X to the data buffer
        
			* sc6551 now does lots of things with flow control, we might want some of this
			* but implemented differently.. for now.. skip it
		   
			* store our data byte
			stb    	,x+     	; store and increment buffer pointer
        
			* adjust RxBufPut	
			cmpx  	RxBufEnd,u 	;end of Rx buffer?
			blo   	IRQSkip1	;no, go keep laydown pointer
			ldx   	RxBufPtr,u 	;get Rx buffer start address
IRQSkip1   	stx   	RxBufPut,u 	;set new Rx data laydown pointer

			* increment RxDatLen
			inc		RxDatLen,u


  			* check if we have a process waiting for data	
CkSuspnd   	lda   	<V.WAKE,u   	;V.WAKE?
			beq   	IRQExit   	;no
			clr 	<V.WAKE,u		;clear V.WAKE
			
			* wake up waiter for read
			IFEQ  	Level-1
			ldb   	#S$Wake
			os9   	F$Send
			ELSE
			clrb
			tfr   	d,x            ;copy process descriptor pointer
			lda   	P$State,x      ;get state flags
			anda  	#^Suspend      ;clear suspend state
			sta   	P$State,x      ;save state flags
			ENDC

IRQExit
IRQExit2  	puls  	cc,dp,pc		;restore interrupts cc,dp, return
         

			
*****************************************************************************
* Write
*
* Entry:
*    A  = character to write
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
* 
Write    	equ   	*
         	pshs	a				; character to send on stack
         	ldb		V.PORT+1,u		;port number into B
         	lda   	#OP_SERWRITE	; put command into A
         	pshs  	d
         	leax  	,s
         	ldy   	#$0003			; 3 bytes to send.. ugh.  need WRITEM (data mode)
         	IFGT  	Level-1
         	ldu   	<D.DWSUB
         	ELSE
         	ldu   	>D.DWSUB
         	ENDC
         	jsr   	6,u
WriteOK   	clrb
WriteExit 	puls	a,x,pc		; clean stack, return

	
*************************************************************************************
* Read - my crazy attempt #4

Read    	equ  	*
			pshs  	cc,dp          save IRQ/Carry status, system DP

ReadChr		orcc	#$50		;mask interrupts
			
			lda   	RxDatLen,u 	;get our Rx buffer count
			beq   	ReadSlp 	;no data, go sleep while waiting for new Rx data...
			
			* we have data waiting
			deca				;one less byte in buffer
			sta   	RxDatLen,u 	;save new Rx data count
			
			ldx   	RxBufGet,u 	;current Rx buffer pickup position
			lda   	,x+       	;get Rx character, set up next pickup position
			
			cmpx  	RxBufEnd,u 	;end of Rx buffer?
			blo   	ReadChr1   	;no, keep pickup pointer
			ldx   	RxBufPtr,u 	;get Rx buffer start address
ReadChr1   	stx   	RxBufGet,u	;set new Rx data pickup pointer
			
			* return to caller
			puls  	cc,dp,pc   	;recover IRQ/Carry status, system DP, return with character in A

ReadSlp		equ		*

           	IFEQ  	Level-1
ReadSlp2   	lda   	<V.BUSY,u
           	sta   	<V.WAKE,u		;store process id in this port's entry in the waiter table
           	lbsr  	Sleep0     	;sleep level 1 style
           	ELSE
ReadSlp2   	lda   	>D.Proc    	;process descriptor address MSB
           	sta   	<V.WAKE,u        	;save MSB in V.WAKE
           	clrb
           	tfr		d,x			;process descriptor address
           	IFNE  	H6309
           	oim   	#Suspend,P$State,x 	;suspend
           	ELSE
           	ldb   	P$State,x
           	orb   	#Suspend
           	stb   	P$State,x 	;suspend
           	ENDC
           	lbsr  Sleep1		;sleep level 2 style
           	ENDC
           	
           	* we've been awakened..
           	
           	* check for signals
           	ldx   	>D.Proc		;process descriptor address
           	ldb   	P$Signal,x 	;pending signal for this process?
           	beq   	ChkState  	;no, go check process state...
           	cmpb  	#S$Intrpt  	;(interrupt only)
           	lbls  	ErrExit    	;yes, go do it...

ChkState   	equ   	*
			* have we been condemned to die?
           	IFNE  	H6309
          	tim   	#Condem,P$State,x
          	ELSE
          	ldb   	P$State,x
           	bitb  	#Condem
           	ENDC
           	bne   	PrAbtErr 	;yes, go do it...
           	
           	* check that our waiter byte was cleared by ISR instance
			lda		<V.WAKE,u		;our waiter byte
			beq		ReadChr		;0 = its our turn, go get a character 
           	bra   	ReadSlp		;false alarm, go back to sleep

PrAbtErr	ldb   	#E$PrcAbt	;set error code

ErrExit    	equ  	*
           	IFNE  	H6309
           	oim   	#Carry,,s 	;set carry
           	ELSE
           	lda   	,s
           	ora   	#Carry
           	sta   	,s
           	ENDC
           	puls 	cc,dp,pc 	;restore CC, system DP, return

           	IFEQ  	Level-1
Sleep0     	ldx   	#$0			;sleep till ISR wakes us
           	bra   	TimedSlp
           	ENDC

Sleep1     	ldx   	#$1			;just sleep till end of slice, we are suspended (level 2)             
TimedSlp	andcc 	#^Intmasks  ;enable IRQs
			os9   	F$Sleep
           	rts          		;return


**********************************************************************
* GetStat - heavily borrowed from sc6551
*
* Entry:
*    A  = function code
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code 
*

GetStt		clrb    			;default to no error...
			pshs  	cc,dp  		;save IRQ/Carry status,system DP
           
        	ldx   	PD.RGS,y	;caller's register stack pointer
        	cmpa  	#SS.EOF		
        	beq   	GSExitOK 	;SCF devices never return EOF
           
        	cmpa  	#SS.Ready
        	bne   	GetScSiz	;next check
           	
        	* SS.Ready
        	lda   	RxDatLen,u	;get Rx data length
        	beq   	NRdyErr    	;none, go report error
        	sta   	R$B,x		;set Rx data available in caller's [B]
GSExitOK	puls  	cc,dp,pc 	;restore Carry status, system DP, return         
         
NRdyErr		ldb  	#E$NotRdy         
			bra		ErrExit		; return error code 

UnSvcErr   	ldb   	#E$UnkSvc
           	bra   	ErrExit		; return error code			
			
GetScSiz   	cmpa  	#SS.ScSiz
           	bne   	GetComSt	; next check
           	ldu   	PD.DEV,y
           	ldu   	V$DESC,u	; device descriptor
           	clra
           	ldb   	IT.COL,u	; return screen size
           	std   	R$X,x
           	ldb   	IT.ROW,u
           	std   	R$Y,x
           	puls  	cc,dp,pc	;restore Carry status, system DP, return

GetComSt   	cmpa  	#SS.ComSt
           	lbne  	UnSvcErr	;no, we have no more answers, report error
           	ldd   	FlowCtrl,u	;flow control info
           	std   	R$Y,x
           	clra                 ;default to DCD and DSR enabled
           	sta   	R$B,x		;set 6551 ACIA style DCD/DSR status in caller's [B]
           	puls  	cc,dp,pc	;restore Carry status, system DP, return			

*************************************************************************         
* SetStat
*
* Entry:
*    A  = function code
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code 
*  
* also needs much work
SetStt   
Close    cmpa  #SS.Close	close the device?
         bne   L0173
         lda   #OP_NOP	
         pshs  a
         ldy   #$0001
         leax  ,s
         IFGT  Level-1
         ldu   <D.DWSUB
         ELSE
         ldu   >D.DWSUB
         ENDC
         jsr   6,u
         puls  a,pc


L0173    comb
         ldb   #E$UnkSVc
         rts

         
         
         emod
eom      equ   *
         end