view 3rdparty/drivers/burke/xtos9.src @ 1652:558cab468052

RG fixed a bug in the Vavasour emulator clock2 module. clock2 is now of type Sbrtn instead of Systm. clock.asm has been modified to link to this type.
author boisy
date Sat, 17 Jul 2004 12:20:31 +0000
parents c10820aa211b
children 892188818514
line wrap: on
line source

*******************************************
***                                     ***
***     COPYRIGHT 1990 BURKE & BURKE    ***
***     ALL RIGHTS RESERVED             ***
***                                     ***
***     COPYRIGHT 1992 BURKE & BURKE    ***
***     ALL RIGHTS RESERVED             ***
***                                     ***
*******************************************

*
*  CoCo XT Hard Disk Driver  Version 2.0
*
*  For Western Digital WD1002-WX1 (or 27X) Controller.
*
*  The controller can only handle 512 bytes per sector,
*  so these routines must synthesize 2 logical 256 byte
*  sectors from each physical 512 byte sector.  This
*  increases the time it takes to read or write a sector.
*
*  To counteract this, the 2nd half of each "even" sector
*  is retained during a read.  If the next read accesses the
*  "odd" half, the sector contents is read from the "verify"
*  buffer instead of the hard disk.
*
*  This version can be optimized for a single ST-225 hard disk,
*  via conditional assembly.  Several other parameters can also
*  be controlled by conditional assembly.
*
*  Conditional assembly control:
*
*   Drives              ;Number of drives supported (1-2)
*
*   irqflg              ;non-zero to mask IRQ during disk access.
*   fmtflg              ;non-zero if hard formatting supported
*   fstflg              ;non-zero if fast transfers supported
*   cchflg              ;non-zero if read cache supported
*   vrfflg              ;non-zero if write verification supported
*   tboflg              ;non-zero if jump to 2 MHz for block moves
*   errflg              ;non-zero for good error messages
*   icdflg              ;non-zero to ignore C/D status bit
*   trsflg              ;non-zero if optimized for 32 SPT, 4 hd disks
*   sysram              ;non-zero to use system RAM for verf buffer
*   sizflg              ;non-zero if drives may be different sizes
*
*   XLEVEL              ;Special level 2 flag -- to use L1 assembler
*
*  Chris Burke  Schaumburg, IL  10/14/87
*
*   Modification History
*   --------------------
*
*   Date        Who     Description
*   --------    ---     -------------------------------------
*   11/20/87    CJB     Fixed bug in PARK.
*   12/23/87    CJB     Corrected table size and location
*                       equates for L2 compatibility.  
*                       Deleted support for Level 1, Version 
*                       1.X OS9.
*   12/27/87    CJB     Added code to grab parameters from
*                       device descriptor if not obtained
*                       by INIT.
*   01/28/88    CJB     Added support for any slot.
*   02/07/88    CJB     Changed FORMAT to reset drive parameters
*                       Added speedy read cache algorithm.
*                       Eliminated DIVA, DIVY, etc.
*   02/23/88    CJB     Recoded for TRSFLG
*   03/03/88    CJB     Changed module names to BBhdisk etc.
*   04/17/88    cjb     Modified INIT to call WaitIdl before
*                        shutting off controller interrupts & DMA
*                       Fixed bugs in INIT which confused path
*                        descriptor and device descriptor pointers
*   04/21/88    cjb     Added code to disable I/O retries after
*                        formatting hard drive.
*   06/12/88    cjb     Changed to call WaitId2 instead of WaitIdl
*   07/01/88    cjb     Changed revision from 2 to 3
*   10/10/88    cjb     Modified for format error recovery
*   05/18/90    cjb     Modified copyright
*                       Added support for up to 4 drives
*   07/07/91    cjb     Rev 2.5 (allow disabling of recalibration)
*   04/15/92    cjb     Rev 2.6 (speed-ups, bug fix for DBLRDD)
*

 page
*
*  Equates
*

 ifp1
        use     defsfile
        use     wx2.equ
 endc

*  Index into device descriptor (using PD.XXX)

DD.BASE equ M$DTYP-PD.OPT
DD.HDSK equ $25              ;Start of extra hard disk stuff

*  Multi-PAK Addresses
MPAK equ $FF7F

*
*   Bogus equates for Level 2
*

D.DMARq2    equ     $008A       ;Bogus Level 2 D.DMAReq
D.PROC2     equ     $0050       ;Bogus Level 2 D.PROC
DRVMEM2     equ     $26         ;Level 2 drive table size
DRVBEG2     equ     $0F         ;Level 2 start of drive tables

*
*   Bogus equates for Level 1
*

D.DMARq1    equ     $006A       ;Bogus Level 1 D.DMAReq
D.PROC1     equ     $004B       ;Bogus Level 1 D.PROC
DRVMEM1     equ     $18         ;Level 1 drive table size
DRVBEG1     equ     $07         ;Level 1 start of drive tables

    ifne    XLEVEL-1
DMASEM  set     D.DMARq2        ;Level 2 DMA stuff (multi-pak)
DPROC   set     D.PROC2         ;Level 2 process control stuff
XDrvBeg set     DRVBEG2
XDrvMen set     DRVMEM2

    else
DMASEM  set     D.DMARq1        ;Level 1 DMA stuff (multi-pak)
DPROC   set     D.PROC1         ;Level 1 process control stuff
XDrvBeg set     DRVBEG1
XDrvMen set     DRVMEM1
    endc

 page
*
*   Module Header
*
verson  equ     2
        mod     cchend,cchnam,drivr+objct,reent+verson,JmpTbl,endmem
        fcb     DIR.+SHARE.+PEXEC.+PREAD.+PWRIT.+EXEC.+READ.+WRITE.

*  Dynamic module name generation

cchnam  equ     *

    ifeq  testing
        fcc     "BB"        ; (normal name)
    else
        fcc     "BK"        ; (special name)
    endc

    ifeq    (XLEVEL-1)      ;If level 1,
        ifne    irqflg      ;If Level 1, Version 1 OS9
            fcc /X/
        else                ;If Level 1, Version 2 OS9
            fcc /1/
        endc
    endc

    ifne    fmtflg          ;If this driver can format hard disk,
        fcc     /F/
    endc

    ifne    tboflg          ;If 2MHz transfers supported,
        fcc     /T/
    endc

    ifne    trsflg          ;If "terse" version
        fcs     /hd/        ;NOTE -- set MSB of last byte
    else                    ;If "normal" version
        fcs     /hdisk/     ;NOTE -- set MSB of last byte
    endc

        fcb     $06         ;revision - 2.6

*   Copyright notice

        fcc     /COPR. 1992 BURKE & BURKE/

 page
*
*  Static Storage
*

    use     hdvars.src

 page
*
*  Jump table
*
JmpTbl  lbra    INIT
        lbra    READ
        lbra    WRITE
        lbra    GETSTA
        lbra    SETSTA
        lbra    TERM

    ifne    sizflg              ;If drives may be different sizes,
    page
*
*   Post-initialization for drives.
*
*   If we ever call a driver routine (other than INIT)
*   with PARK LSN = 0, we know this drive has not been
*   initialized.
*
*   Enter w/ Reg-Y = path descriptor pointer,
*            Reg-U = static storage
*
*   Must save A and B:X as well as Y, U.
*
*   Returns carry set if error initializing drive
*
PostIni pshs    Y,X,D

        ldb     PD.DRV,Y
        lbsr    GOBPtr              ;Point Y to extra variables
        clrb                        ;Force no carry (success)
        ldb     (PRKLSN-OBSTART+0),Y
        orb     (PRKLSN-OBSTART+1),Y
         bne    PSTI0               ; (branch if OK)

*   Perform drive info and controller intialization

        ldy     4,S                 ;Get path pointer
        ldy     PD.DEV,Y            ; get device table pointer
        ldy     V$DESC,Y            ;  get device descriptor pointer
        lbsr    CtlInit             ;Call routine to init. controller

*   Done initiailizing this drive.

PSTI0   puls  D,X,Y,PC

    endc

 page
*
*   Initialize driver routines
*
*   Reg-Y points to device descriptor
*
INIT    pshs    y

*   This section calls routines that expect a *PATH* descriptor
*   pointer in Reg-Y

        leay    DD.BASE,Y   ;Make Y look like path descriptor to get slot

        lbsr    Semfor      ;Wait, then select HD controller.
        sta     >HDRSET     ;Reset controller
        lbsr    WaitId2     ;Sleep -- wait for controller to reset
*** Extra call to give WD1004 time to reset
        lbsr    WaitId2     ;Sleep -- wait for controller to reset
*** ENDK
        ldy     ,S          ;Restore y -> device descriptor

*   Set up controller for non-interrupt, non-DMA operation

        clr     >HDMASK     ;Turn off controller interrupts and DMA

*   Set # of drives we can handle

        lda     #Drives
        sta     V.NDRV,U

*   Set up drive switching info

    ifne    (Drives-1)
        ldd     #$FFFF
        std     prvdrv,u    ; (set PRVDRV unknown, DRVSEM for new drive)
        std     actdrv,u    ;Pointer to active drive table
    else
        lda     #$FF
        sta     DRVSEM,U
    endc

*   Enable hardware I/O retries.  These will be disabled by a FORMAT
*   setstat, and will be re-enabled by an INIT call or a reboot.
*   The main use of this is to disable retries during disk formatting.

        clr     RetryEn,U   ;Enable hardware retries

*   Program controller w/ drive parameters.  
*   Y points to device descriptor.

        bsr     CtlInit     ; (also clears BFRFLG,U and changes Y)
         bcs    INIT9       ; (abort if error)

*  Initialize the drive tables for all drives

        ldd     #(-1)               ;Current track is unknown
        stb     XDrvBeg+V.TRAK,U
        std     XDrvBeg+DD.TOT,U    ;Set total sectors non-zero to allow LSN0
    ifne    (Drives-1)
        stb     XDrvBeg+XDrvMen*1+V.TRAK,U    ; (do all drives)
        std     XDrvBeg+XDrvMen*1+DD.TOT,U
      ifge  (Drives-3)
        stb     XDrvBeg+XDrvMen*2+V.TRAK,U    ; (do all drives)
        std     XDrvBeg+XDrvMen*2+DD.TOT,U
      endc
      ifge  (Drives-4)
        stb     XDrvBeg+XDrvMen*3+V.TRAK,U    ; (do all drives)
        std     XDrvBeg+XDrvMen*3+DD.TOT,U
      endc
    endc

*   Reg-Y points to bogus path descriptor (courtesy of CtlInit).
*   Wait for drive to be ready.  
*
*   Note that WaiDrv returns Reg-B=0 and carry clear to OK, 
*   else reg-B = E$NotRdy and carry set.

        bsr     WaiDrv          ;Note:  WaiDrv requires *PATH* descriptor

*  Error hook
INIT9   puls    Y               ;restore saved Y register (DD pointer)
        bra     HDXIT

*  Generic error-free exit
OKXIT   clrb 

*
*  Generic exit.  Restore MULTI-PAK to
*  slot #4 so floppy disk works, and release semaphore
*
*   Preserves CC and B
*
*   RELEASE also clears the V.WAKE flag.
*
HDXIT   lbsr    Release
        rts 

*
*   Wait for drive ready
*   Assumes reg-Y points to real or bogus path
*   descriptor (for drive # and step rate)
*
WaiDrv  pshs    y,x,a,b

        clr     ,-s                 ;Time out

WaiDr2  lda     #WX$TST     ;Test drive ready

    ifne    (Drives-1)              ;If more than 1 drive
        ldb     PD.DRV,Y            ;get drive #
    endc

        lbsr    DrvCmd
         bcc    WaiDr3              ; (branch if drive ready)

        dec     ,s                  ;Decrement timeout
         beq    WaiDr4              ; (branch if timeout)

        lbsr    Snooze              ;Give drive some time
        bra     WaiDr2              ; then retry.

*   Timeout

WaiDr4  comb
        ldb     #E$NotRdy           ;Drive not ready
        stb     1+1,S

*   Carry clear if ready, else carry set

WaiDr3  leas    1,S                 ;Discard counter
        puls    a,b,x,y,pc

*
*   Get extra bytes from this drive's descriptor.
*   Send them to the controller.  
*
*   Enter w/ 
*   Reg-Y = Dev Desc pointer, and 
*   Reg-U = static storage pointer.
*
*   Note that if there is a 2nd drive with different
*   parameters, they must be obtained in a different
*   manner.
*
*   Modifies Reg-Y to make it look like a bogus *PATH*
*   descriptor
*

CtlInit equ     *

    ifne    cchflg              ;If read cache,
        clr     BFRFLG,U            ;Buffer contents invalid
    endc

        bsr     GetXtra             ;Call subroutine DDPTR in Y.

*   Send drive parameters to controller.  Reg-Y points to device
*   descriptor, but we adjust it to look like a path descriptor.

        leay    DD.BASE,Y           ;Make Y look like path descriptor

        lda     #WX$INI             ;Command to send drive parameters

    ifne    (Drives-1)              ;If more than 1 drive
        ldb     PD.DRV,Y            ;get drive #
    endc

        lbsr    DrvCmd              ;Set up drive parameters
        rts

    page
*
*   Copy precomp cylinder and park LSN from descriptor 
*   (also gives max. tracks!)
*

GetXtra ldd     DD.HDSK+(PCCYL-OBSTART),Y       ;get prec, MSB of park LSN
        ldx     DD.HDSK+(PRKLSN+1-OBSTART),Y    ;get rest of park LSN
     ifne   (Drives-1)          ; If 2 drives,
      ifne  sizflg              ;  of different sizes,
        pshs    Y,X,D
        ldb     DD.BASE+PD.DRV,Y    ;get drive #
        lbsr    GOBPtr              ;get dest. pointer to Y
        puls    D,X
        std     (PCCYL-OBSTART),Y
        stx     (PRKLSN+1-OBSTART),Y
        puls    Y,PC                ;Recover DD ptr and exit
      else                      ;  of same size,
        std     PCCYL,U
        stx     (PRKLSN+1),U        ;set up both tables at once
        std     (PCCYL+OBSIZE),U
        stx     (PRKLSN+1+OBSIZE),U
        rts
      endc
     else                       ; If 1 drive,
        std     PCCYL,U
        stx     (PRKLSN+1),U
        rts
     endc

    page
*
*   Terminate hard disk processing
*
TERM    lbsr    Semfor      ;Wait on semaphore, select controller
        bra     OKXIT       ;Clear semaphore and successful exit

*
*  Dummy routine for GETSTA
*
GETSTA  comb                ;Set carry
        ldb     #E$UnkSVC
        rts 

 page
*
*   Read a sector from disk
*
*   LSN in B:X.  If it is 1+OLDLSN,
*   and the drive # is right, and the
*   read cache is full, don't even bother
*   doing address computations
*
*   There are always an even number of sectors
*   per track, so if the new LSN is the old LSN
*   with MSB set, we have a winner!  We always
*   store the old LSN with MSB set.
*

READ    equ     *

    ifne    cchflg      ;If read cache,

        pshs    X,B         ;Save LSN
        lbsr    SavSlt      ;Save old slot #, but don't change yet

        tst     BFRFLG,U    ;Is buffer valid?
         beq    READXX      ; (branch if no luck)
     ifne   (Drives-1)
        lda     PD.DRV,Y
        cmpa    PRVDRV,U
         bne    READXX      ; (branch if drive different)
     endc
        puls    B,X
        cmpx    OLDLSN+1,U
         bne    READXY
        cmpb    OLDLSN+0,U
         bne    READXY

*   Use verify buffer for sector data -- no read necessary!
*   The OLDLSN and PRVDRV variables are already set right.

        pshs    u,y,x

        ldb     #128
        ldx     PD.BUF,Y        ;Get destination pointer
        leay    vrfbuf,U        ;Get source pointer

DR0     ldu     ,y++
        stu     ,x++
        decb
         bne    DR0

        puls    x,y,u
        bra     OKXIT3          ;Successful exit

*   Not a cache read.  Restore LSN

READXX  puls    B,X

*   Save LSN in case we can do a cache read next time.
*   We always set the LSB of the saved LSN; the BFRFLG
*   will be set only if the actual LSB is 0!

READXY  clr     BFRFLG,U        ;Assume future cache read invalid
        stb     OLDLSN+0,U      ;Save LSN for next time

        pshs    B
        tfr     X,D
        bitb    #%00000001      ;If LSB is 0, cache read valid next time
         bne    READXZ

        com     BFRFLG,U        ; (cache read next time)

READXZ  orb     #%00000001      ; (always set LSB in possible match)
        std     OLDLSN+1,U
        puls    B

    endc

*   Perform normal read -- LSN in B:X

        lbsr    Semfor      ;Wait, select hard disk controller slot

    ifne    sizflg      ;If drives can be different sizes,
        lbsr    PostIni
         bcs    HDXIT3
    endc

*   Check for LSN0

        cmpx    #0          ;Check for LSN 0 -- SPECIAL
         bne    NotLS0
        tstb 
         bne    NotLS0

**   Wants to read LSN 0.
**   See if LSN 0 has already been read once.
**
**   We must use PD.DTB,Y instead of ACTDRV,U because
**   SELDRV has not been called yet.
**
**   Actually, we still read -- we just don't update
**   the drive table
*
*        pshs    X,B         ;Save LSN
*    ifne    (Drives-1)          ;If 2 drives,
*        ldx     PD.DTB,Y    ;Point to drive table
*    else
*        leax    DRVTBL,U    ;Point to drive table
*    endc
*        ldd     DD.TOT,x    ;Get total sectors
*        cmpd    #-1         ; -1 is a special value set by INIT routine
*        puls    B,X         ;Restore LSN (always $000000)
*         bne    NotLS0      ;If not -1, LSN 0 already read; no special stuff

*   Special treatment for LSN0 -- refresh drive table when done

        bsr     DoRead
         bcs    HDXIT3

        ldx     PD.BUF,y    ;Get buffer address
        pshs    Y,X         ;Save old buffer and device descriptor ptrs

*   Note that the SELDRV routine, called by DOREAD, sets up
*   the drive table pointer in actdrv,U if there are 2 drives.

    ifne    (Drives-1)          ;If 2 drives,
        ldy     actdrv,u
    else
        leay    DRVTBL,U
    endc

        ldb     #DD.SIZ-1

*  Update drive table from buffer

RCPY1   lda     b,x
        sta     b,y
        decb 
         bpl    RCPY1

        puls    y,x
        bra     OKXIT3

*  Read (not LSN0)

NotLS0  bsr     DoRead
HDXIT3   lbcs   HDXIT

OKXIT3  lbra    OKXIT

 page
*
*  Utility to read a sector
*
DoRead  lbsr    SETUP
         bcs    DORXIT

*  Entry point for VERIFY
VrfRdd  ldx     PD.BUF,Y    ;Get buffer address

*  General read sector
GetSec  lda     #WX$RDD        ;Create "READ SINGLE SECTOR" command
        lbsr    CMEXEC
DORXIT  rts 

 page
*
*  Write a sector to disk.
*
*  This routine must pre-read the sector in order to
*  pack 2 logical sectors into 1 physical sector.
*  This invalidates any data that might have been pre-read
*  into the verify buffer.
*
WRITE   lbsr    Semfor      ;Wait, then select HD controller

    ifne    sizflg      ;If drives can be different sizes,
        lbsr    PostIni
         bcs    HDXIT3
    endc

*  Pre-read the "other" half of the sector to the verify buffer

        pshs    X,B        ;Save LSN
        lbsr    SETUP      ;Set up head, track, sector #'s
        bsr     flphlf      ;flip LSB of SECNUM

    ifne    cchflg      ;If read cache supported
        clr     BFRFLG,u    ;2nd half of sector should not be saved
    endc

        leax    vrfbuf,U    ;Use verify buffer
        bsr     GetSec      ; (re-use code)

        puls    X,B
         bcs    HDXIT3      ; branch to abort if pre-read fails

        bsr     flphlf       ;fix up sector #

*  Perform the write.  The "other" half of the physical sector
*  has been pre-read into the verify buffer.

        pshs    x,b       ;save LSN
        bsr     DoWrit
        puls    x,b
         bcs    HDXIT3

    ifne    vrfflg              ;If verify enabled,

        tst     PD.VFY,Y   ;Verify writes?
         bne    OKXIT3

*  Verify the write
        bsr     VERIFY
         bcc    OKXIT3

*  Verify failed; carry set
        ldb     #E$Write
        bra     HDXIT3

    else

        bra    OKXIT3

    endc

*
*  Flip LSB of SECNUM.  This is used to pre-read the half of the sector
*  that should not be changed by a write.
*
flphlf  lda     secnum,u
        EORA    #1
        sta     secnum,u
        rts

 page
*
*  Utility to write a sector
*
DoWRIT  lbsr    SETUP
         bcs    DOWXIT

        ldx     PD.BUF,Y        ;Get buffer pointer
        lda     #WX$WRT         ;Create "WRITE SINGLE SECTOR" command
        lbsr    CMEXEC

DOWXIT  rts 

    ifne    vrfflg          ;If verify supported,

*
*  Verify last sector written
*
*  Don't copy unused part of sector to verify buffer
*
VERIFY  pshs    x,b,a

        ldx     PD.BUF,y    ;Save buffer pointer
        pshs    x

        leax    vrfbuf,U
        stx     PD.BUF,y    ;Force dummy buffer pointer
        ldx     4,s         ;Recover LSN

        lbsr    setup
         bcs    VF1

    ifne    cchflg          ;If read cache supported,
        clr     BFRFLG,u    ;Mark don't save 2nd half of sector
    endc

        lbsr    VrfRdd     ;Actual read done here

VF1     puls    x
        stx     PD.BUF,y    ;Restore buffer pointer
         bcs    VRFXIT

*  Look only at every 8th byte to speed up the verify
        lda     #(256/8)    ;Iteration count
        pshs    u,y,a

        leay    vrfbuf,U    ;Point at sector buffer
        tfr     x,u

*  Compare the data read back to the data written
VRF1    ldx     ,u
        cmpx    ,y
         bne    VRFERR

        leau    8,u         ;next byte
        leay    8,y
        dec     ,s
         bne    VRF1

        bra     VRFOK

*  Error exit
VRFERR  orcc    #$01

*  Deallocate temporaries
VRFOK   puls    u,y,a

*  Generic verify exit
VRFXIT  puls    a,b,x,PC

    endc

 page
*
*   Set Status.
*
*   Reg-Y has path descriptor, and Reg-A has status code.
*
SETSTA  lbsr    Semfor      ;Wait, then select HD controller

    ifne    sizflg      ;If drives can be different sizes,
        lbsr    PostIni
         lbcs   HDXIT
    endc

        lbsr    SELDRV

        ldx     PD.RGS,y    ;Point at register stack
        ldb     R$B,x       ;Recover service code

        cmpb    #SS.Reset
         bne    SS1

        lbsr    SEEKT0
        bra     SSXIT

SS1     equ     *

    ifne    fmtflg              ;If hard formatting supported,
        cmpb    #SS.WTrk
         bne    SS2

        bsr     FmtTrk
        bra     SSXIT
    endc

SS2     cmpb    #SS.SQD
         bne    SS9

        bsr     PwrDwn
        bra     SSXIT

*  Error - unrecognized SETSTA code

SS9     comb 
        ldb     #E$UnkSvc

*  Generic exit.  Return error if carry set.

SSXIT   lbcs    HDXIT
        lbra    OKXIT

 page
*
*  Power-down (park) disk
*
PwrDwn  equ     *

*   Get park LSN to B:X

    ifne    (Drives-1)      ;If 2 drives,
        lbsr    GtPkLSN         ;Call routine to get park LSN to B:X
    else
        ldb     PRKLSN+0,U
        ldx     PRKLSN+1,U
    endc

*   Seek to this LSN

PwrDn2  lbsr    SETUP
        lbsr    SEEK            ;Park head at last track
        rts 

    ifne    fmtflg          ;If hard format supported

*
*   Format a track.
*
*   Now we format one track at a time.
*
*   At entry Reg-X points to the register packet,
*   and Reg-Y points to the path descriptor
*
FmtTrk  lda     #$80            ;Disable hardware retries
        sta     RetryEn,U

        ldd     R$U,x           ;Get track number -- must be zero.
        cmpd    #0
         bne    FMTXIT

        ldd     R$Y,x           ;Check side/density -- MSB must be zero
        cmpa    #0
         beq    FMT1

*  Ignore repeated calls, returning success
FMTXIT  clrb 
        rts 

*  Perform the format if not write protected
FMT1    pshs    u,y,x

*  Proceed, disk not write protected
FMT11   equ     *

*   Set up reduced drive characteristics (only legal tracks)

        lda     #WX$XIN     ;Bogus command to init. special parameters
        lbsr    CmExec
         bcs    FMTERR

*  Initialize sector buffer

        lda     #WX$WBF
        lbsr    CmExec
         bcs    FMTERR

*   Begin formatting at track 0

        ldd     #$C0FF          ;Set initial cylinder to (-1)
        std     ERRHCYL,u

*  Set up starting track for format

FMT2    ldd     ERRHCYL,u       ;Use last good cylinder
        anda    #%11000000
        incb
         bne    FMT3
        adda    #%01000000
FMT3    std     hicyl,U         ; (set track number, sector 0)

*        clr     lowcyl,u        ;Clear LOWCYL
*        clr     hicyl,u         ;Clear HICYL (and sector #)

    ifne    (Drives-1)          ;If multiple drives,
        lda     PD.DRV,Y        ;Set up drive #
        ldb     #32
        mul
        stb     sdhreg,u        ;Select correct drive, clear head number
    else
        clr     sdhreg,u
    endc

        lda     PD.ILV,Y        ;Set interleave factor
        lsra                    ; / 2 since physical sectors are 2X
        sta     seccnt,u

        lda     #WX$FMT         ;Command to FORMAT ENTIRE DRIVE
*        lda     #WX$FTK         ;Command to FORMAT TRACK
        lbsr    CmExec
         bcs    FMT2            ;Try next track if error

*   Return error code in B, C set or no error, C clear

FMTERR  pshs    b,cc            ;Stack error status

*   Set up correct drive characteristics (park track allowed)

        lda     #WX$INI     ;Init. original parameters
        lbsr    CmExec

*   Get back error status

        puls    cc,b

*  End of format routine

FMTEND  puls    x,y,u,PC

    endc

*
*   End of main line of XTOS9.SRC.
*   "USE" files follow.
*

 page
*
*   Include routines to issue command to controller
*   and return completion status.
*

        use     hdcmd.src

 page
*
*   Include routines to translate LSN & drive #
*   to a controller task file.
*

        use     hdmath.src

 page
*
*   Include utility routines.
*

        use     hdutil3.src

 emod
cchend equ *

 end