home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol147 / findbad.a86 < prev    next >
Encoding:
Text File  |  1984-04-29  |  31.3 KB  |  1,156 lines

  1.     TITLE    'FINDBAD Ver 1.0 - Universal Version for CP/M-86'
  2. ;
  3. ;            FINDBAD.A86 Ver. 1.0
  4. ;
  5. ;    Modified for CP/M-86 from FINDBAD52.ASM of 02/16/82
  6. ;
  7. ;FINDBAD will find all bad blocks on a disk and build a file
  8. ;named [UNUSED].BAD to allocate them, thus "locking out" the
  9. ;bad blocks so CP/M-86 will not use them.
  10. ;
  11. ;Originally written by Gene Cotton,  published in "Interface
  12. ;Age", September 1980 issue, page 80.
  13. ;
  14. ;               SYSTST and BADUSR options:
  15. ;
  16. ;  Many double-density disk systems have single-density system 
  17. ;tracks.  If this is true with your system, you can change the 
  18. ;program to skip the system tracks,  without re-assembling it.  
  19. ;To do this, set the byte at 103H to a 0 if you don't want the 
  20. ;system  tracks tested,  otherwise leave it 0.   This is  also 
  21. ;necessary if you have a "blocked" disk system;  that is, when 
  22. ;the same physical disk is seperated into logical disks by use 
  23. ;of the SYSTRK word in the disk parameter block.
  24. ;
  25. ;   If  you  are a CP/M-86 1.x user,  you may assign the  user 
  26. ;number  where  [UNUSED.BAD] will be created by  changing  the  
  27. ;byte  at 104H  to  the  desired useru may assign the  user 
  28. ;number  where  [UNUSED.BAD] will be created by  changing  the  
  29. ;byte  at 104H  to  the  desired user--------------
  30. ;NOTE: If you want to  update this program, make sure you have 
  31. ;the latest version first.   After adding your changes, please 
  32. ;modem  a  copy of the new file to CP/M-NET  in  Simi  Valley, 
  33. ;California - phone (805) 527-9321 (50,  110,  300, 450 or 600 
  34. ;baud). Use the filename FNDBDA86.NEW. (K. Smith)
  35. ;
  36. ;Modifications/updates:
  37. ;
  38. ;02/03/82  Changed to CP/M-86 compatibility with the help  of 
  39. ;          DR's XLT86 utility, made changes for goofs made by 
  40. ;           XLT86,  removed ANYTHING to do with CP/M-80  Ver. 
  41. ;          1.4. (K. Smith, CP/M-Net 'SYSOP')
  42. ;         Note: You must assemble with ASM86.CMD as follows:
  43. ;
  44. ;               ASM86 FINDBAD<cr>
  45. ;
  46. ;               Then create a '.CMD' as,
  47. ;
  48. ;               GENCMD FINDBAD 8080<cr>
  49. ;
  50. ;04/10/81 Changed extent DB from -1 to 0FFH so program can be
  51. ;      assembled by ASM.  Added BADUSR info to instructions
  52. ;      for altering with DDT.  (KBP)
  53. ;
  54. ;04/09/81 Changed sign-on message, added control-c abort test,
  55. ;      added '*' to console once each track    (RGF)
  56. ;
  57. ;04/07/81 Re-wrote to add the following features:
  58. ;        1) "universal" operation
  59. ;        2) DDT-changeable "SYSTRK" boolean (see above)
  60. ;        3) Report to console when bad blocks are detected
  61. ;        4) Changed the method of printing the number of
  62. ;           bad blocks found (at end of run)...the old
  63. ;           method used too much code, and was too cum-
  64. ;           bersome.
  65. ;        5) Made several cosmetic changes
  66. ;
  67. ;            Ron Fowler
  68. ;            Westland, Mich
  69. ;
  70. ;03/23/81 Set equates to standard drive and not double-sided. (KBP)
  71. ;
  72. ;03/01/81 Corrected error for a Horizon with double sided drive.
  73. ;      This uses 32k extents, which code did not take into account.
  74. ;      (Bob Clyne)
  75. ;
  76. ;02/05/81 Merged 2/2/81 and 1/24/81 changes, which were done
  77. ;      independently by Clyne and Mack.  (KBP)
  78. ;
  79. ;02/02/81 Added equates for North Star Horizon - 5.25" drives,
  80. ;      double density, single and double sided. (Bob Clyne)
  81. ;
  82. ;01/24/81 Added equates for Jade DD disk controller
  83. ;      (Pete H. Mack)
  84. ;
  85. ;01/19/81 Added equates for Icom Microfloppy 5.25" drives.
  86. ;      (Eddie Currie)
  87. ;
  88. ;01/05/81 Added equates for Heath H-17 5.25" drives.
  89. ;      (Ben Goldfarb)
  90. ;
  91. ;12/08/80 Added equates for National Multiplex D3S/D4S
  92. ;      double-density board in various formats.
  93. ;      (David Fiedler)
  94. ;
  95. ;09/22/80 Added equates for Morrow Disk Jockey 2D/SS, 256,
  96. ;      512 and 1024-byte sector options.  Fix 'S2' update
  97. ;      flag for larger max number of extents. Cleaned up
  98. ;      file. (Ben Bronson and KBP)
  99. ;
  100. ;09/14/80 Corrected DGROUP equate for MMDBL. Added new routine
  101. ;      to correct for IMDOS group allocation.  Corrected
  102. ;      error in instructions for using TEST routine.
  103. ;      (CHS) (AJ) (KBP) - (a group effort)
  104. ;
  105. ;09/08/80 Fixed several errors in Al Jewer's mods.  Changed
  106. ;      return to CP/M to warm boot so bitmap in memory will
  107. ;      be properly updated. Added conditional assembly for
  108. ;      testing program. (KBP)
  109. ;
  110. ;09/02/80 Added IMDOS double-density equates & modified for 
  111. ;      more then 256 blocks per disk. (Al Jewer)
  112. ;
  113. ;09/01/80 Changed equates so that parameters are automatically
  114. ;      set for each disk system conditional assembly (KBP)
  115. ;
  116. ;08/31/80 Add conditional assembly for Digital Microsystems FDC3
  117. ;      controller board in double-density format and fix to
  118. ;      do 256 blocks in one register. (Thomas V. Churbuck)
  119. ;
  120. ;08/31/80 Correct MAXB equate - MAXB must include the directory
  121. ;      blocks as well as the data blocks.  Fix to make sure
  122. ;      any [UNUSED].BAD file is erased before data area is
  123. ;      checked. (KBP)
  124. ;
  125. ;08/30/80 Added conditional assembly for Micromation
  126. ;      double-density format. (Charles H. Strom)
  127. ;
  128. ;08/27/80 Fix missing conditional assembly in FINDB routine.
  129. ;      Put version number in sign-on message. (KBP)
  130. ;
  131. ;08/26/80 Modified by Keith Petersen, W8SDZ, to:
  132. ;      (1) Add conditional assembly for 1k/2k groups
  133. ;      (2) Add conditional assembly for standard drives
  134. ;          and Micropolis MOD II
  135. ;      (3) Make compatible with CP/M-2.x
  136. ;      (4) Remove unneeded code to check for drive name
  137. ;          (CP/M does it for you and returns it in the FCB)
  138. ;      (5) Changed to open additional extents as needed for
  139. ;          overflow, instead of additional files
  140. ;      (6) Add conditional assembly for system tracks check
  141. ;          (some double-density disks have single-density
  142. ;        .
  143. ;
  144. ;08/06/80 Added comments and crunched some code.
  145. ;      KELLY SMITH.    805-527-9321 (Modem, 300 Baud)
  146. ;            805-527-0518 (Verba files
  147. ;      (6) Add conditional assembly for system tracks check
  148. ;          (some double-density disks have single-density
  149. ;        l)
  150. ;
  151. ;
  152. ;            Using the Program
  153. ;
  154. ; Before  using this program to "reclaim" a diskette,  it  is
  155. ;recommended that the diskette be reformatted. If this is not
  156. ;possible,  at least assure yourself that any existing    files
  157. ;on the diskette  do not contain unreadable  sectors.  If you
  158. ;have changed disks since the last warm-boot, you  must warm-
  159. ;boot again before running this program.
  160. ;
  161. ; To  use the program,    insert    both the disk containing  the
  162. ;program  FINDBAD.CMD and the diskette to be checked into the
  163. ;disk drives. It is possible that the diskette containing the
  164. ;program is the one to be checked. Assume that the program is
  165. ;on drive "A" and the suspected bad disk is on drive "B".  In
  166. ;response to the CP/M prompt "A>",  type in FINDBAD B:.  This
  167. ;will  load the file FINDBAD.CMD from drive "A" and test  the
  168. ;diskette  on  drive "B" for  unreadable  sectors.  The  only
  169. ;allowable  parameter  after  the  program name  is  a    drive
  170. ;specification    (of the form " N:") for up to four (A  to  D)
  171. ;disk drives.  If no drive is specified, the currently logged
  172. ;in drive is assumed to contain the diskette to check.
  173. ;
  174. ; The  program first checks the CP/M System tracks (0 and 1),
  175. ;and  any  errors here prohibit the disk from being  used  on
  176. ;drive    "A",  since all "warm  boots" occur using the  system
  177. ;tracks from the "A" drive.
  178. ;
  179. ; The  program next checks the first two data blocks  (groups
  180. ;to some of us) containing the directory of the diskette.  If
  181. ;errors  occur    here,  the  program  terminates  and  control
  182. ;returns  to  CP/M  (no other data blocks are  checked    since
  183. ;errors in the directory render the disk useless).
  184. ;
  185. ; Finally,  all  the remaining data blocks are    checked.  Any
  186. ;sectors  which  are  unreadable cause the data  block    which
  187. ;contains them to be stored temporarily as a "bad block".  At
  188. ;the end of this phase,  the message "XX bad blocks found" is
  189. ;d;contains them to be stored temporarily as a "bad block".  At
  190. ;the end of this phase,  the message "XX bad blocks found" is
  191. ;dname [UNUSED].BAD is created, the list of "bad blocks" is
  192. ;placed  in  the allocation map of the    directory  entry  for
  193. ;[UNUSED].BAD,    and the file is closed.  Note,    that when the
  194. ;number of "bad blocks" exceeds 16,  the  program  will  open
  195. ;additional  extents  as  required  to    hold the overflow.  I
  196. ;suggest that if the diskette has more than  32 "bad blocks",
  197. ;perhaps it should be sent to the "big disk drive in the sky"
  198. ;for the rest it deserves.
  199. ;
  200. ; The  nifty part of all this is that if any "bad blocks"  do
  201. ;occur, they are allocated to [UNUSED].BAD and no longer will
  202. ;be available to CP/M-86 for future allocation... bad sectors
  203. ;are logically locked out on the diskette
  204. ;
  205. ;
  206. ;           Using the TEST conditional assembly
  207. ;
  208. ;A  conditional  assembly has been added to allow  testing  this 
  209. ;program  to  make sure it is reading all sectors on  your  disk 
  210. ;that  are accessible to CP/M.    The program reads the disk on  a 
  211. ;block by block basis, so it is necessary to first determine the 
  212. ;number of blocks present.  To start, we must know the number of 
  213. ;sectors/block (8 sectors/block for standard IBM single  density 
  214. ;format).  If  this  value  is    not  known,  it  can  easily  be 
  215. ;determined  by saving one page in a test file and interrogating 
  216. ;using the STAT command:
  217. ;
  218. ;For standard single-density STAT will report this file as being
  219. ;1k.  The file size reported (in bytes) is the size of a  block. 
  220. ;This  value  divided  by 128 bytes/sector  (the  standard  CP/M 
  221. ;sector  size)    will  give sectors/block.  For    our  IBM  single 
  222. ;density example, we have:
  223. ;
  224. ;  (1024 bytes/block) / (128 bytes/sector) = 8 sectors/block.
  225. ;
  226. ;We  can now calculate blocks/track (assuming we know the number 
  227. ;sectors/track). In our example:
  228. ;
  229. ;  (26 sectors/track) / (8 sectors/block) = 3.25 blocks/track
  230. ;
  231. ;Now  armed with the total number of data tracks (75 in our  IBM 
  232. ;single density example), we get total blocks accessible: 
  233. ;
  234. ;  75 (tracks/disk) x (3.25 blocks/track) = 243.75 blocks/disk 
  235. ;
  236. ;CP/M cannot access a fractional block, so we round down (to 243 
  237. ;blocks  in  our  example).  Now  multiplying  total  blocks  by 
  238. ;sectors/block    results in total sectors as should  be    reported 
  239. ;when TEST is set TRUE and a good disk is read. For our example, 
  240. ;this value is 1944 sectors. 
  241. ;
  242. ;Finally,  note that if SYSTEM is set TRUE,  the sectors present 
  243. ;on  the  first  two tracks must be added in  as  well.  In  the 
  244. ;previous  example,  this  results in  1944 + 52 = 1996  sectors 
  245. ;reported by the TEST conditional.
  246. ;
  247. ;Run the program on a KNOWN-GOOD disk.    It should report that it
  248. ;has read  the    correct number of sectors.  The test conditional
  249. ;assembly should then be set FALSE and the program re-assembled.
  250. ;The test routines  cannot be left in  because this program does
  251. ;not read all the sectors in a block that is found to be bad and
  252. ;thus will report an inaccurate number of sectors read.
  253. ;
  254. ;
  255. ;Define TRUE and FALSE
  256. ;
  257. FALSE    EQU    0
  258. TRUE    EQU    NOT FALSE
  259. ;
  260. ;******************************************************************
  261. ;
  262. ;Conditional assembly switch for testing this program
  263. ;(for initial testing phase only - see remarks above)
  264. ;
  265. TEST    EQU    FALSE            ;TRUE FOR TESTING ONLY
  266. ;
  267. ;******************************************************************
  268. ;
  269. ;System equates
  270. ;
  271. BASE    EQU    0            ;STANDARD CP/M BASE ADDRESS
  272. FCB    EQU    BASE+5CH        ;CP/M DEFAULT FCB LOCATION
  273. ;
  274. ;Define ASCII characters used
  275. ;
  276. CR    EQU    0DH            ;CARRIAGE RETURN CHARACTER
  277. LF    EQU    0AH            ;LINE FEED CHARACTER
  278. TAB    EQU    09H            ;TAB CHARACTER
  279. BEL    EQU    07H            ;BELL CHARACTER
  280. ;
  281. M    EQU    Byte Ptr    0[BX]
  282. ;
  283.     cseg
  284.     ORG    BASE+100H
  285. ;
  286.     JMPS    START            ;JMP AROUND OPTION BYTES
  287. ;
  288. ;If you want the system tracks tested, then put a 1 here, otherwise 0.
  289. ;
  290. SYSTST    DB    0            ;0 IF NO SYS TRACKS, OTHERWISE 1
  291. ;
  292. ;   Change this byte to the user number you want [UNUSED].BAD 
  293. ;to reside in.  If you want it in the default user, then leave 
  294. ;it 0FFH.
  295. ;
  296. BADUSR    DB    0FFH            ;USER # WHERE [UNUSED.BAD] GOES
  297. ;0FFH = DEFAULT USER
  298. ;
  299. START:    CALL    START2            ;GO PRINT SIGNON
  300. ;
  301.     DB    CR,LF,'FINDBAD - Ver 1.0, Bad Sector Lockout Utility'
  302.     DB    CR,LF
  303.     DB    '------- Universal Version for CP/M-86 -------'
  304.     DB    CR,LF,CR,LF,'Type CTL-C to abort',CR,LF,'$'
  305. ;
  306. START2:    POP    DX            ;GET MSG ADRS
  307.     MOV    CL,9            ;bdos PRINT BUFFER function
  308.     NOBAD            ;SAY NO BAD BLOCKS, IF SO
  309.     CALL    SETDM            ;FIX DM BYTES IN FCB
  310. ;
  311. NOBAD:    CALL    CRLF
  312.     MOV    AL,TAB
  313.     CALL    DISPLAY
  314.     MOV    DX'
  315.     DB    CR,LF,CR,LF,'Type CTL-C to abort',CR,LF,'$'
  316. ;
  317. START2:    POP    DX            ;GET MSG ADRS
  318.     MOV    CL,9            ;bdos PRINT BUFFER function
  319. ,(Offset NOMSG)    ;POINT FIRST TO 'NO'
  320.     MOV    BX,Word Ptr BADBKS    ;PICK UP # BAD BLOCKS
  321.     MOV    AL,BH            ;CHECK FOR ZERO
  322.     OR    AL,BL
  323.     JZ    PMSG1            ;JUMP IF NONE
  324.     CALL    DECOUT            ;OOPS..HAD SOME BAD ONES, REPORT
  325.     JMPS    PMSG2
  326. ;
  327. PMSG1:    MOV    CL,9            ;bdos PRINT BUFFER function
  328.     INT    224
  329. ;
  330. PMSG2:    MOV    DX,(Offset ENDMSG)    ;REST OF EXIT MESSAGE
  331. ;
  332. PMSG:    MOV    CL,9
  333.     INT    224
  334. ;
  335.     MOV    CL,0            ;EXIT TO CP/M WARM BOOT
  336.     MOV    DL,0
  337.     INT    224
  338. ;
  339. ;Get actual address of BIOS routines
  340. ;
  341. SETUP    EQU    $    ;Check for drive specification
  342. ;
  343. GDRIV:    MOV    AL,Byte Ptr .FCB    ;GET DRIVE NAME
  344.     MOV    CL,AL
  345.     OR    AL,AL            ;ZERO?
  346.     JNZ    GD2            ;IF NOT,THEN GO SPECIFY DRIVE
  347.     MOV    CL,25            ;GET LOGGED-IN DRIVE
  348.     INT    224
  349.     INC    AL            ;MAKE 1-RELATIVE
  350.     MOV    CL,AL
  351. ;
  352. GD2:    CMP    AL,15+1            ;CHECK FOR HIGHEST DRIVE NUMBER
  353.     JNAE    GD3    
  354.     JMP    SELERR            ;SELECT ERROR
  355. ;
  356. GD3:    DEC    CL            ;BACK OFF FOR CP/M
  357.     PUSH    CX            ;SAVE DISK SELECTION
  358.     MOV    DL,CL            ;ALIGN FOR BDOS
  359.     MOV    CL,14            ;SELECT DISK function
  360.     INT    224
  361.     POP    CX            ;GET BACK DISK NUMBER
  362. ;
  363. SETDSK:    MOV    Byte Ptr FUNC,9        ;bios select disk function
  364.     MOV    Word Ptr BIOS_DESC,CX    ;pass disk number to bios descriptor
  365.     MOV    Word Ptr BIOS_DESC+2,0    ;fill remaining descriptor (DX) zero
  366.     MOV    DX,(Offset FUNC)    ;point to function parameter block
  367.     MOV    CL,50            ;direct bios call
  368.     INT    224            ;do it, to it...
  369. ;
  370.     MOV    DL,ES: M        ;GET SECTOR TABLE PNTR
  371.     LAHF
  372.     INC    BX
  373.     SAHF
  374.     MOV    DH,ES: M
  375.     LAHF
  376.     INC    BX
  377.     SAHF
  378.     XCHG    BX,DX
  379.     MOV    Word Ptr SECTBL,BX    ;STORE IT AWAY
  380.     MOV    BX,8            ;OFFSET TO DPB POINTER
  381.     LAHF
  382.     ADD    BX,DX
  383.     RCR    SI,1
  384.     SAHF
  385.     RCL    SI,1
  386.     MOV    AL,ES: M            ;PICK UP DPB POINTER
  387.     LAHF                ;  TO USE
  388.     INC    BX
  389.     SAHF
  390.     MOV    BH,ES: M            ;  AS PARAMETER
  391.     MOV    BL,AL            ;  TO LOGIT
  392. ;
  393. DOLOG:    CALL    LOGIT            ;LOG IN DRIVE, GET DISK PARMS
  394.     CALL    GETDIR            ;CALCULATE DIRECTORY INFORMATION
  395. ;
  396. ;
  397. HOMDSK:    MOV    Byte Ptr FUNC,8        ;bios HOME DISK function
  398.     MOV    Word Ptr BIOS_DESC,0    ;pass 'nothing' number to bios descriptor
  399.     MOV    Word PtrOV    Byte Ptr FUNC,8        ;bios HOME DISK function
  400.     MOV    Word Ptr BIOS_DESC,0    ;pass 'nothing' number to bios descriptor
  401.     MOV    Word Ptrect bios call
  402.     INT    224            ;do it, to it...
  403. ;
  404. ;Now set the required user number
  405. ;
  406.     MOV    AL,Byte Ptr BADUSR    ;GET THE USER NUMBER
  407.     CMP    AL,0FFH            ;IF IT IS 0FFH, THEN RETURN
  408.     JNZ    L_6
  409.     RET
  410. L_6:
  411.     MOV    CL,32            ;GET/SET USER CODE
  412.     INT    224
  413.     RET
  414. ;
  415. ;Look for bad blocks
  416. ;
  417. FINDB:    MOV    AL,Byte Ptr SYSTST
  418.     OR    AL,AL
  419.     JZ    DODIR            ;JUMP IF NO SYS TRACKS TO BE TESTED
  420.     CALL    CHKSYS            ;CHECK FOR BAD BLOCKS ON TRACK 0 AND 1
  421. ;
  422. DODIR:    CALL    CHKDIR            ;CHECK FOR BAD BLOCKS IN DIRECTORY
  423.     CALL    TELL1
  424. ;
  425.     DB    CR,LF,'Testing data area...',CR,LF,'$'
  426. ;
  427. TELL1:    POP    DX
  428.     MOV    CL,9            ;bdos PRINT STRING function
  429.     INT    224
  430.     CALL    ERAB            ;ERASE ANY [UNUSED].BAD FILE
  431.     MOV    BX,Word Ptr DIRBKS    ;START AT FIRST DATA BLOCK
  432.     MOV    CX,BX            ;PUT INTO [CX]
  433. ;
  434. FINDBA:    CALL    READB            ;READ THE BLOCK
  435.     JZ    L_7    
  436.     CALL    SETBD            ;IF BAD, ADD BLOCK TO LIST
  437. L_7:
  438.     LAHF                ;BUMP TO NEXT BLOCK
  439.     INC    CX
  440.     SAHF
  441.     MOV    BX,Word Ptr DSM
  442.     MOV    DX,CX            ;SET UP FOR (MAXGRP - CURGRP)
  443.     SBB    BX,DX            ;DO SUBTRACT: (MAXGRP - CURGRP)
  444.     JNB    FINDBA            ;UNTIL CURGRP>MAXGRP
  445.     CALL    CRLF
  446.     MOV    BX,Word Ptr DMCNT    ;GET NUMBER OF BAD SECTORS
  447.     MOV    AL,BH
  448.     OR    AL,BL            ;SET ZERO FLAG, IF NO BAD BLOCKS
  449.     RET                ;RETURN FROM "FINDB"
  450. ;
  451. ;Check system tracks, notify user if bad, but continue
  452. ;
  453. CHKSYS:    CALL    CHSY1            ;bdos PRINT MESSAGE
  454. ;
  455.     DB    CR,LF,'Testing system tracks...',CR,LF,'$'
  456. ;
  457. CHSY1:    POP    DX
  458.     MOV    CL,9            ;bdos PRINT STRING function
  459.     INT    224
  460.     MOV    BX,0            ;SET TRACK 0, SECTOR 1
  461.     MOV    Word Ptr TRACK,BX
  462.     LAHF
  463.     INC    BX
  464.     SAHF
  465.     MOV    Word Ptr SECTOR,BX
  466. ;
  467. CHKSY1:    CALL    READS            ;READ A SECTOR
  468.     JNZ    SYSERR            ;NOTIFY, IF BAD BLOCKS HERE
  469.     MOV    BX,Word Ptr SYSTRK    ;SET UP (TRACK-SYSTRK)
  470.     XCHG    BX,DX
  471.     MOV    BX,Word Ptr TRACK
  472.     SBB    BX,DX            ;DO THE SUBTRACT
  473.     JB    CHKSY1            ;LOOP WHILE TRACK < SYSTRK
  474.     RET                ;RETURN FROM "CHKSYS"
  475. ;
  476. SYSERR:    MOV    DX,(Offset ERMSG5)    ;SAY NO GO, AND BAIL OUT
  477.     MOV    CL,9            ;bdos PRINT BUFFER function
  478.     INT    224
  479.     RET                ;RETURN FROM "SYSERR"
  480. ;
  481. ;Check for bad blocks in directory area
  482. ;
  483. CHKDIR:    CALL    CHKD1
  484. ;
  485.     DB    CR,LF,'Testing directory area...',CR,LF,'$'
  486. ;
  487. CHKD1:    POP    DX
  488.     MOV    CL,9            ;bdos PRINT STRING function
  489.     INT    224
  490.     MOV    CX,0            ;START AT BLOCK 0
  491. ;
  492. CHKDI1:    CALL    READB            ;READ A BLOCK
  493.     JZ    L_8    
  494.     JMP    ERROR6            ;IF BAD, INDICATE ERROR IN DIRECTORY AREA
  495. L_8:
  496.     LAHF                ;BUMP FOR NEXT BLOCK
  497.     INC    CX
  498.     SAHF
  499.     MOV    BX,Word Ptr DIRBKS    ;SET UP (CURGRP - DIRBKS)
  500.     LAHF                ;MAKE 0-RELATIVE
  501.     DEC    BX
  502.     SAHF
  503.     MOV    DX,CX
  504.     SBB    BX,DX            ;DO THE SUBTRACT
  505.     JNB    CHKDI1            ;LOOP UNTIL CURGRP > DIRGRP
  506.     RET                ;RETURN FROM "CHKDIR"
  507. ;
  508. ;Read all sectors in block, and return zero flag set if none bad
  509. ;
  510. READB:    CALL    CNVRTB            ;CONVERT TO TRACK/SECTOR IN H&L REGS.
  511.     MOV    AL,Byte Ptr BLM
  512.     INC    AL            ;NUMBER OF SECTORS/BLOCK
  513.     MOV    DH,AL            ;  IN D REG
  514. ;
  515. READBA:    PUSH    DX
  516.     CALL    READS            ;READ SKEWED SECTOR
  517.     POP    DX
  518.     JZ    L_9    
  519.     RET                ;ERROR IF NOT ZERO...
  520. L_9:
  521.     DEC    DH            ;DEBUMP SECTOR/BLOCK
  522.     JNZ    READBA            ;DO NEXT, IF NOT FINISHED
  523.     RET                ;RETURN FROM "READBA"
  524. ;
  525. ;Convert block number to track and skewed sector number
  526. ;
  527. CNVRTB:    PUSH    CX            ;SAVE CURRENT GROUP
  528.     MOV    BX,CX            ;NEED IT IN [BX], FOR EASY SHIFTING
  529.     MOV    AL,Byte Ptr BSH        ;DPB VALUE THAT TELLS HOW TO
  530. ;
  531. SHIFT:    SHL    BX,1            ;  SHIFT GROUP NUMBER TO GET
  532.     DEC    AL            ;  DISK-DATA-AREA RELATIVE
  533.     JNZ    SHIFT            ;  SECTOR NUMBER
  534.     XCHG    BX,DX            ;REL SECTOR # INTO DE
  535.     MOV    BX,Word Ptr SPT        ;SECTORS PER TRACK FROM DPB
  536.     NOT    BX            ;1ST 1'S COMPLEMENT...
  537.     INC    BX            ;...THEN 2'S COMPLEMENT
  538.     XCHG    BX,DX
  539.     MOV    CX,0            ;INITIALIZE QUOTIENT
  540. ;
  541. ;Divide by number of sectors
  542. ;    quotient = track
  543. ;         mod = sector
  544. ;
  545. DIVLP:    LAHF                ;DIRTY DIVISION
  546.     INC    CX
  547.     SAHF
  548.     LAHF
  549.     ADD    BX,DX
  550.     RCR    SI,1
  551.     SAHF
  552.     RCL    SI,1
  553.     JB    DIVLP
  554.     LAHF                ;FIXUP LAST
  555.     DEC    CX
  556.     SAHF
  557.     XCHG    BX,DX
  558.     MOV    BX,Word Ptr SPT
  559.     LAHF
  560.     ADD    BX,DX
  561.     SAHF
  562.     LAHF
  563.     INC    BX
  564.     SAHF
  565.     MOV    Word Ptr SECTOR,BX    ;NOW HAVE LOGICAL SECTOR
  566.     MOV    BX,Word Ptr SYSTRK    ;BUT BEFORE WE HAVE TRACK #,
  567.     LAHF                ;  WE HAVE TO ADD SYS TRACK OFFSET
  568.     ADD    BX,CX
  569.     RCR    SI,1
  570.     SAHF
  571.     RCL    SI,1
  572.     MOV    Word Ptr TRACK,BX
  573.     POP    CX            ;THIS WAS OUR GROUP NUMBER
  574.     RET
  575. ;
  576. ;READS reads a logical sector (if it can)
  577. ;and returns zero flag set if no error.
  578. ;
  579. READS:    PUSH    CX            ;SAVE THE GROUP NUMBER
  580. ;
  581.     CALL    LTOP            ;CONVERT LOGICAL TO PHYSICAL
  582.     MOV    BX,Word Ptr PHYSEC    ;GET PHYSICAL SECTOR
  583.     MOV    CX,BX            ;INTO [CX]
  584. ;
  585. SETSEC:    MOV    BYTE PTR FUNC,11    ;bios SET SECTOR function
  586.     MOV    WORD PX,(Offset FUNC)    ;point to function parameter block
  587.     MOV    CL,50            ;direct bios call
  588.     INT    224            ;do it, to it...
  589. ;
  590.     MOV    BX,WordPtr PHYSEC    ;GET PHYSICAL SECTOR
  591.     MOV    CX,BX            ;INTO [CX]
  592. ;
  593. SETSEC:    MOV    BYTE PTR FUNC,11    ;bios SET SECTOR function
  594.     MOV    WORD P Ptr TRACK    ;NOW SET THE TRACK
  595.     MOV    CX,BX            ;CP/M WANTS IT IN [CX]
  596. ;
  597. SETTRK:    MOV    BYTE PTR FUNC,10    ;bios SELECT TRACK function
  598.     MOV    WORD PTR BIOS_DESC,CX    ;pass track number to bios descriptor
  599.     MOV    WORD PTR BIOS_DESC+2,0    ;fill remaining descriptor (DX) zero
  600.     MOV    DX,(Offset FUNC)    ;point to function parameter block
  601.     MOV    CL,50            ;direct bios call
  602.     INT    224            ;do it, to it...
  603. ;
  604. ;Now do the sector read
  605. ;
  606. DREAD:    MOV    BYTE PTR FUNC,13    ;bios READ SECTOR function
  607.     MOV    WORD PTR BIOS_DESC,0    ;pass 'nothing' number to bios descriptor
  608.     MOV    WORD PTR BIOS_DESC+2,0    ;fill remaining descriptor (DX) zero
  609.     MOV    DX,(Offset FUNC)    ;point to function parameter block
  610.     MOV    CL,50            ;direct bios call
  611.     INT    224            ;do it, to it...
  612. ;
  613.     OR    AL,AL            ;SET FLAGS
  614.     LAHF                ;SAVE ERROR FLAG
  615.     XCHG    AL,AH
  616.     PUSH    AX
  617.     MOV    BX,Word Ptr SECTOR    ;GET LOGICAL SECTOR #
  618.     LAHF                ;WE WANT TO INCREMENT TO NEXT
  619.     INC    BX
  620.     SAHF
  621.     XCHG    BX,DX            ;BUT FIRST...CHECK OVERFLOW
  622.     MOV    BX,Word Ptr SPT        ;  BY DOING (SECPERTRK-SECTOR)
  623.     SBB    BX,DX            ;DO THE SUBTRACTION
  624.     XCHG    BX,DX
  625.     JNB    NOOVF            ;JUMP IF NOT SECTOR>SECPERTRK
  626. ;
  627. ;Sector overflow...bump track number, reset sector
  628. ;
  629.     MOV    BX,Word Ptr TRACK
  630.     LAHF
  631.     INC    BX
  632.     SAHF
  633.     MOV    Word Ptr TRACK,BX
  634.     MOV    AL,'*'            ;TELL CONSOLE ANOTHER TRACK DONE
  635.     CALL    DISPLAY
  636.     CALL    STOP            ;SEE IF CONSOLE WANTS TO QUIT
  637.     MOV    BX,1            ;NEW SECTOR NUMBER ON NEXT TRACK
  638. ;
  639. NOOVF:    MOV    Word Ptr SECTOR,BX    ;PUT SECTOR AWAY
  640.     POP    AX            ;GET BACK ERROR FLAGS
  641.     XCHG    AL,AH
  642.     SAHF
  643.     POP    CX            ;RESTORE GROUP NUMBER
  644.     RET
  645. ;
  646. ;Convert logical sector # to physical
  647. ;
  648. LTOP:    MOV    BX,Word Ptr SECTBL    ;SET UP PARAMETERS
  649.     XCHG    BX,DX            ;  FOR CALL TO SECTRAN
  650.     MOV    BX,Word Ptr SECTOR
  651.     MOV    CX,BX
  652.     DEC    CX            ;ALWAYS CALL SECTRAN W/ZERO-REL SEC #
  653. ;
  654. SECT1:    CALL    SECTRN            ;DO THE SECTOR TRANSLATION
  655.     MOV    AL,Byte Ptr SPT+1    ;CHECK IF BIG TRACKS
  656.     OR    AL,AL            ;SET FLAGS (TRACKS > 256 SECTORS)
  657.     JNZ    LTOP1            ;NO SO SKIP
  658.     MOV    BH,AL            ;ZEROByte Ptr SPT+1    ;CHECK IF BIG TRACKS
  659.     OR    AL,AL            ;SET FLAGS (TRACKS > 256 SECTORS)
  660.     JNZ    LTOP1            ;NO SO SKIP
  661.     MOV    BH,AL            ;ZEROOV    BYTE PTR FUNC,16    ;bios SECTOR TRANSLATE function
  662.     MOV    WORD PTR BIOS_DESC,CX    ;pass sector to bios descriptor
  663.     MOV    WORD PTR BIOS_DESC+2,DX    ;translate table offset
  664.     MOV    DX,(Offset FUNC)    ;point to function parameter block
  665.     MOV    CL,50            ;direct bios call
  666.     INT    224            ;do it, to it...
  667.     ret                ;return from 'sectrn'
  668. ;
  669. ;Put bad block in bad block list
  670. ;
  671. SETBD:    PUSH    CX
  672.     CALL    SETBD1
  673.     DB    CR,LF,BEL,'Bad block: $'
  674. ;
  675. SETBD1:    POP    DX            ;RETRIEVE ARG
  676.     MOV    CL,9            ;bdos PRINT STRING
  677.     INT    224
  678.     POP    CX            ;GET BACK BLOCK NUMBER
  679.     MOV    AL,CH
  680.     CALL    HEXO            ;bdos PRINT IN HEX
  681.     MOV    AL,CL
  682.     CALL    HEXO
  683.     CALL    CRLF
  684.     MOV    BX,Word Ptr DMCNT    ;GET NUMBER OF SECTORS
  685.     MOV    AL,Byte Ptr BLM        ;GET BLOCK SHIFT VALUE
  686.     INC    AL            ;MAKES SECTOR/GROUP VALUE
  687.     MOV    DL,AL            ;WE WANT 16 BITS
  688.     MOV    DH,0
  689.     ADD    BX,DX            ;BUMP BY NUMBER IN THIS BLOCK
  690.     MOV    Word Ptr DMCNT,BX    ;UPDATE NUMBER OF SECTORS
  691.     MOV    BX,Word Ptr BADBKS    ;INCREMENT NUMBER OF BAD BLOCKS
  692.     INC    BX
  693.     MOV    Word Ptr BADBKS,BX
  694.     MOV    BX,Word Ptr DMPTR    ;GET POINTER INTO DM
  695.     MOV    M,CL            ;...AND PUT BAD BLOCK NUMBER
  696.     INC    BX            ;BUMP TO NEXT AVAILABLE EXTENT
  697.     MOV    AL,Byte Ptr DSM+1    ;CHECK IF 8 OR 16 BIT BLOCK SIZE
  698.     OR    AL,AL
  699.     JZ    SMGRP            ;JUMP IF 8 BIT BLOCKS
  700.     MOV    M,CH            ;ELSE STORE HI BYTE OF BLOCK #
  701.     LAHF                ;AND BUMP POINTER
  702.     INC    BX
  703.     SAHF
  704. ;
  705. SMGRP:    MOV    Word Ptr DMPTR,BX    ;SAVE DM POINTER, FOR NEXT TIME THROUGH HERE
  706.     RET                ;RETURN FROM "SETBD"
  707. ;
  708. ;Eliminate any previous [UNUSED].BAD entries
  709. ;
  710. ERAB:    MOV    DX,(Offset BFCB)    ;POINT TO BAD FCB
  711.     MOV    CL,19            ;bdos DELETE FILE function
  712.     INT    224
  713.     RET
  714. ;
  715. ;Create [UNUSED].BAD file entry
  716. ;
  717. OPENB:    MOV    DX,(Offset BFCB)    ;POINT TO BAD FCB
  718.     MOV    CL,22            ;bdos MAKE FILE function
  719.     INT    224
  720.     CMP    AL,0FFH            ;CHECK FOR OPEN ERROR
  721.     JZ    L_10    
  722.     RET                ;RETURN FROM "OPENB", IF NO ERROR
  723. L_10:
  724.     JMP    ERROR7            ;BAIL OUT...CAN'T CREATE [UNUSED].BAD
  725. ;
  726. CLOSEB:    XOR    AL,AL
  727.     MOV    AL,Byte Ptr BFCB+14    ;GET CP/M 'S2' BYTE
  728.     AND    AL,1FH            ;ZERO UPDATE FLAGS
  729.     MOV    Byte Ptr BFCB+14,AL    ;RESTORE IT TO OUR FCB
  730.     MOV    DX,(Offset BFCB)    ;FCB FOR [UNUSED].BAD
  731.     MOV    CL,16            ;bdos CLOSE FILE function
  732.     INT    224
  733.     RET                ;RETURN FROM "CLOSEB"
  734. ;
  735. ;Move bad area DM to BFCB
  736. ;
  737. SETDM:    MOV    BX,(Offset DM)        ;GET DM
  738.     MOV    Word Ptr DMPTR,BX    ;SAVE AS NEW POINTER
  739.     MOV    AL,Byte Ptr EXM        ;GET THE EXTENT SHIFT FACTOR
  740.     MOV    CL,0            ;INIT BIT COUNT
  741.     CALL    COLECT            ;GET SHIFT VALUE
  742.     MOV    BX,128            ;STARTING EXTENT SIZE
  743.     MOV    AL,CL            ;FIRST SEE IF ANY SHIFTS TO DO
  744.     OR    AL,AL
  745.     JZ    NOSHFT            ;JUMP IF NONE
  746. ;
  747. ESHFT:    SHL    BX,1            ;SHIFT
  748.     DEC    AL            ;BUMP
  749.     JNZ    ESHFT            ;LOOP
  750. ;
  751. NOSHFT:    PUSH    BX            ;SAVE THIS, IT IS RECORDS PER EXTENT
  752.     MOV    AL,Byte Ptr BSH        ;GET BLOCK SHIFT
  753.     MOV    CH,AL
  754. ;
  755. BSHFT:    CLC                ;CLEAR CARRY FLAG
  756.     RCR    BX,1            ;SHIFT RIGHT
  757.     DEC    CH
  758.     JNZ    BSHFT            ;TO GET BLOCKS PER EXTENT
  759.     MOV    AL,BL            ;IT'S IN [BL] (CAN'T BE >16)
  760.     MOV    Byte Ptr BLKEXT,AL    ;SET BLOCK EXTENT, WILL NEED THIS LATER
  761.     POP    BX            ;GET BACK REC/EXT
  762. ;
  763. SET1:    XCHG    BX,DX            ;NOW HAVE REC/EXTENT IN [DX]
  764.     MOV    BX,Word Ptr DMCNT    ;COUNT OF BAD SECTORS
  765. ;
  766. SETDMO:    PUSH    BX            ;SET FLAGS ON (DMCNT-BADCNT)
  767.     SBB    BX,DX            ;HAVE TO SUBTRACT FIRST
  768.     MOV    CX,BX            ;SAVE RESULT IN [CX]
  769.     POP    BX            ;THIS POP MAKES IT COMPARE ONLY
  770.     JB    SETDME            ;JUMP IF LESS THAN 1 EXTENT WORTH
  771.     MOV    AL,CH
  772.     OR    AL,CL            ;TEST IF SUBTRACT WAS 0
  773.     JZ    EVENEX            ;EXTENT IS EXACTLY FILLED (SPL CASE)
  774.     MOV    BX,CX            ;RESTORE RESULT TO [BX]
  775.     PUSH    BX            ;SAVE TOTAL
  776.     PUSH    DX            ;AND SECTORS/EXTENT
  777.     XCHG    BX,DX
  778.     CALL    SETDME            ;PUT AWAY ONE EXTENT
  779.     XCHG    BX,DX
  780.     MOV    Word Ptr DMPTR,BX    ;PUT BACK NEW DM POINTER
  781.     POP    DX            ;GET BACK SECTORS/EXTENT
  782.     POP    BX            ;AND COUNT OF BAD SECTORS
  783.     JMPS    SETDMO            ;AND LOOP
  784. ;
  785. ;Handle the special case of a file that ends on an extent
  786. ;boundary.  CP/M requires that such a file have a succeeding
  787. ;empty extent in order for the BDOS to properly access the file.
  788. ;
  789. EVENEX:    XCHG    BX,DX            ;FIRST SET EXTENT W/BAD BLOCKS
  790.     CALL    SETDME
  791.     XCHG    BX,DX
  792.     MOV    Word Ptr DMPTR,BX
  793.     MOV    BX,0            ;NOW SET ONE WITH NO DATA BLOCKS
  794. ;
  795. ;Fill in an extent's worth of bad sectors/block numbers.
  796. ;Also fill in the extent number in the FCB.
  797. ;
  798. SETDME:    PUSH    BX            ;SAVE RECORD COUNT
  799.     MOV    AL,Byte Ptr EXTNUM    ;UPDATE EXTENT BYTE
  800.     INC    AL
  801.     MOV    Byte Ptr EXTNUM,AL    ;SAVE FOR LATER
  802.     MOV    Byte Ptr BFCB+12,AL    ; AND PUT IN FCB
  803.     CALL    OPENB            ;OPEN THIS EXTENT
  804.     POP    BX            ;RETRIEVE REC COUNT
  805. ;
  806. ;Divide record count by 128 to get the number
  807. ;of l
  808.     OR    AL,BL            ;  OF NO RECORDS
  809.     JZ    SKIP
  810. ;
  811. DIVLOP:    ADD    BX,DX            ;SUBTRACT
  812.     INC    CH            ;BUMP QUOTIENT
  813.     JB    DIVLOP
  814.     MOV    DX,128            ; IN FCB
  815.     CALL    OPENB            ;OPEN THIS EXTENT
  816.     POP    BX            ;RETRIEVE REC COUNT
  817. ;
  818. ;Divide record count by 128 to get the number
  819. ;of lFIX UP OVERSHOOT
  820.     ADD    BX,DX
  821.     DEC    CH
  822.     MOV    AL,BH            ;TEST FOR WRAPAROUND
  823.     OR    AL,BL
  824.     JNZ    SKIP
  825.     MOV    BL,80H            ;RECORD LENGTH
  826.     DEC    CH
  827. ;
  828. SKIP:    MOV    AL,Byte Ptr EXTNUM    ;NOW FIX UP EXTENT NUM
  829.     ADD    AL,CH
  830.     MOV    Byte Ptr EXTNUM,AL
  831.     MOV    Byte Ptr BFCB+12,AL
  832.     MOV    AL,BL            ;MOD IS RECORD COUNT
  833.     MOV    Byte Ptr BFCB+15,AL    ;THAT GOES IN RC BYTE
  834. ;
  835. MOVDM:    MOV    AL,Byte Ptr BLKEXT    ;GET BLOCKS PER EXTENT
  836.     MOV    CH,AL            ;INTO B
  837. ;
  838. SETD1:    MOV    BX,Word Ptr DMPTR    ;POINT TO BAD ALLOCATION MAP
  839.     XCHG    BX,DX
  840.     MOV    BX,(Offset BFCB)+16    ;DISK ALLOC MAP IN FCB
  841. ;
  842. SETDML:    MOV    SI,DX
  843.     MOV    AL,[SI]
  844.     MOV    M,AL
  845.     INC    BX
  846.     INC    DX
  847. ;
  848. ;Now see if 16 bit groups...if so,
  849. ;we have to move another byte
  850. ;
  851.     MOV    AL,Byte Ptr DSM+1    ;THIS TELLS US
  852.     OR    AL,AL
  853.     JZ    BUMP1            ;IF ZERO, THEN NOT
  854.     MOV    SI,DX            ;IS 16 BITS, SO DO ANOTHER
  855.     MOV    AL,[SI]
  856.     MOV    M,AL
  857.     LAHF
  858.     INC    BX
  859.     SAHF
  860.     LAHF
  861.     INC    DX
  862.     SAHF
  863. ;
  864. BUMP1:    DEC    CH            ;COUNT DOWN
  865.     JNZ    SETDML
  866.     PUSH    DX
  867.     CALL    CLOSEB            ;CLOSE THIS EXTENT
  868.     POP    DX
  869.     RET
  870. ;
  871. ;Error messages
  872. ;
  873. SELERR:    MOV    DX,(Offset SELEMS)    ;SAY NO GO, AND BAIL OUT
  874.     JMP    PMSG
  875. ;
  876. SELEMS    DB    CR,LF,'Drive specifier out of range$'
  877. ;
  878. ERMSG5    DB    CR,LF,BEL,'+++ Warning...System tracks'
  879.     DB    ' bad +++',CR,LF,CR,LF,'$'
  880. ;
  881. ERROR6:    MOV    DX,(Offset ERMSG6)    ;OOPS...CLOBBERED DIRECTORY
  882.     JMP    PMSG
  883. ;
  884. ERMSG6    DB    CR,LF,BEL,'Bad directory area, try reformatting$'
  885. ;
  886. ERROR7:    MOV    DX,(Offset ERMSG7)    ;SAY NO GO, AND BAIL OUT
  887.     JMP    PMSG
  888. ;
  889. ERMSG7    DB    CR,LF,'Can''t create [UNUSED].BAD$'
  890. ;
  891. ;
  892. ;==== SUBROUTINES ====
  893. ;
  894. ;Decimal output routine
  895. ;
  896. DECOUT:    PUSH    CX
  897.     PUSH    DX
  898.     PUSH    BX
  899.     MOV    CX,-10
  900.     MOV    DX,-1
  901. ;
  902. DECOU2:    ADD    BX,CX
  903.     INC    DX
  904.     JB    DECOU2
  905.     MOV    CX,10
  906.     ADD    BX,CX
  907.     XCHG    BX,DX
  908.     MOV    AL,BH
  909.     OR    AL,BL
  910.     JZ    L_17    
  911.     CALL    DECOUT
  912. L_17:
  913.     MOV    AL,DL
  914.     ADD    AL,'0'
  915.     CALL    DISPLAY
  916.     POP    BX
  917.     POP    DX
  918.     POP    CX
  919.     RET
  920. ;
  921. ;Carriage-return/line-feed to console
  922. ;
  923. CRLF:    MOV    AL,CR
  924.     CALL    DISPLAY
  925.     MOV    AL,LF            ;FALL INTO 'DISPLAY'
  926. ;
  927. DISPLAY:PET
  928. ;
  929. ;Carriage-return/line-feed to console
  930. ;
  931. CRLF:    MOV    AL,CR
  932.     CALL    DISPLAY
  933.     MOV    AL,LF            ;FALL INTO 'DISPLAY'
  934. ;
  935. DISPLAY:P PRINT CHARACTER
  936.     POP    BX
  937.     POP    DX
  938.     POP    CX
  939.     RET
  940. ;
  941. ;Subroutine to test console for control-c abort
  942. ;
  943. STOP:    MOV    BYTE PTR FUNC,2        ;BIOS CONSOLE STATUS FUNCTION
  944.     MOV    WORD PTR BIOS_DESC,0    ;PASS 'NOTHING' NUMBER TO BIOS DESCRIPTOR
  945.     MOV    WORD PTR BIOS_DESC+2,0    ;FILL REMAINING DESCRIPTOR (DX) ZERO
  946.     MOV    DX,(OFFSET FUNC)    ;POINT TO FUNCTION PARAMETER BLOCK
  947.     MOV    CL,50            ;direct bios call
  948.     INT    224            ;do it, to it...
  949. ;
  950.     OR    AL,AL            ;TEST FLAGS ON ZERO
  951.     JNZ    L_18    
  952.     RET                ;RETURN IF NO CHAR
  953. L_18:    MOV    BYTE PTR FUNC,3        ;BIOS READ CONSOLE CHARACTER FUNCTION
  954.     MOV    WORD PTR BIOS_DESC,0    ;PASS 'NOTHING' NUMBER TO BIOS DESCRIPTOR
  955.     MOV    WORD PTR BIOS_DESC+2,0    ;FILL REMAINING DESCRIPTOR (DX) ZERO
  956.     MOV    DX,(OFFSET FUNC)    ;POINT TO FUNCTION PARAMETER BLOCK
  957.     MOV    CL,50            ;direct bios call
  958.     INT    224            ;do it, to it...
  959. ;
  960.     CMP    AL,'C'-40H        ;IS IT CONTROL-C?
  961.     JZ    L_19    
  962.     RET                ;RETURN IF NOT
  963. L_19:
  964.     MOV    DX,(Offset ABORTM)    ;EXIT WITH MESSAGE
  965.     JMP    PMSG
  966. ;
  967. ABORTM    DB    CR,LF
  968.     DB    'Test aborted by control-C'
  969.     DB    CR,LF,'$'
  970. ;
  971. ;Move from [BX] to [DX], Count in [CX]
  972. ;
  973. MOVE:    MOV    AL,ES: M
  974.     MOV    SI,DX
  975.     MOV    [SI],AL
  976.     LAHF
  977.     INC    BX
  978.     SAHF
  979.     LAHF
  980.     INC    DX
  981.     SAHF
  982.     DEC    CH
  983.     JNZ    MOVE
  984.     RET
  985. ;
  986. ;Print byte in accumulator in hex
  987. ;
  988. HEXO:    LAHF                ;SAVE FOR SECOND HALF
  989.     XCHG    AL,AH
  990.     PUSH    AX
  991.     XCHG    AL,AH
  992.     ROR    AL,1            ;MOVE INTO POSITION
  993.     ROR    AL,1
  994.     ROR    AL,1
  995.     ROR    AL,1
  996.     CALL    NYBBLE            ;bdos PRINT MS NYBBLE
  997.     POP    AX
  998.     XCHG    AL,AH
  999. ;
  1000. NYBBLE:    AND    AL,0FH            ;LO NYBBLE ONLY
  1001.     ADD    AL,90H
  1002.     DAA
  1003.     ADC    AL,40H
  1004.     DAA
  1005.     JMP    DISPLAY            ;bdos PRINT IN HEX
  1006. ;
  1007. ;Subroutine to determine the number
  1008. ;of groups reserved for the directory
  1009. ;
  1010. GETDIR:    MOV    CL,0            ;INIT BIT COUNT
  1011.     MOV    AL,Byte Ptr AL0        ;READ DIR GRP BITS
  1012.     CALL    COLECT            ;COLLECT COUNT OF DIR GRPS..
  1013.     MOV    AL,Byte Ptr AL1        ;..IN REGISTER [CL]
  1014.     CALL    COLECT
  1015.     MOV    BL,CL
  1016.     MOV    BH,0            ;[CX] NOW HAS A DEFAULT START GRP #
  1017.     MOV    Word Ptr DIRBKS,BX    ;SAVE FOR LATER
  1018.     RET
  1019. ;
  1020. ;Collect the number of '1' bits in A as a count in [CL]
  1021. ;
  1022. COLECT:    MOV    CH,8
  1023. ;
  1024. COLOP:    RCL    AL,1
  1025.     JNB    COSKIP
  1026.     INC    CL
  1027. ;
  1028. COSKIP:    DEC    CH
  1029.     JNZ    COLOP
  1030.     RET
  1031. ;
  1032. ;Routine to fill in disk parameters
  1033. ;
  1034. LOGIT:    MOV    DX,(Offset DPB)        ;   THEN MOVE TO LOCAL
  1035.     MOV    CH,(Offset DPBLEN)    ;  WORKSPACE
  1036.     CALL    MOVE
  1037.     RET
  1038. ;
  1039. L_22    EQU    $
  1040.     dseg    $
  1041.     ORG    Offset L_22
  1042. ;
  1043. ;--------------------------------------------------
  1044. ;The disk parameter block is moved here from CP/M-86
  1045. ;
  1046. DPB    EQU    (Offset $)        ;DISK PARAMETER BLOCK (COPY)
  1047. ;
  1048. SPT    RS    2            ;SECTORS PER TRACK
  1049. BSH    RS    1            ;BLOCK SHIFT
  1050. BLM    RS    1            ;BLOCK MASK
  1051. EXM    RS    1            ;EXTENT MASK
  1052. DSM    RS    2            ;MAXIMUM BLOCK NUMBER
  1053. DRM    RS    2            ;MAXIMUM DIRECTORY BLOCK NUMBER
  1054. AL0    RS    1            ;DIRECTORY ALLOCATION VECTOR
  1055. AL1    RS    1            ;DIRECTORY ALLOCATION VECTOR
  1056. CKS    RS    2            ;CHECKED DIRECTORY ENTRIES
  1057. SYSTRK    RS    2            ;SYSTEM TRACKS
  1058. ;
  1059. ;End of disk parameter block
  1060. ;
  1061. DPBLEN    EQU    (Offset $)-(Offset DPB)    ;LENGTH OF DISK PARM BLOCK
  1062. ;
  1063. ;--------------------------------------------------
  1064. BLKEXT    DB    0            ;BLOCKS PER EXTENT
  1065. DIRBKS    DW    0            ;CALCULATED # OF DIR BLOCKS
  1066. ;
  1067. BFCB    DB    0,'[UNUSED]BAD',0,0,0,0
  1068. FCBDM    RS    17
  1069. ;
  1070. NOMSG    DB    'No$'
  1071. ENDMSG    DB    ' bad blocks found',CR,LF,'$'
  1072. ;
  1073. BADBKS    DW    0            ;COUNT OF BAD BLOCKS
  1074. SECTOR    DW    0            ;CURRENT SECTOR NUMBER
  1075. TRACK    DW    0            ;CURRENT TRACK NUMBER
  1076. PHYSEC    DW    0            ;CURRENT PHYSICAL SECTOR NUMBER
  1077. SECTBL    DW    0            ;SECTOR SKEW TABLE POINTER
  1078. ;
  1079. EXTNUM    DB    0FFH            ;USED FOR UPDATING EXTENT NUMBER
  1080. DMCNT    DW    0            ;NUMBER OF BAD SECTORS
  1081. DMPTR    DW    (Offset DM)        ;POINTER TO NEXT BLOCK ID
  1082. ;
  1083. ;storage for 5 byte BIOS function descriptor
  1084. ;
  1085. FUNC        RS    1        ;bios function code goes here
  1086. BIOS_DESC    RS    2        ;[CX] data goes here
  1087.         RS    2        ;[DX] data goes here
  1088. ;
  1089. DM    EQU    (Offset $)        ;BAD BLOCK ALLOCATION MAP
  1090. ;
  1091.     DW    0            ;RESERVE 2 BYTES
  1092.     DW    0            ;RESERVE 2 BYTES
  1093.     DW    0            ;RESERVE 2 BYTES
  1094.     DW    0            ;RESERVE 2 BYTES
  1095.     DW    0            ;RESERVE 2 BYTES
  1096.     DW    0            ;RESERVE 2 BYTES
  1097.     DW    0            ;RESERVE 2 BYTES
  1098.     DW    0            ;RESERVE 2 BYTES
  1099.     DW    0            ;RESERVE 2 BYTES
  1100.     DW    0            ;RESERVE 2 BYTES
  1101.     DW    0            ;RESERVE 2 BYTES
  1102.     DW    0            ;RESERVE 2 BYTES
  1103.     DW    0            ;RESERVE 2 BYTES
  1104.     DW    0            ;RESERVE 2 BYTES
  1105.     DW    0            ;RESERVE 2 BYTES
  1106.     DW    0            ;RESERVE 2 BYTES
  1107.     DW    0            ;RESERVE 2 BYTES
  1108.     DW    0            ;RESERVE 2 BYTES
  1109.     DW    0            ;RESERVE 2 BYTES
  1110.     DW    0            ;RESERVE 2 BYTES
  1111.     DW    0            ;RESERVE 2 BYTES
  1112.     DW    0            ;RESERVE 2 BYTES
  1113.     DW    0            ;RESERVE 2 BYTES
  1114.     DW    0            ;RESERVE 2 BYTES
  1115.     DW    0            ;RESERVE 2 BYTES
  1116.     DW    0            ;RESERVE 2 BYTES
  1117.     DW    0            ;RESERVE 2 2 BYTES
  1118.     DW    0            ;RESERVE 2 BYTES
  1119.     DW    0            ;RESERVE 2 BYTES
  1120.     DW    0            ;RESERVE 2 BYTES
  1121.     DW    0            ;RESERVE 2 BYTES
  1122.     DW    0            ;RESERVTES
  1123.     DW    0            ;RESERVE 2 BYTES
  1124.     DW    0            ;RESERVE 2 BYTES
  1125.     DW    0            ;RESERVE 2 BYTES
  1126.     DW    0            ;RESERVE 2 BYTES
  1127.     DW    0            ;RESERVE 2 E 2 BYTES
  1128.     DW    0            ;RESERVE 2 BYTES
  1129.     DW    0            ;RESERVE 2 BYTES
  1130.     DW    0            ;RESERVE 2 BYTES
  1131.     DW    0            ;RESERVE 2 BYTES
  1132.     DW    0            ;RESERVE 2 BYTES
  1133.     DW    0            ;RESERVE 2 BYTES
  1134.     DW    0            ;RESERVE 2 BYTES
  1135.     DW    0            ;RESERVE 2 BYTES
  1136.     DW    0            ;RESERVE 2 BYTES
  1137.     DW    0            ;RESERVE 2 BYTES
  1138.     DW    0            ;RESERVE 2 BYTES
  1139.     DW    0            ;RESERVE 2 BYTES
  1140.     DW    0            ;RESERVE 2 BYTES
  1141.     DW    0            ;RESERVE 2 BYTES
  1142.     DW    0            ;RESERVE 2 BYTES
  1143.     DW    0            ;RESERVE 2 BYTES
  1144.     DW    0            ;RESERVE 2 BYTES
  1145.     DW    0            ;RESERVE 2 BYTES
  1146.     DW    0            ;RESERVE 2 BYTES
  1147.     DW    0            ;RESERVE 2 BYTES
  1148.     DW    0            ;RESERVE 2 BYTES
  1149.     DW    0            ;RESERVE 2 BYTES
  1150.     DW    0            ;RESERVE 2 BYTES
  1151.     DW    0            ;RESERVE 2 BYTES
  1152.     DW    0            ;RESERVE 2 BYTES
  1153. ;
  1154.     END
  1155.