changeset 2628:3160f7080691

Atari version
author Boisy Pitre <boisy.pitre@nuance.com>
date Sat, 25 Feb 2012 18:58:20 -0600
parents 56876a9752b9
children 65b1b5c80fec
files level1/atari/modules/dw3.asm
diffstat 1 files changed, 560 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/level1/atari/modules/dw3.asm	Sat Feb 25 18:58:20 2012 -0600
@@ -0,0 +1,560 @@
+********************************************************************
+* DW3 - DriveWire 3 Low Level Subroutine Module for Atari XL/XE
+*
+* $Id$
+*
+* Edt/Rev  YYYY/MM/DD  Modified by
+* Comment
+* ------------------------------------------------------------------
+*   1      2008/01/26  Boisy G. Pitre
+* Started as a segregated subroutine module.
+*
+*   2      2010/01/20  Boisy G. Pitre
+* Added support for DWNet
+*
+*   3      2010/01/23  Aaron A. Wolfe
+* Added dynamic polling frequency
+*
+               nam       DW3
+               ttl       DriveWire 3 Low Level Subroutine Module for Atari XL/XE
+
+               ifp1      
+               use       defsfile
+               use       drivewire.d
+               use       atari.d
+               endc      
+
+tylg           set       Sbrtn+Objct
+atrv           set       ReEnt+rev
+rev            set       $01
+
+               mod       eom,name,tylg,atrv,start,0
+
+* irq
+IRQPckt        fcb       $00,$01,$0A         ;IRQ packet Flip(1),Mask(1),Priority(1) bytes
+* Default time packet
+DefTime        fcb       109,12,31,23,59,59
+
+* for dynamic poll frequency, number of ticks between firing poller - should we move to dwdefs?
+* speed 1 = interactive (typing)
+PollSpd1       fcb       3
+* speed 2 = bulk transfer (depending on how much processing needs to be done to incoming stream, 5-8 seems good)
+PollSpd2       fcb       6
+* speed 3 = idle 
+PollSpd3       fcb       40
+* X pollidle -> drop to next slower rate
+PollIdle       fcb       60
+
+
+name           fcs       /dw3/
+
+* DriveWire subroutine entry table
+start          lbra      Init
+               bra       Read
+               nop       
+               lbra      Write
+
+* Term
+*
+* Entry:
+*    U  = address of device memory area
+*
+* Exit:
+*    CC = carry set on error
+*    B  = error code
+*
+Term                     
+               clrb                          clear Carry
+               rts       
+
+               use       dwread.asm
+               
+               use       dwwrite.asm
+               
+
+* Init
+*
+* Entry:
+*    Y  = address of device descriptor
+*    U  = address of device memory area
+*
+* Exit:
+*    CC = carry set on error
+*    B  = error code
+*
+* Initialize the serial device
+Init                     
+               clrb                          clear Carry
+               pshs      y,x,cc              then push CC on stack
+               orcc      #IntMasks
+
+* initialize POKEY to ~38.4K
+*               ldd       #$1000
+*               exg       a,b
+*               std       AUDF3
+
+*     		lda	#$23
+*	     	sta	SKCTL
+
+*               lda  #$28	     clock ch. 3 with 1.79 MHz, ch. 4 with ch. 3
+*               sta  AUDCTL	set audio control
+
+* allocate DW statics page
+               pshs      u
+               ldd       #$0100
+               os9       F$SRqMem
+               tfr       u,x
+               puls      u
+               lbcs      InitEx
+               ifgt      Level-1
+               stx       <D.DWStat
+               else      
+               stx       >D.DWStat
+               endc      
+; clear out 256 byte page at X
+               clrb      
+loop@          clr       ,x+
+               decb      
+               bne       loop@
+
+* send OP_DWINIT
+         ; setup DWsub command
+               pshs      u
+               ldb		 #1					 ; DRIVER VERSION
+               lda       #OP_DWINIT          ; load command
+               pshs      d                   ; command store on stack
+               leax      ,s                  ; point X to stack 
+               ldy       #2                  ; 1 byte to send
+               ifgt      Level-1
+               ldu       <D.DWSubAddr
+               else      
+               ldu       >D.DWSubAddr
+               endc      
+               jsr       6,u                 ; call DWrite
+               leas      1,s                 ; leave one byte on stack for response 
+               
+               ; read protocol version response, 1 byte
+               leax      ,s                  ; point X to stack head
+               ldy       #1                  ; 1 byte to retrieve
+               jsr       3,u                 ; call DWRead
+               IFNE      atari-1
+               beq       InstIRQ             ; branch if no error
+               ENDC
+               leas      3,s                 ; error, cleanup stack (u and 1 byte from read) 
+               lbra      InitEx            	 ; don't install IRQ handler
+
+* install ISR
+InstIRQ                  
+			   puls      a,u		; a has proto version from server.. not used yet
+
+			   ifgt      Level-1
+               ldx       <D.DWStat
+               else      
+               ldx       >D.DWStat
+               endc      
+               leax      DW.VIRQPkt,x
+               pshs      u
+               tfr       x,u
+               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
+               os9       F$IRQ               ;install
+               puls      u
+               bcs       InitEx              ;exit with error
+               clra      
+               ldb       PollSpd3,pcr        ; start at idle
+               ifgt      Level-1
+               ldx       <D.DWStat
+               else      
+               ldx       >D.DWStat
+               endc      
+               leax      DW.VIRQPkt,x
+               std       Vi.Rst,x            ; reset count
+               tfr       x,y                 ; move VIRQ software packet to Y
+tryagain                 
+               ldx       #$0001              ; code to install new VIRQ
+               os9       F$VIRQ              ; install
+               bcc       IRQok               ; no error, continue
+               cmpb      #E$UnkSvc
+               bne       InitEx
+; if we get an E$UnkSvc error, then clock has not been initialized, so do it here
+               leax      DefTime,pcr
+               os9       F$STime
+               bra       tryagain            ; note: this has the slim potential of looping forever
+IRQok                    
+               ifgt      Level-1
+               ldx       <D.DWStat
+               else      
+               ldx       >D.DWStat
+               endc      
+; cheat: we know DW.StatTbl is at offset $00 from D.DWStat, do not bother with leax
+               leax      DW.StatTbl,x
+               tfr       u,d
+               ldb       <V.PORT+1,u         ; get our port #
+               sta       b,x                 ; store in table
+
+InitEx                   
+               puls      cc,x,y,pc
+
+
+; ***********************************************************************
+; Interrupt handler  - Much help from Darren Atkinson
+
+IRQMulti3      anda      #$0F                ; mask first 4 bits, a is now port #+1
+               deca                          ; we pass +1 to use 0 for no data
+               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.DWSubAddr
+               else      
+               ldu       >D.DWSubAddr
+               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
+
+               lbra      CkSSig              ; had to lbra
+
+IRQMulti                 
+		  ; set IRQ freq for bulk
+               pshs      a
+               lda       PollSpd2,pcr
+               lbsr      IRQsetFRQ
+               puls      a
+
+          ; 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      
+               lbne      CkSSig              ;had to lbra
+               lbra      IRQExit             ;had to lbra
+
+
+; **** IRQ ENTRY POINT
+IRQSvc         equ       *
+               pshs      cc,dp               ; save system cc,DP
+               orcc      #IntMasks           ; mask interrupts
+
+          ; mark VIRQ handled (note U is pointer to our VIRQ packet in DP)
+               lda       Vi.Stat,u           ; VIRQ status register
+               anda      #^Vi.IFlag          ; clear flag in VIRQ status register
+               sta       Vi.Stat,u           ; 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.DWSubAddr
+               else      
+               ldu       >D.DWSubAddr
+               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
+               lbra      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)
+               bne       IRQGotOp            ; branch if D != 0 (something to do)
+* this is a NOP response.. do we need to reschedule
+               ifgt      Level-1
+               ldx       <D.DWStat
+               else      
+               ldx       >D.DWStat
+               endc      
+               lda       DW.VIRQPkt+Vi.Rst+1,x
+               cmpa      PollSpd3,pcr
+               lbeq      IRQExit             ;we are already at idle speed
+
+               lda       DW.VIRQNOP,x
+               inca      
+               cmpa      PollIdle,pcr
+               beq       FRQdown
+
+               sta       DW.VIRQNOP,x        ;inc NOP count, exit
+               lbra      IRQExit
+
+FRQdown        lda       DW.VIRQPkt+Vi.Rst+1,x
+               cmpa      PollSpd1,pcr
+               beq       FRQd1
+               lda       PollSpd3,pcr
+FRQd2                    
+               sta       DW.VIRQPkt+Vi.Rst+1,x
+               clr       DW.VIRQNOP,x
+               lbra      IRQExit
+FRQd1          lda       PollSpd2,pcr
+               bra       FRQd2
+
+; save back D on stack and build our U
+IRQGotOp       pshs      d
+          * mode switch on bits 7+6 of A: 00 = vserial, 01 = system, 10 = wirebug?, 11 = ?							
+               anda      #$C0                ; mask last 6 bits
+               beq       mode00              ; virtual serial mode
+          					; future - handle other modes
+               lbra      IRQExit             ; for now, bail
+
+mode00         lda       ,s                  ; restore A		  
+               anda      #$0F                ; mask first 4 bits, a is now port #+1
+               beq       IRQCont             ; if we're here with 0 in the port, its not really a port # (can we jump straight to status?)
+               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
+               ifgt      Level-1
+               ldx       <D.DWStat
+               else      
+               ldx       >D.DWStat
+               endc      
+; cheat: we know DW.StatTbl is at offset $00 from D.DWStat, do not bother with leax
+;			leax    DW.StatTbl,x
+               lda       a,x
+               bne       IRQCont             ; if A is 0, then this device is not active, so exit
+               puls      d
+               lbra      IRQExit
+IRQCont                  
+               clrb      
+               tfr       d,u
+
+               puls      d
+
+          * multiread/status flag is in bit 4 of A
+               bita      #$10
+               beq       IRQPutch            ; branch for read1 if multiread not set
+
+          * all 0s in port means status, anything else is multiread
+
+               bita      #$0F                ;mask bit 7-4
+               beq       dostat              ;port # all 0, this is a status response
+               lbra      IRQMulti            ;its not all 0, this is a multiread
+
+
+		 * in status events, databyte is split, 4bits status, 4bits port #          
+dostat         bitb      #$F0                ;mask low bits
+               lbne      IRQExit             ;we only implement code 0000, term
+			* set u to port #
+               ifgt      Level-1
+               ldx       <D.DWStat
+               else      
+               ldx       >D.DWStat
+               endc      
+               lda       b,x
+               bne       statcont            ; if A is 0, then this device is not active, so exit
+               lbra      IRQExit
+
+* IRQ set freq routine
+* sets freq and clears NOP counter
+* a = desired IRQ freq
+IRQsetFRQ      pshs      x                   ; preserve
+               ifgt      Level-1
+               ldx       <D.DWStat
+               else      
+               ldx       >D.DWStat
+               endc      
+               sta       DW.VIRQPkt+Vi.Rst+1,x
+* +++ BGP +++ added following line so that the counter (which was copied by
+* clock before calling us) gets reset to the same value the reset value. Without
+* this line, we get called again with the PRIOR Vi.Rst value.
+               sta       DW.VIRQPkt+Vi.Cnt+1,x
+               clr       DW.VIRQNOP,x
+               puls      x
+               rts       
+
+
+* This routine roots through process descriptors in a queue and
+* checks to see if the process has a path that is open to the device
+* represented by the static storage pointer in U. if so, the S$HUP
+* signal is sent to that process
+*
+* Entry: X = process descriptor to evaluate
+*        U = static storage of device we want to check against
+RootThrough              
+               ldb       #NumPaths
+               leay      P$Path,x
+               pshs      x
+loop           decb      
+               bmi       out
+               lda       ,y+
+               beq       loop
+               pshs      y
+               ifgt      Level-1
+               ldx       <D.PthDBT
+               else      
+               ldx       >D.PthDBT
+               endc      
+               os9       F$Find64
+               ldx       PD.DEV,y
+               leax      V$STAT,x
+               puls      y
+               bcs       out
+
+               cmpu      ,x
+               bne       loop
+
+               ldx       ,s
+               lda       P$ID,x
+               ldb       #S$HUP
+               os9       F$Send
+
+out            puls      x
+               ldx       P$Queue,x
+               bne       RootThrough
+               rts       
+
+statcont       clrb      
+               tfr       d,u
+* NEW: root through all process descriptors. if any has a path open to this
+* device, send then S$HUP
+               ldx       <D.AProcQ
+               beq       dowaitq
+               bsr       RootThrough
+dowaitq        ldx       <D.WProcQ
+               beq       dosleepq
+               bsr       RootThrough
+dosleepq       ldx       <D.SProcQ
+               beq       CkLPRC
+               bsr       RootThrough
+
+CkLPRC                   
+               lda       <V.LPRC,u
+               beq       IRQExit             ; no last process, bail
+               ldb       #S$HUP
+               os9       F$Send              ; send signal, don't think we can do anything about an error result anyway.. so
+               bra       CkSuspnd            ; do we need to go check suspend?
+
+; put byte B in port As buffer - optimization help from Darren Atkinson       
+IRQPutCh                 
+		  ; set IRQ freq for bulk
+               lda       PollSpd1,pcr
+               lbsr      IRQsetFRQ
+               ldx       RxBufPut,u          ; point X to the data buffer
+
+; process interrupt/quit characters here
+; note we will have to do this in the multiread (ugh)
+               tfr       b,a                 ; put byte in A
+               ldb       #S$Intrpt
+               cmpa      V.INTR,u
+               beq       send@
+               ldb       #S$Abort
+               cmpa      V.QUIT,u
+               bne       store
+send@          lda       V.LPRC,u
+               beq       IRQExit
+               os9       F$Send
+               bra       IRQExit
+
+store                    
+          ; store our data byte
+               sta       ,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
+
+CkSSig                   
+               lda       <SSigID,u           ; send signal on data ready?
+               beq       CkSuspnd
+               ldb       <SSigSg,u           ; else get signal code
+               os9       F$Send
+               clr       <SSigID,u
+               bra       IRQExit
+
+          ; 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
+
+               emod      
+eom            equ       *
+               end