view level2/modules/cc3disk.asm @ 1534:46c68f878889

rbfdefs moved up into defsfile
author boisy
date Fri, 16 Apr 2004 23:44:32 +0000
parents 9bc515084837
children 4a4ff4d844ef
line wrap: on
line source

********************************************************************
* ccxdisk - CoCo Disk Controller Driver
*
* A lot of references to **.CYL or <u00B6 using 16 bit registers can be
* changed to 8 bit registers with a +1 offset, since track #'s >255 are
* ignored
*
* NOTE: 512 bytes is reserved as a physical sector buffer. Any reads/
*  writes are done from this buffer to the controller. Copies of the 256
*  byte chunk needed are done by a block memory move

* $Id$
*
* Edt/Rev  YYYY/MM/DD  Modified by
* Comment
* ------------------------------------------------------------------
*  11      1993/05/12  ???
* Special opts for TC9 to slow controller reads and writes TFM's
* between sector buffers & in drive table init/copies.
* Changed software timing loop (drive spin-up) to F$Sleep for 32 ticks
* Shrunk (slowed slightly) error returns
* Added blobstop code
*
*  11r1    2003/09/03  Boisy G. Pitre
* Added code to sense if HW is present or not and return error if not.

         nam   ccxdisk
         ttl   WD1773 Disk Controller Driver

TC9      equ   0              Set to 1 for TC9 special slowdowns
PRECOMP  equ   0              Set to 1 to turn on write precompensation

         IFP1
         use   defsfile
         ENDC

tylg     set   Drivr+Objct   
atrv     set   ReEnt+rev
rev      set   $01
edition  set   11

         mod   eom,name,tylg,atrv,start,size

u0000    rmb   DRVBEG+(DRVMEM*4)
u00A7    rmb   2              Last drive table accessed (ptr)
u00A9    rmb   1              Bit mask for control reg (drive #, side,etc)
u00AA    rmb   1
sectbuf  rmb   2              Ptr to 512 byte sector buffer
u00AD    rmb   1
u00AE    rmb   1
FBlock   rmb   2              block number for format
         IFGT  Level-1
FTask    rmb   1              task number for format
         ENDC
u00B1    rmb   2              Vi.Cnt word for VIRQ
u00B3    rmb   2              Vi.Rst word for VIRQ
u00B5    rmb   1              Vi.Stat byte for VIRQ (drive motor timeout)
u00B6    rmb   2              OS9's logical sector #
u00B8    rmb   1              PCDOS (512 byte sector) sector #
size     equ   .

         fcb   DIR.+SHARE.+PEXEC.+PWRIT.+PREAD.+EXEC.+UPDAT.

name     equ   *
         IFEQ  Level-1
         fcs   /CCDisk/
         ELSE
         fcs   /CC3Disk/
         ENDC
         fcb   edition

VIRQCnt  fdb   $00F0          Initial count for VIRQ (240)

IRQPkt   fcb   $00            Normal bits (flip byte)
         fcb   $01            Bit 1 is interrupt request flag (Mask byte)
         fcb   10             Priority byte

* Init
*
* Entry:
*    Y  = address of device descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*     
* New code added 09/03/2003 by Boisy G. Pitre
* Write a pattern to $FF4B and read it back to verify that the hardware
* does exist.
Init     ldx   V.PORT,u       Get Base port address
         lda   $0B,x          get byte at FDC Data register
         coma                 complement it to modify it
         sta   $0B,x          write it
         clrb
Init2    decb                 delay a bit...
         bmi   Init2
         suba  $0B,x          read it back
         lbne  NoHW           if not zero, we didn't read what we wrote
**
         clr   <D.MotOn       flag drive motor as not running
         leax  8,x            Point to Status/Command register
         lda   #$D0           Force Interrupt command
         sta   ,x             Send to FDC
         lbsr  L0406          Time delay for ~ 108 cycles
         lda   ,x             Eat status register
         ldd   #$FF04         'Invalid' value & # of drives
         sta   >u00B8,u       Set 512 byte sector # to bogus value
         sta   >u00B8+1,u
         leax  DRVBEG,u       Point to start of drive tables
L004B    sta   ,x             DD.TOT MSB to bogus value
         sta   <V.TRAK,x      Init current track # to bogus value
         leax  <DRVMEM,x      Point to next drive table
         decb                 Done all 4 drives yet?
         bne   L004B          No, init them all
         leax  >NMISvc,pc     Point to NMI service routine
         stx   <D.NMI         Install as system NMI
         pshs  y              Save device dsc. ptr
         leay  >u00B5,u       Point to Vi.Stat in VIRQ packet
         tfr   y,d            Make it the status register ptr for IRQ
         leay  >IRQSvc,pc     Point to IRQ service routine
         leax  >IRQPkt,pc     Point to IRQ packet
         os9   F$IRQ          Install IRQ
         puls  y              Get back device dsc. ptr
         bcs   Return         If we can't install IRQ, exit
         ldd   #512           Request 512 byte sector buffer
         pshs  u              Preserve device mem ptr
         os9   F$SRqMem       Request sector buffer
         tfr   u,x            Move ptr to sector buffer to x
         puls  u              Restore device mem ptr
         bcs   Return         If error, exit with it
         stx   >sectbuf,u     Save ptr to sector buffer

* GetStat
*
* 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                 no GetStt calls - return, no error, ignore
Return   rts   

* Term
*
* Entry:
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Term     leay  >u00B1,u       Point to VIRQ packet
         IFNE  H6309
         tfr   0,x            "remove"
         ELSE
         ldx   #$0000
         ENDC
         os9   F$VIRQ         Remove VIRQ
         IFNE  H6309
         tfr   0,x            "remove"
         ELSE
         ldx   #$0000
         ENDC
         os9   F$IRQ          Remove IRQ
         pshs  u              Save device mem ptr
         ldu   >sectbuf,u     Get pointer to sector buffer
         ldd   #512           Return sector buffer memory
         os9   F$SRtMem 
         puls  u              Restore device mem ptr
         clr   >DPort+$00     shut off drive motors
         clr   <D.MotOn       Clear out drive motor timeout flag
L00AB    rts                  return

* Check if 512 byte sector conversion needed
* Entry: B:X=LSN
*          U=Static mem ptr
*          Y=Path dsc. ptr
* Exit:    X=New LSN (same as original for 256 byte sectors, 1/2 of original
*            for 512 byte sectors
L00AC    pshs  x,b            Save LSN
         stx   >u00B6,u       Save OS9 LSN
         lda   <PD.TYP,y      Get device type from path dsc.
         anda  #%00000100     Mask out all but 512 byte sector flag
         bne   L00BB          512 byte sectors, go process
L00CA    puls  pc,x,b         Restore LSN & return

* 512 byte sector processing goes here
L00BB    puls  x,b            Get back LSN
         clrb                 Clear carry for rotate (also high byte of LSN)
         tfr   x,d            Move to mathable register
         IFNE  H6309
         rord                 Divide LSN by 2
         ELSE
         rora
         rorb
         ENDC
         tfr   d,x            Move new LSN back to X
         stx   >u00B8,u       Save 'physical' LSN (for controller)
         clrb                 No error & return
         rts   

start    lbra  Init
         bra   Read
         nop
         lbra  Write
         bra   GetStat
         nop
         lbra  SetStat
         bra   Term
         nop

* Read
*
* Entry:
*    B  = MSB of LSN
*    X  = LSB of LSN
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Read     bsr   L00AC          Go check for 512 byte sector/adjust if needed
         lda   #%10010001     Error flags (see Disto SCII source)
         pshs  x              Preserve sector #
         lbsr  L0162          Go read the sector
         puls  x              Restore sector #
         bcs   L00AB          If error, exit
         pshs  y,x            Save path dsc ptr & LSN
         leax  ,x             LSN0?
         bne   L012D          No, go calculate normally
         puls  y,x            Yes, restore path dsc ptr & LSN
         lda   <PD.TYP,y      Get type from path dsc.
         bita  #TYP.NSF       Standard OS-9 format?
         beq   L00F0          Yes, skip ahead
         lbsr  L051A
         pshs  y,x            save path dsc ptr
         bra   L012D

* LSN0, standard OS-9 format
L00F0    ldx   >sectbuf,u     Get ptr to sector buffer
         pshs  y,x            Preserve path dsc. ptr & sector buffer ptr
         ldy   >u00A7,u       Get last drive table accessed ptr
         IFNE  H6309
         ldw   #DD.SIZ        # bytes to copy from new LSN0 to drive table
         tfm   x+,y+          Copy them
         ELSE
         ldb   #DD.SIZ
L00F0Lp  lda   ,x+
         sta   ,y+
         decb
         bne   L00F0Lp
         ENDC
         ldy   >u00A7,u       Get drive table ptr back
         lda   <DD.FMT,y      Get format for disk in drive
         ldy   2,s            restore path descriptor pointer
         ldb   <PD.DNS,y      Get path's density settings
         bita  #FMT.DNS       Disk in drive double density?
         beq   L0115          No, all drives can read single, skip ahead
         bitb  #DNS.MFM       Can our path dsc. handle double density?
         beq   erbtyp         No, illegal
L0115    bita  #FMT.TDNS      Is new disk 96 tpi?
         beq   L011D          No, all drives handle 48 tpi, so skip ahead
         bitb  #DNS.DTD       Can path dsc. handle 96 tpi?
         beq   erbtyp         No, illegal
L011D    bita  #FMT.SIDE      Is new disk double sided?
         beq   L0128          No, all drives handle single sided, we're done
         lda   <PD.SID,y      Get # sides path dsc. can handle
         suba  #2             sides higher or equal to 2?
         blo   erbtyp         Yes, exit with illegal type error
L0128    clrb                 No error
*        puls  y,x            ??? 2 USELESS LINES?
*        pshs  y,x
* LSN's other than 0 come straight here
L012D    ldy   2,s            Get path dsc. ptr back??
         ldx   PD.BUF,y       Get path dsc. buffer ptr
         lda   <PD.TYP,y      Get path dsc. disk type
         ldy   >sectbuf,u     Get ptr to sector buffer
         IFNE  H6309
         ldw   #256           OS9 sector size (even if physical was 512)
         ENDC
         anda  #%00000100     Mask out all but 512 byte sector flag
         beq   L014B          If normal sector, just copy it
         ldd   >u00B6,u       Get OS9's LSN (twice of the 'real' 512 sector)
         andb  #$01           Mask out all but odd/even sector indicator
         beq   L014B          Even, use 1st half of 512 byte sector
         IFNE  H6309
         addr  w,y            Odd, bump sector buffer ptr to 2nd half
         ELSE
         leay  256,y
         ENDC
L014B    equ   *
         IFNE  H6309
         tfm   y+,x+          Copy from physical sector buffer to PD buffer
         puls  pc,y,x         restore path dsc & sector buffer ptrs & return
         ELSE
         pshs  d
         clrb
L014BLp  lda   ,y+
         sta   ,x+
         decb
         bne   L014BLp
         puls  pc,y,x,d       restore path dsc & sector buffer ptrs & return
         ENDC

erbtyp   comb  
         ldb   #E$BTyp        Error - wrong type error
         puls  pc,y,x

* Read error - retry handler
L0159    bcc   L0162          Normal retry, try reading again
         pshs  x,d            Preserve regs
         lbsr  sktrk0         Seek to track 0 (attempt to recalibrate)
         puls  x,d            Restore regs & try reading again

L0162    pshs  x,d            Preserve regs
         bsr   L016F          Go read sector
         puls  x,d            Restore regs (A=retry flags)
         bcc   L01D7          No error, return
         lsra                 Shift retry flags
         bne   L0159          Still more retries allowed, go do them
* otherwise, final try before we give up
L016F    lbsr  L02AC          Do double-step/precomp etc. if needed, seek
         bcs   L01D7          Error somewhere, exit with it
L0176    ldx   >sectbuf,u     Get physical sector buffer ptr
*         pshs  y,cc           Preserve timeout timer & CC
         ldb   #$80           Read sector command
         bsr   L01A1          Send to controller & time delay to let it settle
*** Next few lines are commented out for blobstop patches
*L0180    bita  >DPort+$08     Check status register
*         bne   L0197          Eat it & start reading sector
*         leay  -1,y           Bump timeout timer down
*         bne   L0180          Keep trying until it reaches 0 or sector read
*         lda   >u00A9,u       Get current drive settings
*         ora   #%00001000     Turn drive motor on
*         sta   >DPort+$00     Send to controller
*         puls  y,cc           Restore regs
*         lbra  L03E0          Exit with Read Error
*** Blobstop fixes
         stb   >DPort+$00     send command to FDC
         nop                  allow HALT to take effect
         nop
         bra   L0197          and a bit more time
* Read loop - exited with NMI
* Entry: X=ptr to sector buffer
*        B=Control register settings
L0197    lda   >DPort+$0B     Get byte from controller
         sta   ,x+            Store into sector buffer
*         stb   >DPort+$00     Drive info
         nop              -- blobstop fix
         bra   L0197          Keep reading until sector done

L01A1    orcc  #IntMasks      Shut off IRQ & FIRQ
         stb   >DPort+$08     Send command
*         ldy   #$FFFF
         ldb   #%00101000     Double density & motor on
         orb   >u00A9,u       Merge with current drive settings
         stb   >DPort+$00     Send to control register
         ldb   #%10101000     Enable halt, double density & motor on
         orb   >u00A9,u       Merge that with current drive settings
         lbra  L0406          Time delay to wait for command to settle
*         lda   #$02
*L01BE    rts   

* Write
*
* Entry:
*    B  = MSB of LSN
*    X  = LSB of LSN
*    Y  = address of path descriptor
*    U  = address of device memory area
*
* Exit:
*    CC = carry set on error
*    B  = error code
*
Write    lbsr  L00AC          Go adjust LSN for 512 byte sector if needed
         lda   #%1001001      Retry flags for I/O errors (see Disto SCII source)
L01C4    pshs  x,d            Preserve LSN, retries
         bsr   L01E8          Go write the sector
         puls  x,d            Restore LSN, retries
         bcs   L01D8          Error writing, go to write retry handler
         tst   <PD.VFY,y      No error, do we want physical verify?
         bne   L01D6          No, exit without error
         lbsr  verify         Go re-read & verify 32 out of 256 bytes
         bcs   L01D8          Error on verify, go to write retry handler
L01D6    clrb                 No error & return
L01D7    rts   

* Write error retry handler
L01D8    lsra                 Shift retry flags
         lbeq  L03AF          Too many retries, exit with error
         bcc   L01C4          Normal retry, attemp to re-write sector
         pshs  x,d            Preserve flags & sector #
         lbsr  sktrk0         Seek to track 0 (attempt to recalibrate)
         puls  x,d            Restore flags & sector #
         bra   L01C4          Try re-writing now

* 512 byte sector write here
L01E8    lbsr  L02AC          Go do double-step/write precomp if needed
         bcs   L01D7          Error, exit with it
         pshs  y,d            Preserve path dsc. ptr & LSN
         lda   <PD.TYP,y      Get device type
         anda  #%00000100     512 byte sector?
         beq   L020D          No, skip ahead
         lda   #$91           ??? appears to be useless
         lbsr  L0176          Go read the sector in
         ldd   >u00B6,u       Get OS9 LSN
         andb  #$01           Even or odd?
         beq   L020D          Even, skip ahead
         ldx   >sectbuf,u     Get physical sector buffer ptr
         leax  >$0100,x       Point to 2nd half
         bra   L0211          Copy caller's buffer to 2nd half of sector

L020D    ldx   >sectbuf,u     Get physical sector buffer ptr

L0211    ldy   PD.BUF,y       Get path dsc. buffer ptr
         IFNE  H6309
         ldw   #256           Copy write buffer to sector buffer
         tfm   y+,x+
         ELSE
         clrb
L0211Lp  lda   ,y+
         sta   ,x+
         decb
         bne   L0211Lp
         ENDC
         puls  y,d             Get path dsc. ptr & LSN back
         ldx   >sectbuf,u      Get physical sector buffer ptr again
         ldb   #$A0            Write sector command

* Format track comes here with B=$F0 (write track)
*L0224    pshs  y,cc           Preserve path dsc. ptr & CC
L0224     lbsr  L01A1          Send command to controller (including delay)
*** Commented out for blobstop fixes
*L0229    bita  >DPort+$08     Controller done yet?
*         bne   L0240          Yes, go write sector out
*         leay  -$01,y         No, bump wait counter
*         bne   L0229          Still more tries, continue
*         lda   >u00A9,u       Get current drive control register settings
*         ora   #%00001000     Drive motor on (but drive select off)
*         sta   >DPort+$00     Send to controller
*         puls  y,cc           Restore regs
*         lbra  L03AF          Check for errors from status register

         IFGT  Level-1
*** added blobstop
         lda   FBlock+1,u      get the block number for format
         beq   L0230           if not format, don't do anything
         sta   >$FFA1          otherwise map the block in
         ENDC

L0230    stb   >DPort+$00 send command to FDC
         bra   L0240           wait a bit for HALT to enable
* Write sector routine (Entry: B= drive/side select) (NMI will break out)
L0240    nop               --- wait a bit more
         lda   ,x+             Get byte from write buffer
         sta   >DPort+$0B      Save to FDC's data register
* EAT 2 CYCLES: TC9 ONLY (TRY 1 CYCLE AND SEE HOW IT WORKS)
         IFEQ TC9-1
         nop
         nop
         ENDC
*         stb   >DPort+$00    Set up to read next byte
         bra   L0240          Go read it

* NMI routine
NMISvc   leas  R$Size,s       Eat register stack
*         puls  y,cc           Get path dsc. ptr & CC
         IFGT  Level-1
         ldx   <D.SysDAT  get pointer to system DAT image
         lda   3,x        get block number 1
         sta   >$FFA1     map it back into memory
         andcc #^IntMasks turn IRQ's on again
         ELSE
*         puls  y,cc           Get path dsc. ptr & CC
         ENDC
         ldb   >DPort+$08     Get status register
         bitb  #%00000100     Did we lose data in the transfer?
*         lbne  L03E0          Yes, exit with Read Error
         lbeq  L03B2          Otherwise, check for drive errors
         comb             -- blobstop error code
         ldb   #E$DevBsy  -- device busy
         rts              -- and exit

verify   pshs  x,d
         ldx   PD.BUF,y       Get write buffer ptr
         pshs  x              Preserve it
         ldx   >sectbuf,u     Get sector buffer ptr
         stx   PD.BUF,y       Save as write buffer ptr
         ldx   4,s
         lbsr  L016F          Go read sector we just wrote
         puls  x              Get original write buffer ptr
         stx   PD.BUF,y       Restore path dsc. version
         bcs   L02A3          If error reading, exit with it
         pshs  u,y            Preserve device mem, path dsc. ptrs
         ldb   <PD.TYP,y      Get type from path dsc.
         ldy   >sectbuf,u     Get sector buffer ptr
         andb  #%00000100     512 byte sector?
         beq   L028D          No, skip ahead
         ldd   >u00B6,u       Get OS9's sector #
         andb  #$01           Odd/even sector?
         beq   L028D          Even; compare first half
         leay  >$0100,y       Odd, compare second half
L028D    tfr   x,u            Move PD.BUF ptr to U (since cmpx is faster)
         lda   #32            # of 'spotty' checks to do
L028F    ldx   ,u             Get 2 bytes from original write buffer
         cmpx  ,y             Same as corresponding bytes in re-read sector?
         bne   L029F          No, error & return
         leau  8,u            Skip next 6 bytes
         leay  8,y
         deca                 Done our 'spotty' check?
         bne   L028F          No, continue
         fcb   $8C            skip the next 2 bytes

L029F    orcc  #Carry
L02A1    puls  u,y
L02A3    puls  pc,x,d

L02A5    pshs  a              Save Caller's track #
         ldb   <V.TRAK,x      Get track # drive is currently on
         bra   L02E9          Go save it to controller & continue

L02AC    lbsr  L0376          Go set up controller for drive, spin motor up
         bsr   L032B          Get track/sector # (A=Trk, B=Sector)
         pshs  a              Save track #
         lda   >u00AD,u       Get side 1/2 flag
         beq   L02C4          Side 1, skip ahead
         lda   >u00A9,u       Get control register settings
         ora   #%01000000     Set side 2 (drive 3) select
         sta   >u00A9,u       Save it back
L02C4    lda   <PD.TYP,y      Get drive type settings
         bita  #%00000010     ??? (Base 0/1 for sector #?)
         bne   L02CC          Skip ahead
         incb                 Bump sector # up by 1
L02CC    stb   >DPort+$0A     Save into Sector register
         ldx   >u00A7,u       Get last drive table accessed
         ldb   <V.TRAK,x      Get current track # on device
         lda   <DD.FMT,x      Get drive format specs
         lsra                 Shift track & bit densities to match PD
         eora  <PD.DNS,y      Check for differences with path densities
         anda  #%00000010     Keep only 48/96 tpi differences
         pshs  a              Save differences
         lda   1,s            Get track # back
         tst   ,s+            Are tpi's different?
         beq   L02E9          No, continue normally
         lsla                 Yes, multiply track # by 2 ('double-step')
         lslb                 Multiply current track # by 2 ('double-step')
L02E9    stb   >DPort+$09     Save current track # onto controller

* From here to the line before L0307 is for write precomp, but is not used.
* Unless write precomp is needed, all of this is useless
* I think most (if not all) drives do NOT need precomp
         IFEQ  PRECOMP-1
         ldb   #21            Pre-comp track #
         pshs  b              Save it
         ldb   <PD.DNS,y      Get current density settings
         andb  #%00000010     Just want to check track density
         beq   L02F9          48 tpi, skip ahead
         lsl   ,s             Multiply pre-comp value by 2 ('double-step')
L02F9    cmpa  ,s+            Is track # high enough to warrant precomp?
         bls   L0307          No, continue normally
         ldb   >u00A9,u
         orb   #%00010000     Turn on Write precomp
         stb   >u00A9,u
         ENDC

L0307    ldb   >u00AA,u       ??? Get flag (same drive flag?)
         bne   L0314          No, skip ahead
         ldb   ,s             Get track #
         cmpb  <V.TRAK,x      Same as current track on this drive?
         beq   L0321          Yes, skip ahead
L0314    sta   >DPort+$0B     Save track # to data register
         ldb   <PD.STP,y      Get stepping rate
         andb  #%00000011     Just keep usable settings (6-30 ms)
         eorb  #%00011011     Set proper bits for controller
         lbsr  L03E4          Send command to controller & time delay
L0321    puls  a              Get track # back
         sta   <V.TRAK,x      Save as current track #
         sta   >DPort+$09     Save to controller
         clrb                 No error & return
         rts   

* Entry: B:X LSN
* Exit:  A=Track #
*        B=Sector #
*   <u00AD=00 = Head 1 , $FF = Head 2
L032B    tstb                 Sector # > 65535?
         bne   L033F          Yes, illegal for floppy
         tfr   x,d            Move sector # to D
         leax  ,x         LSN 0?
         beq   L0371          Yes, exit this routine
         ldx   >u00A7,u       Get previous drive table ptr
         cmpd  DD.TOT+1,x     Within range of drive spec?
         blo   L0343          Yes, go calculate track/sector #'s
L033F    comb                 Exit with Bad sector # error
         ldb   #E$Sect
         rts   

* Calculate track/sector #'s?
L0343    stb   >u00AE,u       Save LSB of LSN
         clr   ,-s            Clear track # on stack
         ldb   <DD.FMT,x      Get drive format
         lsrb                 Shift out # sides into carry
         ldb   >u00AE,u       Get LSB of LSN again
         bcc   L0367          Single sided drive, skip ahead
         bra   L035D          Double sided drive, skip ahead
* Double sided drive handling here
L0355    com   >u00AD,u       ???? Odd/even sector track flag?
         bne   L035D          Odd, so don't bump track # up
         inc   ,s             Bump up track #

L035D    subb  DD.TKS,x       Subtract # sectors/track
         sbca  #$00
         bcc   L0355          Still more sectors left, continue
         bra   L036D          Wrapped, skip ahead
* Single sided drive handling here
L0365    inc   ,s             Bump track # up

L0367    subb  DD.TKS,x       Subtract # sectors/track
         sbca  #$00
         bcc   L0365          Still more, go bump the track up
L036D    addb  $03,x          Bump sector # back up from negative value
         puls  a              Get the track #
L0371    rts                  A=track #, B=Sector #, <u00AD=Odd

* Drive control register bit mask table
L0372    fcb   $01            Drive 0
         fcb   $02            Drive 1
         fcb   $04            Drive 2
         fcb   $40            Drive 3 / Side select

L0376    clr   >u00AA,u       ???

chkdrv   lda   <PD.DRV,y      Get drive # requested
         cmpa  #4             Drive 0-3?
         blo   L0385          Yes, continue normally
NoHW     comb                 Illegal drive # error
         ldb   #E$Unit
         rts   

* Entry: A=drive #, X=LSN (Physical, not OS9 logical if PCDOS disk)
L0385    pshs  x,d            Save sector #, drive # & B???
         leax  >L0372,pc      Point to drive bit mask table
         ldb   a,x            Get bit mask for drive # we want
         stb   >u00A9,u       Save mask
         leax  DRVBEG,u       Point to beginning of drive tables
         ldb   #DRVMEM        Get size of each drive table
         mul                  Calculate offset to drive table we want
         leax  d,x            Point to it
         cmpx  >u00A7,u       Same as Last drive table accessed?
         beq   L03A6          Yes, skip ahead
         stx   >u00A7,u       Save new drive table ptr
         com   >u00AA,u       ??? Set flag
L03A6    clr   >u00AD,u       Set side (head) flag to side 1
         lbsr  L04B3          Go set up VIRQ to wait for drive motor
         puls  pc,x,d         Restore sector #,drive #,B & return

L03AF    ldb   >DPort+$08     Get status register from FDC
L03B2    bitb  #%11111000     Any of the error bits set?
         beq   L03CA          No, exit without error
         aslb             Drive not ready?
         bcs   L03CC          Yes, use that error code
         aslb             Write protect error?
         bcs   L03D0          Yes, use that error code
         aslb             Write fault error?
         bcs   L03D4          Yes, use that error code
         aslb             Sector not found?
         bcs   L03D8          Yes, use Seek error code
         aslb             CRC error?
         bcs   L03DC          Yes, use that error code
L03CA    clrb                 No error & return
         rts   

L03CC    ldb   #E$NotRdy      not ready
         fcb   $8C        skip 2 bytes

L03D0    ldb   #E$WP          write protect
         fcb   $8C        skip 2 bytes

L03D4    ldb   #E$Write       write error
         fcb   $8C

L03D8    ldb   #E$Seek        seek error
         fcb   $8C

L03DC    ldb   #E$CRC         CRC error
*         fcb   $8C

*L03E0    ldb   #E$Read        Read error
         orcc  #Carry         set carry
         rts   

L03E4    bsr   L0404          Send command to controller & waste some time
L03E6    ldb   >DPort+$08     Check FDC status register
         bitb  #$01           Is controller still busy?
         beq   L0403          No, exit
         ldd   >VIRQCnt,pc    Get initial count value for drive motor speed
         std   >u00B1,u       Save it
         bra   L03E6          Wait for controller to finish previous command

* Send command to FDC
L03F7    lda   #%00001000     Mask in Drive motor on bit
         ora   >u00A9,u       Merge in drive/side selects
         sta   >DPort+$00     Turn the drive motor on & select drive
         stb   >DPort+$08     Save command & return
L0403    rts   

L0404    bsr   L03F7          Go send command to controller

* This loop has been changed from nested LBSRs to timing loop.
* People with crystal upgrades should modify the loop counter
* to get a 58+ us delay time.  MINIMUM 58us.
L0406    pshs  a          14 cycles, plus 3*loop counter
         lda   #29        (only do about a 100 cycle delay for now)
L0409    deca             for total ~63 us delay (123 cycles max.)
         bne   L0409
         puls  a,pc       restore register and exit

* 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  ldx   PD.RGS,y       Get caller's register stack ptr
         ldb   R$B,x          Get function code
         cmpb  #SS.WTrk       Write track?
         beq   SSWTrk         Yes, go do it
         cmpb  #SS.Reset      Restore head to track 0?
         lbeq  sktrk0         Yes, go do it --- beq
         comb                 set carry for error
         ldb   #E$UnkSvc      return illegal service request error
         rts   

SSWTrk   pshs  u,y            preserve register stack & descriptor

         IFEQ   Level-1

         ldd   #$1A00         Size of buffer to hold entire track image
         os9   F$SRqMem       Request memory from system
         bcs   L0489          Error requesting, exit with it
         stu   >FBlock,x

         ELSE

*--- new code
         ldb   #1         1 block to allocate
         os9   F$AllRAM   allocate some RAM
         bcs   L0489      error out if at all
         leax  >FBlock,u   point to 'my' DAT image
         std   ,x         save a copy of the block
         os9   F$ResTsk   reserve a task number for the copy
         bcs   FError     error out
         stb   2,x        save temporary task number in FTask,u
         lslb             2 bytes per entry
         ldu   <D.TskIPt  get task image table pointer
         stx   b,u        save pointer to the task's DAT image
         lsrb             get the right number again
         IFNE  H6309
         tfr   0,u        destination is address 0
         ELSE
         ldu   #$0000
         ENDC
*--- end new code

         ldx   2,s            get pointer to descriptor
*         stu   >FBlock,x
         ldx   <D.Proc        Get current process ptr
         lda   P$Task,x       Get task # for current process
*         ldb   <D.SysTsk      Get system task #
         ldy   ,s
         ldx   PD.RGS,y       Get register stack ptr
         ldx   R$X,x          Get ptr to caller's track buffer
         ldy   #$1A00         Size of track buffer
         os9   F$Move         Copy from caller to temporary task
         bcs   L0479          Error copying, exit

         ENDC

         puls  u,y
         pshs  u,y
         lbsr  L0376          Go check drive #/wait for it to spin up
         ldx   PD.RGS,y       Get caller's register stack ptr
         ldb   R$Y+1,x        Get caller's side/density
         bitb  #$01           Check side
         beq   L0465          Side 1, skip ahead
         com   >u00AD,u
         ldb   >u00A9,u       Get current control register settings
         orb   #%01000000     Mask in side 2
         stb   >u00A9,u       Save updated control register
L0465    lda   R$U+1,x        Get caller's track #
         ldx   >u00A7,u       Get current drive table ptr
         lbsr  L02A5          
         bcs   L0489
         ldb   #$F0           Write track command?
*---
         IFEQ  Level-1
         ldx   >FBlock,u
         ELSE
         ldx   #$2000     start writing from block 1
         ENDC

         lbsr  L0224          Go write the track
L0479    ldu   2,s
         pshs  b,cc           Preserve error

         IFEQ  Level-1

         ldu   >FBlock,u       Get ptr to track buffer
         ldd   #$1A00         Return track buffer
         os9   F$SRtMem 

         ELSE

         ldb   >FTask,u   point to task
         os9   F$RelTsk   release the task
         fcb   $8C        skip 2 bytes

         ENDC

* format comes here when block allocation passes, but task allocation
* gives error.  So er de-allocate the block.
FError   
         IFGT  Level-1
         pshs  b,cc       save error code, cc
         ldx   >FBlock,u   point to block
         ldb   #1         1 block to return
         os9   F$DelRAM   de-allocate image RAM blocks
         clr   FBlock+1,u ensure that the block # in FBlock is zero.
         puls  b,cc           Restore error
         ENDC

L0489    puls  pc,u,y         Restore regs & return

* seek the head to track 0
sktrk0   lbsr  chkdrv
         ldx   >u00A7,u
         clr   <$15,x
         lda   #$05
L0497    ldb   <PD.STP,y
         andb  #%00000011     Just keep usable settings (6-30 ms)
         eorb  #%01001011     Set proper bits for controller
         pshs  a
         lbsr  L03E4
         puls  a
         deca  
         bne   L0497
         ldb   <PD.STP,y
         andb  #%00000011     Just keep usable settings (6-30 ms)
         eorb  #%00001011     Set proper bits for controller
         lbra  L03E4

L04B3    pshs  y,x,d          Preserve regs
         ldd   >VIRQCnt,pc    Get VIRQ initial count value
         std   >u00B1,u       Save it
         lda   >u00A9,u       ?Get drive?
         ora   #%00001000     Turn drive motor on for that drive
         sta   >DPort+$00     Send drive motor on command to FDC
         lda   <D.MotOn       Get VIRQ flag
         bmi   L04DE          Not installed yet, try installing it
         bne   L04E0          Drive already up to speed, exit without error

* Drive motor speed timing loop (could be F$Sleep call now) (was over .5 sec)
         ldx   #32            wait for 32 ticks
         os9   F$Sleep

L04DE    bsr   InsVIRQ        Install VIRQ to wait for drive motors
L04E0    clrb                 No error & return
         puls  pc,y,x,d

InsVIRQ  lda   #$01           Flag drive motor is up to speed
         sta   <D.MotOn
         ldx   #$0001         Install VIRQ entry
         leay  >u00B1,u       Point to packet
         clr   Vi.Stat,y      Reset Status byte
         ldd   >VIRQCnt,pc    Get initial VIRQ count value
         os9   F$VIRQ         Install VIRQ
         bcc   VIRQOut        No error, exit
         lda   #$80           Flag that VIRQ wasn't installed
         sta   <D.MotOn
VIRQOut  clra  
         rts   

* IRQ service routine for VIRQ (drive motor time)
* Entry: U=Ptr to VIRQ memory area
IRQSvc   pshs  a
         lda   <D.DMAReq
         beq   L0509
         bsr   InsVIRQ
         bra   IRQOut
L0509    sta   >DPort+$00
         IFNE  H6309
         aim   #$FE,>u00B5,u
         ELSE
         lda   u00B5,u
         anda  #$FE
         sta   u00B5,u
         ENDC
*         fdb   u00B5      --- so changes in data size won't affect anything
         clr   <D.MotOn
IRQOut   puls  pc,a

* Non-OS9 format goes here
* Entry: X=LSN
*        Y=Path dsc. ptr
*        U=Device mem ptr
L051A    pshs  x              Preserve Logical sector #
         ldx   >u00A7,u       Get last drive table accessed ptr
         clra
         pshs  x,a            Save ptr & NUL byte
         IFNE  H6309
         ldw   #$14           Clear 20 bytes
         tfm   s,x+
         ELSE
         ldb   #$14
L051ALp  clr   ,x+
         decb
         bne   L051ALp
         ENDC
         puls  x,a            Eat NUL & get back drive table ptr
         ldb   <PD.CYL+1,y    Get # cylinders on drive (ignores high byte)
         lda   <PD.SID,y      Get # sides
         mul                  Calculate # tracks on drive (1 per head)
         IFNE  H6309
         decd                 Adjust to ignore track 0
         ELSE
         subd  #$0001
         ENDC
         lda   <PD.SCT+1,y    Get # sectors/track
         sta   DD.TKS,x       Save in drive table
         sta   <DD.SPT+1,x    Save in other copy in drive table
         mul                  Calculate # sectors on drive (minus track 0)
         pshs  x              Preserve drive table ptr
         tfr   d,x            Move # sectors on drive to X
         lda   <PD.T0S+1,y    Get # sectors on track 0
         leax  a,x            Add that many sectors to total
         lda   <PD.TYP,y      Get device type settings
         anda  #%00000100     Mask out all but 512 byte sector flag
         beq   L0550          Not 512 byte sector, skip ahead
         IFNE  H6309
         addr  x,x        Multiply by 2 (convert to 256 byte OS9 sectors)
         ELSE
         tfr   x,d
         leax  d,x
         ENDC
L0550    tfr   x,d            Move # sectors to D
         puls  x              Get back drive table ptr
         std   DD.TOT+1,x     Save # sectors allowed on drive
         lda   #UPDAT.+EXEC.  Owner's read/write/exec attributes
         sta   DD.ATT,x       Set attributes for disk
         lda   <PD.DNS,y      Get density settings
         lsla                 Shift for DD.FMT
         pshs  a              Preserve it a sec
         lda   <PD.SID,y      Get # sides
         deca                 Adjust to base 0
         ora   ,s+            Merge with density settings
         sta   <DD.FMT,x      Save in device table
         clrb                 No error?
         puls  pc,x           Restore original LSN & return

         emod
eom      equ   *
         end