'===========================================================================
' Subject: DISK FORMATTING PROGRAM FOR PB     Date: 12-28-99 (18:05)       
'  Author: Dieter Folger                      Code: PB                     
'  Origin: folger@bnv-bamberg.de            Packet: DISK.ABC
'===========================================================================
'----------------------------------------------------------------------------
' PBFORMAT.BAS is a complete disk formatting program based upon
' DISK13IO.PAS written by Kurt Diesch using Turbo Pascal.
' PBFORMAT allows to format 360k / 1.2M / 720k / 1.44M disks in
' drives A or B.
' Changes to DISK13IO.PAS: A special boot sector is written that
' boots from hard disk when no system is found on floppy.
' PBFORMAT is freeware (c) 1995 by D. Folger
' Use at your own risk.
'----------------------------------------------------------------------------
$COMPILE EXE
$ERROR ALL OFF
$OPTIMIZE SPEED
DEFINT A - Z
%False = 0 : %True = NOT %False

'==== main program ========================================================
 IF DosVersion! < 3.3 THEN
    PRINT "Need MS-DOS 3.3 or newer" : END
 END IF
 CY=CSRLIN:Cx=POS(0)
 SaveScreen Temp$
 Cm$ = UCASE$(COMMAND$)
 IF Cm$ = "" THEN
    PRINT : Print "  -þ- P B F O R M A T -þ-"
    PRINT
    PRINT " Drive A or B (or ESC to quit): ";
    DO
       DO : K$ = UCASE$(INKEY$) : LOOP UNTIL LEN(K$)
       IF K$ = CHR$(27) THEN GOTO Quit
    LOOP UNTIL INSTR("AB", K$)
    PRINT K$: PRINT
    DriveNumber = ASC(K$) - 65
    PRINT " 1 = 360K 5¬" + CHR$(34) + "  3 = 720K  3«" + CHR$(34)
    PRINT " 2 = 1.2M 5¬" + CHR$(34) + "  4 = 1.44M 3«" + CHR$(34)
    PRINT :  Print " Disk size 1 - 4 (or ESC to quit): ";
    DO
       DO : K$ = INKEY$ : LOOP UNTIL LEN(K$)
       IF K$ = CHR$(27) THEN GOTO Quit
    LOOP UNTIL INSTR ("1234", K$)
    PRINT K$ : PRINT
    DiskType = VAL(K$)
    DoVerify = %True
 END IF
 IF LEN(Cm$) THEN
    IF INSTR(Cmd$,":") THEN DriveNumber = ASC(MID$(Cm$,INSTR(Cm$,":")-1,1)) - 65
    IF DriveNumber < 0 OR DriveNumber > 1 THEN Syntax = %True
    DiskType = VAL(MID$(Cm$,INSTR(Cm$, "/1")+1,1)) + _
               VAL(MID$(Cm$,INSTR(Cm$, "/2")+1,1)) + _
               VAL(MID$(Cm$,INSTR(Cm$, "/3")+1,1)) + _
               VAL(MID$(Cm$,INSTR(Cm$, "/4")+1,1))
    IF DiskType < 1 OR DiskType > 4 THEN Syntax = %True
    IF INSTR(Cm$, "/V") THEN DoVerify = %True
    IF INSTR(Cm$, "/?") THEN Syntax = %True
 END IF
 IF Syntax THEN
       PRINT "Syntax: PBFORMAT Drive /DiskType [/V] [/Q]"
       PRINT "        Drive = A: or B:
       PRINT "        Disktype: /1 = 360k  /2 = 1.2M
       PRINT "                  /3 = 720k  /4 = 1.44M
       PRINT "        /V = Verify
       END
 END IF
 PRINT " Insert disk in drive "; CHR$(DriveNumber + 65);
 PRINT " and press RETURN (or ESC to quit)";
 DO
    DO: K$=INKEY$: LOOP UNTIL LEN(K$)
    IF K$ = CHR$(27) THEN END
 LOOP UNTIL K$ = CHR$(13)
 CLS
 F = FormatDisk (DiskType, DriveNumber, DoVerify)
 IF F = %False THEN el = 255
 Quit:
 RestoreScreen Temp$
END el  'end program with errorlevel

'===========================================================================
FUNCTION FormatDisk (BYVAL DType, BYVAL Drive, DoVerify) PUBLIC SHARED
'---------------------------------------------------------------------------
 InitData (DType)
 FormatDisk = %False
 SELECT CASE DType           ' determine drive type
        CASE 1               ' 360k or 1.2M drive?
             ! MOV AX, &H1500
             ! MOV DX, Drive
             ! INT &H13
             ! MOV Dt, AH
             SELECT CASE Dt
                    CASE 1  ' 360K in 360K drive
                         ! MOV AX, &H1701
                    CASE 2  ' 360K in 1.2M drive
                         ! MOV AX, &H1702
                    CASE ELSE  'invalid type
                         IF Error13 (251,0) THEN EXIT FUNCTION
             END SELECT
        CASE 2                  ' 1.2M in 1.2M drive
             ! MOV AX, &H1703
        CASE 3,4                ' 720K or 1.44M in 720K or 1.44M drive
             ! MOV AX, &H1704
  END SELECT
  Spt = Spt(DType)
  Tpd = Tpd(DType)
  ! MOV DX, Drive
  ! INT &H13
  ! MOV AX, &H1800
  ! MOV DX, Drive
  ! MOV CH, Tpd
  ! MOV CL, Spt
  ! INT &H13
  ResetDrive Drive
  BadSectors = 0 : Track = 0 : Sector = 1
  '-----------------
  ' Formatting
  '-----------------
  IF Tpd(DType) = &h27 THEN Sx = 2
  Box 8,22,13-Sx,35,15,3
  VIEW TEXT (22,8)-(57,25)
  LOCATE 2,5
  Print "   -þ- P B F O R M A T -þ-" : PRINT
  Y = 2 : X = 9
  COLOR 0,3
  FOR i=1 TO (Tpd(DType)+1)/20
      LOCATE Y + i, X
      PRINT STRING$(20,176)
  NEXT
  COLOR 15,3
  Y = 1 : X = 1
  Fat$ = STRING$(Spf(DType) * 512, CHR$(0))
  MID$(Fat$,1,3) = CHR$(Fid(DType)) + CHR$(&HFF) + CHR$(&HFF)
  RTotal = 2 * Spf(DType) + Dsc(DType) ' number of reserved sectors
  IF QuickFormat = %False THEN
     LOCATE 8-Sx,3 : PRINT " Formatting";Tpd(DType)+1;"tracks"
     DO
       Side = 0
       LOCATE 9-Sx,3 : PRINT " Formatting Track ";
       PRINT USING "##"; Track + 1;
       DO
         LOCATE 2+Y,8+X : IF Side=0 then COLOR 12,3 ELSE COLOR 14,3
         PRINT "²";
         Chrn$=""
         FOR Sector = 1 TO Spt(DType)
             Chrn$=Chrn$ + CHR$(Track)+ CHR$(Side) + _
             CHR$(Sector) + CHR$(Ssz(Dtype))
         NEXT
         COLOR 15,3
         LOCATE 9-Sx, 24: PRINT "Side"; Side;
         Offs& = STRPTR(Chrn$) : Segm& = STRSEG(Chrn$)
         Tries = 0 : Done = %False
         DO
           ! MOV AH,&H05    ;Format
           ! MOV AL, Spt
           ! MOV CH, Track
           ! MOV CL, 1
           ! MOV DH, Side
           ! MOV DL, Drive
           ! MOV ES, Segm&
           ! MOV BX, Offs&
           ! INT &H13
           ! MOV FormatError, AH
           IF FormatError THEN
              INCR Tries
              IF Tries > 2 THEN
                 IF Error13 (FormatError,1) THEN
                    EXIT FUNCTION
                 ELSE
                    Tries = 0
                 END IF
              ELSE
                 ResetDrive (Drive)
              END IF
           ELSE
              Done = %True
           END IF
         LOOP UNTIL Done
         INCR Side
       LOOP UNTIL Side = 2
       '-------------------
       ' Verifying
       '-------------------
       IF DoVerify THEN
          Maxbad = 100 : Side = 0
          LOCATE 9-Sx, 3 : PRINT " Verifying ";
          DO
             V = VerifySectors (DType,Drive,Track, Side,1,Spt(DType))
             IF V = 0 THEN
                IF Track = 0 THEN IF Error13 (253,0) THEN EXIT FUNCTION
                Sector = 0
                DO
                  INCR Sector
                  V = VerifySectors (DType,Drive,Track,Side,Sector,1)
                  IF V = 0 THEN
                     MarkBadSector DType, Track, Side, Sector
                     COLOR 12,3
                     LOCATE 10-Sx, 3 : PRINT " Bad sectors"; BadSectors
                     LOCATE Y+2,X+8 : PRINT "²":COLOR 15,3
                     IF BadSectors > MaxBad THEN
                        IF Error13 (252,0) THEN EXIT FUNCTION
                     END IF
                  END IF
                LOOP UNTIL Sector = Spt(DType)
             END IF
             INCR Side
          LOOP UNTIL Side = 2
       END IF
       IF Side=2 THEN INCR X : IF X MOD 21 = 0 THEN X = 1 : INCR Y
       INCR Track
     LOOP UNTIL Track > Tpd(DType)
  END IF
  '----------------------------------
  ' Writing boot and FATs
  '----------------------------------
  W = WriteSectors (DType, Drive, 0, 0, 1, 1, BootRecord$)
  RWritten = 0 : Track = 0 : Side = 0 : Sector = 2 : FATNum = 0
  DO
    IF Sector > Spt(DType) THEN Sector = 1 : INCR Side
    IF RWritten < Spf(DType) * 2 THEN
       IF FATNum > Spf(DType) - 1  THEN FATNum = 0
    ELSE
       MID$(FAT$,1,512) = STRING$(512, CHR$(0))
       IF RWritten = 2 * SPF(DType) THEN
          VIEW TEXT (1,1)-(80,25)
          Box 22-Sx,10,2,60,15,3
          LOCATE 23-Sx,11
          PRINT "Volume name (11 characters or RETURN for none): ..........."
          LOCATE 23-Sx,59,5
          VLabel$ = ""
          DO
             K$ = INKEY$
             IF K$ = CHR$(8) THEN
                LOCATE 23-Sx, POS(a) - 1 : PRINT " ";
                LOCATE 23-Sx, POS(a) - 1
             END IF
             IF K$ > CHR$(31) THEN
                PRINT K$;
                VLabel$ = VLabel$ + UCASE$(K$)
             END IF
          LOOP UNTIL K$ = CHR$(13)
          VLabel$=LEFT$(VLabel$+SPACE$(11),11)+CHR$(8)
          MID$(Fat$,1,12) = VLabel$
          MID$(Fat$,23,4) = Packtime$
       END IF
       FatNum = 0
    END IF
    W = WriteSectors (DType, Drive, Track, Side, Sector, 1, _
                      MID$(FAT$,1 + Fatnum * 511, 512))
    IF W THEN INCR Sector :  INCR RWritten :  INCR FatNum
  LOOP UNTIL RTotal = RWritten
  '--------------------------
  ' Give summery and clean up
  '--------------------------
  RestoreTable
  DiskInfo Drive, Total&, Free&
  LOCATE 18-Sx,23,0
  PRINT " "; Total&;" bytes total    "
  LOCATE 19-Sx,23
  PRINT " "; Free&; " bytes available"
  LOCATE 20-Sx,23
  IF BadSectors THEN PRINT " "; BadSectors * 512 ; "bytes marked as bad"
  FormatDisk = %True
  LOCATE 23-Sx,11
  PRINT "                        Press a key                        "
  WaitKey
END FUNCTION
'--------------------------------------------------
SUB MarkBadSector (Dtype, Track, Side, Sector) PUBLIC SHARED
'--------------------------------------------------
BadMark = &H0FF7  ' bad cluster mark
' CNum = cluster number
' FOffs = offset into fat for this cluster
' FVal = FAT value for this cluster
' OldVal = Old FAT value for this cluster
 CNum = (((((Track * 2) + Side) * Spt(DType)) + Sector - RTotal - 2) _
         \ ASC(MID$(Brd$(DType),1, 2))) + 2
 IF CNum > 1 THEN '   make sure it is in data space
    Foffs = ((CNum * 3) \ 2) +1
    FVal$ = MID$(Fat$, FOffs,2)
    FVal& = ASC(FVal$)
    IF CNum MOD 2 > 0  THEN
       OldVal = FVal& AND (BadMark * (2^4))
    ELSE
       OldVal = FVal& AND BadMark
    END IF
    IF OldVal = 0 THEN ' if not already marked bad, mark it
       IF CNum MOD 2 > 0 THEN
          FVal& = FVal& OR BadMark * (2 ^4)
       ELSE
          FVal& = FVal& OR BadMark
       END IF
       FVal$=RIGHT$("0000"+HEX$(FVal&),4)
       FVal$ = CHR$(VAL("&H"+(MID$(FVal$,3)))) + _
       CHR$(VAL("&H"+(LEFT$(FVal$,2))))
       MID$(Fat$,FOffs,2)=FVal$
       INCR BadSectors, ASC(LEFT$(Brd$(DType),1)) ' Count bad sector
    END IF
 END IF
END SUB
'-----------------------------------------------------------------------------
FUNCTION PackTime$
' format time and date as unsigned integer
' and return as string
'-----------------------------------------------------------------------------
 Year  = VAL(MID$(DATE$,7))
 Month = VAL(LEFT$(DATE$,2))
 Day   = VAL(MID$(DATE$,4,2)) :
 CurDate = (Year - 1980) * 512 + Month * 32 + Day
 D$ = HEX$(CurDate)
 Seconds = VAL(MID$(TIME$,7))
 Minutes = VAL(MID$(TIME$,4,2))
 Hours   = VAL(LEFT$(TIME$,2))
 CurTime = VAL("&B"+ BIN$(Hours) + RIGHT$("00000000000" + BIN$(Minutes) + _
            RIGHT$("00000"+BIN$(Seconds\2),5),11))
 T$ = HEX$(CurTime)
 PackTime$ = CHR$(VAL("&H"+MID$(T$,3))) + CHR$(VAL("&H"+LEFT$(T$,2))) + _
             CHR$(VAL("&H"+MID$(D$,3))) + CHR$(VAL("&H"+LEFT$(D$,2)))
END FUNCTION
'--------------------------------------------------------------------
SUB RestoreTable PUBLIC SHARED  ' Restore formatting table
'--------------------------------------------------------------------
 DEF SEG = DtSegm
     POKE$ DtOffs, Dfpt$
 DEF SEG
END SUB
'--------------------------------------------------
SUB ResetDrive (Drive)
'--------------------------------------------------
  ! MOV AX, 0
  ! MOV Dx, Drive
  ! INT &H13
END SUB
'--------------------------------------------------
FUNCTION WriteSectors (BYVAL DType,BYVAL Drive, BYVAL Track, BYVAL Side, _
                       BYVAL SSect, BYVAL NSect, Buffer$) PUBLIC SHARED
' write absolute disk sectors
'--------------------------------------------------
  WriteSectors = %False
  Tries = 0 : Done = %False
  Segm& = STRSEG(Buffer$) : Offs& = STRPTR(Buffer$)
  DO
    ! MOV AH,3
    ! MOV AL, Nsect
    ! MOV BX, Offs&
    ! MOV CH, Track
    ! MOV CL, SSect
    ! MOV DH, Side
    ! Mov DL, Drive
    ! MOV ES, Segm&
    ! INT &H13
    ! MOV WriteError, AH
    IF WriteError <> 0 THEN
       INCR Tries
       IF Tries > 2 THEN
          IF Error13 (WriteError,1) THEN EXIT FUNCTION ELSE Tries = 0
       ELSE
          ResetDrive (Drive)
       END IF
    ELSE
       Done = %True
    END IF
  LOOP UNTIL Done
  WriteSectors = %True
END FUNCTION
'--------------------------------------------------
FUNCTION VerifySectors (BYVAL DType, BYVAL Drive, BYVAL Track, BYVAL Side, _
                        BYVAL SSect, BYVAL NSect) PUBLIC SHARED
'--------------------------------------------------
  BUFF$ = STRING$(4000,CHR$(0))
  Offs& = STRPTR(Buff$):
  Segm& = STRSEG(Buff$)
  VerifySectors = %False
  Done = %False
  Tries = 0
  DO
   ! MOV AH,4
   ! MOV AL, Nsect
   ! MOV BX, Offs&
   ! MOV CH, Track
   ! MOV CL, Ssect
   ! MOV DH, Side
   ! Mov DL, Drive
   ! MOV ES, Segm&
   ! INT &H13
   ! MOV VerifyError, AH
   IF VerifyError THEN
       INCR Tries
       IF Tries > 5 THEN
          IF Error13 (VerifyError,1) THEN EXIT FUNCTION ELSE Tries = 0
       ELSE
          ResetDrive (Drive)
       END IF
    ELSE
       Done = %True
    END IF
  LOOP UNTIL Done
  VerifySectors = %True
END FUNCTION
'----------------------------------------------------------------------------
SUB InitData (DType) PUBLIC SHARED
'----------------------------------------------------------------------------
DIM Ssz(1:4), Spt(1:4), Tpd(1:4), Spf(1:4), Dsc(1:4), Fid(1:4), Brd$(1:4)
Ssz(1) = &H02 : Ssz(2) = &H02 : Ssz(3) = &H02 : Ssz(4) = &H02 'Sector size
Spt(1) = &H09 : Spt(2) = &H0F : Spt(3) = &H09 : Spt(4) = &H12 'Sectors/track
Tpd(1) = &H27 : Tpd(2) = &H4F : Tpd(3) = &H4F : Tpd(4) = &H4F 'Tracks/disk
Spf(1) = &H02 : Spf(2) = &H07 : Spf(3) = &H03 : Spf(4) = &H09 'Sectors/FAT
Dsc(1) = &H07 : Dsc(2) = &H0E : Dsc(3) = &H07 : Dsc(4) = &H0E 'Dirs/Sectors
Fid(1) = &HFD : Fid(2) = &HF9 : Fid(3) = &HF9 : Fid(4) = &HF0 'FAT identity
Brd$(1) = CHR$(&H02)+CHR$(&H01)+CHR$(&H00)+CHR$(&H02)+CHR$(&H70)+CHR$(&H00)+ _
          CHR$(&HD0)+CHR$(&H02)+CHR$(&HFD)+CHR$(&H02)+CHR$(&H00)+CHR$(&H09)+ _
          CHR$(&H00)+CHR$(&H02)
Brd$(2) = CHR$(&H01)+CHR$(&H01)+CHR$(&H00)+CHR$(&H02)+CHR$(&HE0)+CHR$(&H00)+ _
          CHR$(&H60)+CHR$(&H09)+CHR$(&HF9)+CHR$(&H07)+CHR$(&H00)+CHR$(&H0F)+ _
          CHR$(&H00)+CHR$(&H02)
Brd$(3) = CHR$(&H02)+CHR$(&H01)+CHR$(&H00)+CHR$(&H02)+CHR$(&H70)+CHR$(&H00)+ _
          CHR$(&HA0)+CHR$(&H05)+CHR$(&HF9)+CHR$(&H03)+CHR$(&H00)+CHR$(&H09)+ _
          CHR$(&H00)+CHR$(&H02)
Brd$(4) = CHR$(&H01)+CHR$(&H01)+CHR$(&H00)+CHR$(&H02)+CHR$(&HE0)+CHR$(&H00)+ _
          CHR$(&H40)+CHR$(&H0B)+CHR$(&HF0)+CHR$(&H09)+CHR$(&H00)+CHR$(&H12)+ _
          CHR$(&H00)+CHR$(&H02)
FOR i = 1 to 177
    READ B$
    Boot$ = Boot$ + CHR$(VAL("&H"+B$))
NEXT
BootRecord$ = Boot$ + STRING$(333,CHR$(0)) + CHR$(85) + CHR$(170)
MID$(BootRecord$, 14,14) = Brd$(DType) ' Insert variable part of boot record
' Change disk parameter table
  DEF SEG = 0
  DtOffs = PEEK(120) + PEEK(121) * 256
  DtSegm = PEEK(122) + PEEK(123) * 256
  DEF SEG = DtSegm
     Dfpt$ = PEEK$(&H522,11) ' Save table that we can restore it later
     SELECT CASE DType       ' set gap length for formatting
            CASE 1   : POKE DtOffs + 7, &H50 ' 360K
            CASE 2   : POKE DtOffs + 7, &H54 ' 1.2M
            CASE 3,4 : POKE DtOffs + 7, &H6C ' 720K or 1.44M
     END SELECT
     POKE DtOffs + 4, Spt(DType) ' sectors per track
     POKE DtOffs + 8, 247        ' formatting char (÷)
  DEF SEG
' boot record data
 DATA EB,29,90,50,42,46,4F,52,4D,41,54,00,02,02,01,00
 DATA 02,70,00,D0,02,FD,02,00,09,00,02,00,00,00,00,00
 DATA 02,70,00,D0,02,FD,02,00,09,00,02,00,00,00,00,00
 DATA 00,00,00,00,00,00,00,00,00,00,00,33,C9,8E,D9,8E
 DATA C1,FA,8E,D1,BC,00,7C,FB,C6,06,40,04,01,8B,F4,BF
 DATA 00,7E,B5,01,FC,F3,A5,E9,00,02,8B,DC,41,BA,80,00
 DATA B8,01,02,CD,13,72,0A,81,3E,FE,7D,55,AA,75,02,FF
 DATA E3,E8,2C,00,49,6E,73,65,72,74,20,73,79,73,74,65
 DATA 6D,20,64,69,73,6B,20,61,6E,64,20,70,72,65,73,73
 DATA 20,61,6E,79,20,6B,65,79,20,20,20,20,20,20,20,00
 DATA 5E,AC,22,C0,74,06,B4,0E,CD,10,EB,F5,32,E4,CD,16
 DATA B8,0D,0E,CD,10,B8,20,0A,32,FF,B9,50,00,CD,10,CD
 DATA 19
END SUB
'-----------------------------------------------------------------------------
SUB Waitkey
'-----------------------------------------------------------------------------
 DO : K$ = INKEY$ : LOOP UNTIL LEN(K$)
END SUB
'-----------------------------------------------------------------------------
FUNCTION Error13 (ErrNum, Ask) PUBLIC SHARED
' Show error and allow to retry
'-----------------------------------------------------------------------------
  Error13 = %True
  SELECT CASE ErrNum
         CASE 1    : ErrMsg$ = "Invalid drive"
         CASE 2    : ErrMsg$ = "Bad address mark"
         CASE 3    : ErrMsg$ = "Write protect error"
         CASE 4    : ErrMsg$ = "Bad sector"
         CASE 8    : ErrMsg$ = "DMA failure"
         CASE 16   : ErrMsg$ = "Bad CRC check"
         CASE 32   : ErrMsg$ = "Controller failure"
         CASE 64   : ErrMsg$ = "Bad seek"
         CASE 128  : ErrMsg$ = "Drive not ready"
         CASE 251  : ErrMsg$ = "Invalid media type"
         CASE 252  : ErrMsg$ = "Too many bad sectors"
         CASE 253  : ErrMsg$ = "Bad disk, cannot format"
         CASE 255  : ErrMsg$ = "Disk not formatted."
         CASE ELSE : ErrMsg$ = "Unknown disk error"
  END SELECT
  VIEW TEXT (1,1)-(80,25)
  M$= ErrMsg$
  IF Ask THEN
     M$=M$ + " (A)bort or (R)etry? "
  ELSE
     M$=M$+" - Press a key"
  END IF
  Xp=(80-LEN(M$)-2)\2
  SaveScreen T$
  Box 22,Xp,2,LEN(M$)+2,12,3
  LOCATE 23,Xp+1 :PRINT M$;
  IF Ask THEN
     DO
        DO : K$ = UCASE$(INKEY$) :LOOP UNTIL LEN(K$)
     LOOP UNTIL INSTR(CHR$(&H27)+"AR", K$)
     IF K$ = "R" THEN Error13 = %False
  ELSE
     Waitkey
  END IF
  RestoreScreen T$
  VIEW TEXT (22,8)-(57,22)
 COLOR 15,3
END FUNCTION
'---------------------------------------------------------------------
FUNCTION DosVersion! PUBLIC SHARED ' Get DOS version decimal (e.g. 6.2)
'---------------------------------------------------------------------
    ! MOV AX, &h3000
    ! INT &H21
    ! MOV Hi, AH
    ! MOV Lo, AL
    DosVersion! = Lo + Hi / 100
END FUNCTION
'---------------------------------------------------------------------
SUB DiskInfo (BYVAL Drive, Total&, Free&) PUBLIC SHARED
'--------------------------------------------------------------------
 LOCAL f,c,s,b
    Drive = Drive + 1     ' A = 1 / B = 2
    ! MOV AX,&H3600       ; AH 36h = Get drive information
    ! MOV DX, Drive       ; drive number
    ! INT &H21            ; do it
    ! MOV f, BX           ; free clusters
    ! MOV c, DX           ; total number of clusters
    ! MOV s, AX           ; sectors per cluster
    ! MOV b, CX           ; bytes per sector
    Total& = c * s * b    ' bytes total
    Free&  = f * s * b    ' bytes available
    Bytes  = b * s        ' bytes per cluster
    Clusters = c          ' clusters on disk
    Sectors = s           ' sectors per cluster
END SUB
'-----------------------------------------------
SUB Box (Row, Col, Rows, Cols, Fgc, Bgc) PUBLIC
'-----------------------------------------------
COLOR Fgc,Bgc
LOCATE Row, Col
PRINT "Ú" + STRING$(Cols - 1, "Ä") + "¿"
LOCATE Row + Rows , Col
PRINT "À" + STRING$(Cols - 1, "Ä") + "Ù"
FOR i = 1 TO Rows - 1
    LOCATE Row + i, Col
    PRINT "³" + SPACE$(Cols - 1) + "³"
NEXT
END SUB
'-----------------------------
SUB SaveScreen (Scrn$) PUBLIC SHARED
'-----------------------------
  IF (pbvScrnCard AND 1) = 0 THEN
	Address = &HB800
  ELSE
	Address = &HB000
  END IF
  DEF SEG = Address
      Scrn$ = PEEK$(0,4000)
  DEF SEG
  END SUB
'--------------------------------
SUB RestoreScreen (Scrn$) PUBLIC SHARED
'--------------------------------
  IF (pbvScrnCard AND 1) = 0 THEN
	Address = &HB800
  ELSE
	Address = &HB000
  END IF
  DEF SEG = Address
      POKE$ 0,Scrn$
  DEF SEG
END SUB
'==== eof ==================================================================
