view level1/modules/scdwn.asm @ 2385:cd29118ee172

Fixed SCF to allow it to return if SS.Open errored out.
author boisy
date Sun, 24 Jan 2010 00:40:46 +0000
parents 67b11fbf5253
children e454c42a134b
line wrap: on
line source

********************************************************************
* scdwn - CoCo DriveWire Network Driver
*
* $Id$
*
* How this wildcard /N stuff works:
* A process interested in obtaining a path to a network device
* can open any /Nx descriptor but it may be in use by another process.
* The safest way to obtain a new network device is to open the wildcard
* descriptor /N. This works similarly to the way /W works in the CoCo 3
* VTIO driver.
*
* When /N is open, the INIT routine is called but most of it is bypassed
* because it detects the wildcard device (which has an address of $FFFF). 
*
* The SS.Open I$SetStat entry point is called. That routine also detects
* the wildcard is used, so the hunt is on for the next available /Nx
* descriptor (it does this by checking the DW static storage for each /Nx
* in the DW static storage page).  If it finds a free page, it notes the
* offset as a number, then builds a descriptor name (ex: /N4) and then
* F$Links to it.  It then takes the descriptor module address and sticks it
* in the device table entry for /N.  It also takes the V.PORT address and sticks
* it in the static storage memory allocated for /N.
* Afer that, SS.Open does something really sneaky: it branches into the
* Init routine which detects via the static storage that this is not a /N 
* descriptor but a /N4 descriptor.  Init then sends the SERINIT and initializes
* the DW static storage for /N4.  Control is returned to the SS.Open code which
* then advertises the SS.Open to the server.
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*   1      2009/11/30  Aaron Wolfe
* Started
*
*          2009/12/28  Boisy G. Pitre
* Modified so that F$STime is called if we get an error on calling
* F$VIRQ (which means the clock module has not be initialized)
*
*          2009/12/31  Boisy G. Pitre
* Fixed crash in Init where F$Link failure would not clean up stack
*
*          2010/01/03  Boisy G. Pitre
* Moved IRQ stuff into DW3 subroutine module
*
*   2      2010/01/23  Boisy G. Pitre
* Added code in SS.Open to use /N wildcard device (tricky stuff!)

               nam       scdwn
               ttl       CoCo DriveWire Network Driver

               ifp1      
               use       defsfile
               use       dwdefs.d
               endc      


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

* Note: driver memory defined in dwdefs.d
               mod       eom,name,tylg,atrv,start,SCFDrvMemSz

* module info         	
               fcb       UPDAT.+SHARE.       ;driver access modes
name           fcs       /scdwn/             ;driver name
               fcb       edition             ;driver edition 

* dispatch calls            
start          equ       *
               lbra      Init
               lbra      Read
               lbra      Write
               lbra      GetStat
               lbra      SetStat

***********************************************************************
* 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   
Term           equ       *
               lda       <V.PORT+1,u         ;get our port #
 bmi termbye
               pshs      a                   ;port # on stack
          * clear statics table entry
               ifgt      Level-1
               ldx       <D.DWStat
               else      
               ldx       >D.DWStat
               endc      
               beq       tell
; cheat: we know DW.StatTbl is at offset $00 from D.DWStat, do not bother with leax
;		leax      DW.StatTbl,x
               clr       a,x                 ;clear out

          ; tell server
tell                     
               lda       #OP_SERTERM         ; load command
               pshs      a                   ; command store on stack
               leax      ,s                  ; point X to stack 
               ldy       #2                  ; 2 bytes to send 

               pshs      u

               ifgt      Level-1
               ldu       <D.DWSubAddr
               else      
               ldu       >D.DWSubAddr
               endc      
               beq       nosub
               jsr       6,u                 ; call DWrite

nosub                    
               puls      u
               leas      2,s                 ; clean 3 DWsub args from stack 
termbye        clrb      
               rts       

***********************************************************************
* Init
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*

Init           equ       *

; link to subroutine module
; has the link already been done?
               ifgt      Level-1
               ldx       <D.DWSubAddr
               else      
               ldx       >D.DWSubAddr
               endc      
               bne       already             ; if so, do not bother

               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      
               bcs       InitEx2
               ifgt      Level-1
               sty       <D.DWSubAddr
               else      
               sty       >D.DWSubAddr
               endc      
               jsr       ,y                  ; call DW init routine

               puls      u                   ; restore u

already                  
; tell DW we have a new port opening (port mode already on stack)
               ldb       <V.PORT+1,u         ; get our port #	
* if /N wildcard, skip advertising via SERINIT
 bmi initEx              
               lda       #OP_SERINIT         ; command 
               pshs      d                   ; command + port # on stack
               leax      ,s                  ; point X to stack 
               ldy       #2                  ; # of bytes to send

               pshs      u
               ifgt      Level-1
               ldu       <D.DWSubAddr
               else      
               ldu       >D.DWSubAddr
               endc      
               jsr       6,u                 ; call DWrite
               puls      u

; 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

               tfr       u,d                 ; (A = high page of statics)
               puls      b
               puls      b                   ; (B = port number)
               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
               sta       b,x
InitEx         equ       *
               rts       
InitEx2                  
               puls      u
               rts       

; drivewire info
dw3name        fcs       /dw3/


*****************************************************************************
* 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.DWSubAddr
               else      
               ldu       >D.DWSubAddr
               endc      
               jsr       6,u
WriteOK        clrb      
WriteExit      puls      a,x,pc              ; clean stack, return


NotReady       comb      
               ldb       #E$NotRdy
               rts       

*************************************************************************************
* Read
*
* Entry:
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    A  = character read
*    CC = carry set on error
*    B  = error code
*
Read           equ       *
* Check to see if there is a signal-on-data-ready set for this path.
* If so, we return a Not Ready error.
               lda       <SSigID,u           data ready signal trap set up?
               bne       NotReady            yes, exit with not ready error
               pshs      cc,dp               ; save IRQ/Carry status, system DP

ReadChr        orcc      #IntMasks           ; 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      
               bsr       Sleep1              ; sleep level 2 style
               endc      

          ; we have 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$HUP              ; (S$HUP or lower)
               bls       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
               tst       <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
               clr       <V.WAKE,u
               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 
*
GetStat                  
               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       Advertise           ; 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			

; We advertise all of our SERGETSTAT calls (except SS.Ready) to the server
Advertise                
               ldb       #OP_SERGETSTAT
               bsr       SendStat

; Note: Here we could somehow obtain the size of the terminal window from the server
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
               bne       UnSvcErr            ; no, we have no more answers, report error
               ldd       #$0000              ; not used, return $0000
               std       R$Y,x
               sta       R$B,x
               puls      cc,dp,pc            ; restore Carry status, system DP, return			

* Advertise Stat Code to server
* A = Function Code
* B = OP_SERGETSTAT or OP_SERSETSTAT
SendStat                 
; advertise our GetStt code to the server
               pshs      a,y,x,u
               leas      -3,s
               leax      ,s
               stb       ,x
               sta       2,x
               ldb       V.PORT+1,u
               stb       1,x
               ldy       #$0003
               ifgt      LEVEL-1
               ldu       <D.DWSubAddr
               else      
               ldu       >D.DWSubAddr
               endc      
               jsr       6,u
               leas      3,s
               puls      a,y,x,u,pc

*************************************************************************         
* 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 
*
SetStat                  
               cmpa      #SS.Open
               bne       isitcomst
               bsr       open
               bcs       ssbye
               ldd       #SS.Open*256+OP_SERSETSTAT
               bra       SendStat
isitcomst                              
               ldb       #OP_SERSETSTAT
               bsr       SendStat
               cmpa      #SS.ComSt
               beq       comst
               cmpa      #SS.Close
               beq       ex
               cmpa      #SS.SSig
               beq       ssig
               cmpa      #SS.Relea
               lbne      donebad
relea          lda       PD.CPR,y            get curr proc #
               cmpa      <SSigID,u           same?
               bne       ex
               clr       <SSigID,u           clear process id
ex             rts       
ssig           pshs      cc
               orcc      #IntMasks
               lda       PD.CPR,y            ; get curr proc #
               ldx       PD.RGS,y
               ldb       R$X+1,x             ; get user signal code
               tst       RxDatLen,u          ; get Rx data length
               beq       ssigsetup           ; branch if no data in buffer
* if here, we have data so send signal immediately
               os9       F$Send
               puls      cc,pc
ssigsetup      std       <SSigID,u           ; save process ID & signal
               puls      cc,pc

comst          leax      PD.OPT,y
               ldy       #OPTCNT
               ifgt      LEVEL-1
               ldu       <D.DWSubAddr
               else      
               ldu       >D.DWSubAddr
               endc      
               jsr       6,u
               clrb      
ssbye          rts       

* SS.Open processor
* Entry: X=Register stack pointer
*        U=Static memory pointer
*        Y=Path descriptor pointer
open     pshs  u,y        preserve registers
         tst   <V.PORT+1,u         check if this is $FFFF (wildcard)
         lbpl  L0BCD
         ldx   PD.DEV,y   get pointer to device table entry
         ldx   V$DESC,x   get pointer to /N descriptor
         pshs  x          save device descriptor pointer
* start at /N1
         ldb       #1      
L0B58    equ   *
         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
next     cmpb      #DW.StatCnt-1
         bge       opexer
         tst       b,x
         beq       found
         incb      
         bra       next
opexer   comb             set carry
         ldb   #E$MNF     get module not found error
         puls  x,y,u,pc      purge stack and return
         
* Found a free spot
found    pshs  b          save # of free entry
         leas  -5,s
         leay  ,s
         ldb   #'N        get netdev name prefix
         stb   ,y+        put it in buffer
         ldb   5,s        get netdev # that was free
* Convert netdev # in B to ASCII eqivalent with high bit set

         IFNE  H6309
         divd  #10        divide it by 10
         ELSE
         lda   #-1
L0B87b   inca
         subb  #10
         bcc   L0B87b
         addb  #10
         exg   a,b
         cmpb  #0
         ENDC
         beq   L0B87      if answer is 0 there is only 1 digit, skip ahead 
         orb   #$30       make first digit ASCII
         stb   ,y+        put it in buffer
L0B87    ora   #$B0       make remainder ASCII with high bit set
         sta   ,y+        put it in buffer
L0B92    leas  -2,s       make a buffer for process decriptor pointer
         IFGT  Level-1
         lbsr  L0238      switch to system process descriptor
         ENDC
         leax  2,s         Point to calculated dsc. name
         lda   #Devic+Objct get module type
         os9   F$Link     try & link it
         IFGT  Level-1
         pshs  cc
         lbsr  L0244      switch back to current process
         puls  cc
         ENDC
         leas  7,s        purge stack
         bcc   L0BAB      it's linked, skip ahead
L0BA7    puls  b          get original number
         incb
         bra   L0B58      go find another free space

         IFGT  Level-1
* Switch to system process descriptor
L0238    pshs  d          Preserve D
         ldd   <D.Proc    Get current process dsc. ptr
         std   4,s        Preserve on stack
         ldd   <D.SysPrc  Get system process dsc. ptr
         std   <D.Proc    Make it the current process
         puls  d,pc       Restore D & return

* Switch back to current process
L0244    pshs  d          Preserve D
         ldd   4,s        Get current process ptr
         std   <D.Proc    Make it the current process
         puls  d,pc       Restore D & return
         ENDC
         
* Got a device descriptor, put into device table & save netdev # into static
L0BAB    
         lda   M$PORT+2,u get MSB of port byte of newly linked /N? descriptor
         ldy   3,s        get path descriptor pointer
         ldx   PD.DEV,y   get pointer to device table
         stu   V$DESC,x   save pointer to descriptor into it
         ldu   1,s        get pointer to descriptor
         os9   F$UnLink   unlink it from system map
         ldu   5,s        get static mem pointer
         sta   V.PORT+1,u
         leas  7,s        purge stack
* Load Y with address of descriptor and U with address of memory area
 ldy   V$DESC,x
 pshs  x,y,u
 lbsr  Init
 puls  x,y,u,pc

L0BCD    clrb             No error
         puls             y,u,pc

               ifeq      1
SetPortSig               
               cmpa      #SS.PortSig
               bne       SetPortRel
               lda       PD.CPR,y            current process ID
               ldb       R$X+1,x             LSB of [X] is signal code
               std       <PortSigPID
               clrb      
               rts       
SetPortRel               
               cmpa      #SS.PortRel
               bne       donebad
               leax      PortSigPID,u
               bsr       ReleaSig
               clrb      
               rts       
               endc      
donebad        comb      
               ldb       #E$UnkSvc
               rts       

ReleaSig       pshs      cc                  save IRQ enable status
               orcc      #IntMasks           disable IRQs while releasing signal
               lda       PD.CPR,y            get current process ID
               suba      ,x                  same as signal process ID?
               bne       NoReleas            no, go return...
               sta       ,x                  clear this signal's process ID
NoReleas       puls      cc,pc               restore IRQ enable status, return

               emod      
eom            equ       *
               end