home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / ENTERPRS / CPM / UTILS / F / FBAD60A.LZH / FBAD60A.ASM < prev    next >
Assembly Source File  |  1991-02-14  |  45KB  |  1,735 lines

  1. ;      FBAD.ASM ver. 60A
  2. ;     (revised 10/18/87)
  3. ;     NON-DESTRUCTIVE DISK TEST PROGRAM
  4. ;
  5. ; FBAD will find all bad blocks on a disk and build a file [UNUSED].BAD
  6. ; to allocate them, thus "locking out" the bad blocks so CP/M will not
  7. ; use them.  This allows continued use of the disk as though it had no
  8. ; bad areas.
  9. ;
  10. ; If an [UNUSED].BAD file is found on the disk before the test you will
  11. ; be prompted to keep the existing file (and all currently flagged bad
  12. ; blocks) or erase it and only flag the bad blocks found on the current
  13. ; pass.
  14. ;
  15. ; Originally written by Gene Cotton, published in "Interface Age" for
  16. ; September 1980, page 80.
  17. ;
  18. ; See notes below concerning 'TEST' conditional assembly option, SYSTST
  19. ; and BADUSR directives.
  20. ;
  21. ;=======================================================================
  22. ;       current update
  23. ;
  24. ; 10/18/87 Modified to allow TEST routines to be left in. If TEST is
  25. ;   v60A   YES the number of records read will be reported, but only if
  26. ;    no bad blocks were found.  Added help message - A>FBAD $?
  27. ;    Added S, A and X command line options.  See help message for
  28. ;    proper usage.  Revised handling of system tracks.  Modified
  29. ;    Modified LTOP routine so that system track sectors do not go
  30. ;    through sector translation. Reading is slow but system sec-
  31. ;    tors may not use same XLATE as data sectors.
  32. ;     - Tom Head
  33. ;
  34. ;=======================================================================
  35. ;
  36. ; COMMAND LINE OPTIONS:
  37. ;
  38. ; Three command line options can now be specified -- S, A and X.
  39. ;
  40. ; If the ASTRS equate is set to NO the TRACK-Nr will be displayed
  41. ; during testing unless the A option is specified in which case
  42. ; an * will be printed for each track read.
  43. ;
  44. ; If the SYSTST equate is set to NO the system track test will be
  45. ; skipped unless the S option is specified in which case system
  46. ; track 0 will be read using the sector count value in the SPT0
  47. ; equate. All other system tracks will be read using the sector
  48. ; count value in the OSTK equate. If SPT0 or OSTK is set to zero
  49. ; then the SPT value for the data tracks will be used.
  50. ;
  51. ; This should permit testing of system tracks where track 0 has
  52. ; a different sector count than other system tracks which, in turn,
  53. ; may have a different sector count than the data tracks. (e.g. JADE DD)
  54. ;
  55. ; The X option is identical to the S option but allows using a second
  56. ; set of default values for SPT0 and/or OSTK (XSPT0 and XOSTK) in case
  57. ; you routinely use two different disk formats.
  58. ;
  59. ; NOTE THAT YOU SHOULD NOT USE THE  X  AND  S  OPTIONS TOGETHER.
  60. ;
  61. ; Finally, for the odd case, you may specify one or two numbers
  62. ; following the S option ( Sn0 or Sn0,n1 ). This will override
  63. ; the default sector count values set either for SPT0 alone or
  64. ; for both SPT0 and OSTK.
  65. ;
  66. ; SYSTST, BADUSR and ASTRS options:
  67. ;
  68. ; Many double-density disk systems have single-density system tracks.
  69. ; If this is true with your system, you can change the program to skip
  70. ; the system tracks, without re-assembling it. To do this, set the byte
  71. ; at at 103H to 0 if you don't want the system tracks tested, otherwise
  72. ; keep it 1.  The program tests if you have a "blocked" disk system,
  73. ; that is, when the same physical disk is separated into logical disks
  74. ; by use of the SYSTRK word in the disk parameter block.  If more than 5
  75. ; tracks are specified, the program skips the system tracks.
  76. ;
  77. ; If you are using CP/M 2.x , you may assign the user number where the
  78. ; [UNUSED.BAD] file will be created, by changing the byte at 104H to the
  79. ; desired user number.
  80. ;
  81. ; FBAD displays the TRACK-Nr it has checked on the screen-terminal.  If
  82. ; you like to log the results on a printer (or you have a hardcopy term-
  83. ; inal, you may want to change LOC 105H to a non-zero value, and FBAD
  84. ; will display a * for each track checked.  The number in 105H controls
  85. ; the number of *'s per line.  (Note patch values are HEX: 76=4CH.)  Use
  86. ; ^P to turn the printer on before running FBAD, it will be automatically
  87. ; turned off by the warm boot at the end.
  88. ;
  89. ; NOTE:  These changes can be done with DDT as follows:
  90. ;
  91. ;  A>DDT FBAD.COM
  92. ;  -S103
  93. ;  103 01 00 ; Don't test SYSTEM tracks
  94. ;  104 FF 0F ; Put [UNUSED.BAD] in USER 15
  95. ;  105 00 4C ; Issue CR/LF after 76 *'s
  96. ;  106 31 . ; Finished with changes
  97. ;  -^C
  98. ;
  99. ;  A>SAVE 12 FBAD.COM
  100. ;
  101. ;=======================================================================
  102. ;
  103. ;      USING THE PROGRAM
  104. ;
  105. ; Before using this program to "reclaim" a diskette, the diskette should
  106. ; be reformatted.  If this is not possible, at least assure yourself
  107. ; that any existing files on the diskette do not contain unreadable re-
  108. ; cords.  If you have changed diskettes since the last warm-boot, you
  109. ; must warm boot again before running this program.
  110. ;
  111. ; To use the program, insert both the disk containing FBAD.COM and the
  112. ; diskette to be checked into the disk drives. The diskette containing
  113. ; FBAD.COM can be the one that is to be checked.  Assume that the pro-
  114. ; gram is on drive "A" and the suspected bad disk is on drive "B".  In
  115. ; response to the CP/M prompt "A>", type in FBAD B: This will load the
  116. ; file FBAD.COM from drive "A" and test the diskette on drive "B" for
  117. ; unreadable records. If no drive is specified, the currently logged-in
  118. ; drive is assumed to contain the diskette to check.
  119. ;
  120. ; The program first checks the CP/M System tracks (up to 5), and any
  121. ; errors here prohibit the diskette from being used on drive "A", since
  122. ; all "warm boots" occur using the system tracks from the "A" drive.
  123. ; Floppy diskettes normally use 2 tracks for the system; Winchester hard
  124. ; disks may use one or more.
  125. ;
  126. ; Version  5.5 and  later  automatically skip the system check if 5 or
  127. ; more tracks are reserved for the system.  This allows the program to
  128. ; be used on BOTH floppy and Winchester systems without patching.
  129. ;
  130. ; The  program next checks the first two data blocks containing the
  131. ; diskette directory.  If errors occur here, the program terminates with
  132. ; the control returning to CP/M.  No other data blockes are checked as
  133. ; errors in the directory render the diskette useless.
  134. ;
  135. ; Finally, all the remaining data blocks are checked.  Any records that
  136. ; are unreadable cause the data block which contains them to be stored
  137. ; temporarily as a "bad block".  At the end of this phase, the message
  138. ; "nn bad blocks found" is displayed (where nn is replaced by the number
  139. ; of bad blocks, or "No" if no read errors occur).  If bad blocks occur,
  140. ; the filename [UNUSED].BAD is created, the list of "bad blocks" is put
  141. ; in the allocation map of the directory entry for [UNUSED].BAD, and the
  142. ; file is closed.  When the number of "bad blocks" exceeds 16, the pro-
  143. ; gram will open additional extents as required to hold the overflow.
  144. ; If the diskette has more than 32 "bad blocks", perhaps it should be
  145. ; sent to the "big disk drive in the sky" for the rest it deserves.
  146. ;
  147. ; If any "bad blocks" do occur, they are allocated to [UNUSED].BAD and
  148. ; no longer will be available to CP/M for future allocation.  This ef-
  149. ; fectively locks out bad records on the diskette allowing its continued
  150. ; use.
  151. ;
  152. ;    Using the TEST conditional assembly
  153. ;
  154. ; A conditional assembly has been added to allow testing this program to
  155. ; make sure it is reading all records on your disk that are accessible
  156. ; to CP/M.  The program reads the disk on a block-by-block basis, so it
  157. ; is necessary to first determine the number of blocks present.  To com-
  158. ; mence, we must know the number of records/block (8 records/block for
  159. ; standard IBM single density format). If this value is not known, it
  160. ; can be easily found by saving one page in a test file and interrogating
  161. ; using the STAT command:
  162. ;
  163. ; A>SAVE 1 TEST.SIZ
  164. ; A>STAT TEST.SIZ
  165. ;
  166. ; For standard single-density, STAT will report this file as being 1k.
  167. ; The file size reported (in bytes) is the size of a block.  This value
  168. ; divided by 128 bytes/record (the standard CP/M record size) will give
  169. ; records/block.  For our IBM single density example, we have:
  170. ;
  171. ;      (1024 bytes/block) / (128 bytes/record) = 8 records/block.
  172. ;
  173. ; We can now calculate blocks/track (assuming we know the number of
  174. ; records per track.  In our example:
  175. ;
  176. ;      (26 records/track) / (8 records/block) = 3.25 blocks/track
  177. ;
  178. ; Now armed with the total number of data tracks (75 in our IBM single
  179. ; density example), we get total blocks accessible:
  180. ;
  181. ;      75 (tracks/disk) x (3.25 blocks/track) = 243.75 blocks/disk
  182. ;
  183. ; CP/M cannot access a fractional block, so we round down (to 243 blocks
  184. ; in our example).  Now multiplying total blocks by records per block
  185. ; results in total records as should be reported when TEST is set YES
  186. ; and a good disk is read.  For our example, this value is 1944 records.
  187. ;
  188. ; Finally, note that if SYSTST is set YES, the records present on the
  189. ; system tracks will be displayed as well.  In the previous example,
  190. ; this results in 1944 + 52 = 1996 records (reported separately by the
  191. ; TEST conditional).  Version 5.4 reported these as a single total at
  192. ; the end.
  193. ;
  194. ; Run the program on a KNOWN-GOOD diskette.  It should report that it
  195. ; has read the correct number of records.
  196. ; We should not display the number of records read if bad blocks are
  197. ; found since this program does not read all the records in a
  198. ; block that is found to be bad and thus will report an inaccurate
  199. ; number of records read.
  200. ;
  201. ;=======================================================================
  202. ;       prior updates
  203. ;
  204. ; 03/28/85 Cleaned up some code that sends messages.
  205. ;   v60    Modified STOP routine so that you really can abort the
  206. ;    program with ^C.  - Dave Mabry
  207. ;
  208. ; 03/16/85 Program will ask you if you want to continue checking on the
  209. ;   v59    same drive as originally entered - added a byte at the end of
  210. ;    the code (ORIGDR) to store the value from the FCB.
  211. ;     - Ken Kaplan
  212. ;
  213. ; 12/12/84  Added the ability to keep bad blocks that were flagged in a
  214. ;   v58     previous [UNUSED].BAD file.  If a block was ever flagged as
  215. ;     bad by this program, it is probably weak.  If on a subse-
  216. ;     quent test, it makes it through the BIOS retires and is read
  217. ;     successfully, I want the block to stay in the [UNUSED].BAD
  218. ;     file.  Removed the coded in LTOP which cleared the high byte
  219. ;     of HL after a call to RECTRN.  My BIOS (Morrow DJDMA) sets
  220. ;     the high bit of HL to indicated side 1 of a double-sided
  221. ;     drive.   - Ron Schwabel
  222. ;
  223. ; 11/29/84  Integrated Mike Webbs idea to display Track-Nr. Changed DOC
  224. ;   v57     up front accordingly. - William Earnest
  225. ;
  226. ; 07/04/84  Added Ted Shapin's fixes from 1981 that were not included in
  227. ;   v56     the 06/07/84 version.  Reformatted.
  228. ;     - Irv Hoff
  229. ;
  230. ; 06/07/84  Added code at CHKSYS to skip system tracks if more than 5
  231. ;   v55     are present (most systems use 1 or at most 2 tracks for the
  232. ;     system).  This makes the program practical for both floppy
  233. ;     and Winchester systems.  Cosmetic change for printer logging
  234. ;     to add CR/LF after 76 *'s. Fixed problem in DECOUT to give
  235. ;     correct total for max size Winchester disks.
  236. ;     - Dave Hardy
  237. ;
  238. ;=======================================================================
  239. ;
  240. NO EQU 0
  241. YES EQU NOT NO
  242. ;
  243. ;=======================================================================
  244. ;
  245. ; Conditional assembly switch for testing this program
  246. ;
  247. ;
  248. TEST EQU YES  ; Yes to desplay records read if good disk
  249. ;
  250. ;=======================================================================
  251. ;
  252. ; System equates
  253. ;
  254. WBOOT EQU 0  ; CP/M warm boot entry
  255. BDOS EQU 0005H  ; BDOS entry point
  256. FCB EQU 005CH  ; CP/M default FCB location
  257. ;
  258. TBUFF EQU 0080H  ; Command line copied here
  259. SPCHR EQU '$'  ; Leadin for options
  260. ;
  261. ;
  262. ; Define ASCII characters used
  263. ;
  264. BELL EQU 07H
  265. CR EQU 0DH  ; Carriage return character
  266. LF EQU 0AH  ; Line feed character
  267. TAB EQU 09H  ; Tab character
  268. VERS EQU 60  ; Version number
  269. ;
  270. DPBOFF EQU 3AH  ; Cp/m 1.4 offset to DPB within BDOS
  271. TRNOFF EQU 15  ; Cp/m 1.4 offset to record Xlate routine
  272. ;
  273. ;
  274.  ORG 0100H
  275. ;
  276. ;
  277.  JMP START  ; Jmp around option bytes
  278. ;
  279. ;
  280. ; If you want the system tracks tested, then put a 1 here, otherwise 0.
  281. ;
  282. SYSTST: DB 0
  283. ;
  284. ;
  285. ; If using CP/M 2.x change this byte to the user number you want
  286. ; [UNUSED].BAD to reside in.
  287. ;
  288. BADUSR: DB 15  ; User # where [UNUSED.BAD] goes
  289. ;
  290. ; Set this byte to the number of *'s you want per display line
  291. ;
  292. ASTRS: DB 00  ; Number of *'s per line (0 == 'track xx')
  293. ;
  294. ;
  295. ; Enter default SPT value for system track 0   (26 for 8" SS/SD)
  296. ;
  297. DFSPT0 EQU 26  ; If 00, use data track value
  298. ;
  299. ; Enter default SPT value for other system track(S)
  300. ;
  301. DFOSTK EQU 00  ; If 00, use data track value
  302. ;
  303. SPT0: DW DFSPT0
  304. OSTK: DW DFOSTK
  305. ;
  306. ;
  307. ; The following two equates are for use in conjunction with the 'X' com-
  308. ; mand line option which will initialize SPT0 and OSTK to the values
  309. ; entered.
  310. ;
  311. XSPT0 EQU 26  ; Special value for system track 0 SPT
  312. XOSTK EQU 50  ; Special value for other system track(s)
  313. ;    ; 50 for Jade double D controller
  314. ;
  315. ;=======================================================================
  316. ;
  317. ;         PROGRAM STARTS HERE
  318. ;
  319. ;=======================================================================
  320. ;
  321. START: LXI SP,STACK ; Make new stack
  322.  LXI D,SIGNON ; Introduce ourself
  323.  CALL PSTRNG
  324.  CALL GETOPT  ; Check command line options
  325. ;
  326. RESTRT: CALL SETUP  ; Set BIOS entry, and check drive
  327.  CALL ZMEM  ; Zero all available memory
  328.  CALL FINDB  ; Establish all bad blocks
  329.  JZ NOBAD  ; Say no bad blocks, if so
  330.  CALL SETDM  ; Fix DM bytes in FCB
  331. ;
  332. NOBAD: CALL CRLF
  333.  MVI A,TAB
  334.  CALL TYPE
  335.  LXI D,NOMSG  ; Point first to 'no'
  336.  LHLD BADBKS  ; Pick up # bad blocks
  337.  MOV A,H  ; Check for zero
  338.  ORA L
  339.  JZ PMSG1  ; Jump if none
  340.  LXI D,BELLS
  341.  CALL PSTRNG
  342.  LHLD BADBKS
  343.  PUSH H
  344.  LXI H,BADBKS
  345.  CALL DECOUT  ; Oops..had some bad ones, report
  346.  POP H
  347.  SHLD BADBKS
  348.  LXI D,BELLS
  349.  CALL PSTRNG
  350.  JMP PMSG2
  351. ;
  352. PMSG1: CALL PSTRNG
  353. ;
  354. PMSG2: LXI D,ENDMSG ; Rest of exit message
  355. ;
  356. PMSG: CALL PSTRNG
  357. ;
  358.   IF TEST
  359.  LHLD BADBKS
  360.  MOV A,H
  361.  ORA L
  362.  JNZ SKPT1
  363.  MVI A,TAB  ; Get a tab
  364.  CALL TYPE  ; Print it
  365.  LXI H,RECCNT ; Dir+data block count
  366.  CALL DECOUT  ; Print it
  367.  LXI D,RECMSG ; Point to message
  368.  CALL PSTRNG  ;
  369.   ENDIF   ; TEST
  370. ;
  371. SKPT1: LXI D,CMSG1  ; Do you want to continue?
  372.  CALL PSTRNG
  373.  MVI C,1  ; BDOS read console function
  374.  CALL BDOS
  375.  ANI 5FH  ; Convert lower to upper case
  376.  CPI 'C'  ; If C or c entered continue
  377.  JNZ WBOOT  ; Exit
  378.  MVI C,0DH  ; Do a disk reset to avoid BDOS R/O errors
  379.  CALL BDOS
  380.  LDA ORIGDR  ; Get drive value
  381.  STA FCB  ; Put it back in FCB
  382.  LXI SP,STACK
  383.  JMP RESTRT  ; Start over
  384. ;
  385. ;
  386. ; Get actual address of BIOS routines
  387. ;
  388. ; WARNING...Program modification takes place here...do not change.
  389. ;
  390. SETUP: LHLD 1  ; Get pointer to warm boot
  391.  LXI D,24  ; Offset to 'SETDSK'
  392.  DAD D
  393.  SHLD SETDSK+1 ; Fix our call address
  394.  LXI D,3  ; Offset to 'SETTRK'
  395.  DAD D
  396.  SHLD SETTRK+1 ; Fix our call address
  397.  LXI D,3  ; Offset to 'SETREC'
  398.  DAD D
  399.  SHLD SETREC+1 ; Fix our call address
  400.  LXI D,6  ; Offset to 'DREAD'
  401.  DAD D
  402.  SHLD DREAD+1  ; Fix our call address
  403.  LXI D,9  ; Offset to CP/M 2.x RECTRAN
  404.  DAD D
  405.  SHLD RECTRN+1 ; Fix our call address
  406.  MVI C,12  ; Get version function
  407.  CALL BDOS
  408.  MOV A,H  ; Save as flag
  409.  ORA L
  410.  STA VER2FL
  411.  JNZ GDRIV  ; Skip 1.4 stuff if is 2.x
  412.  LXI D,TRNOFF ; Cp/m 1.4 offset to RECTRAN
  413.  LHLD BDOS+1  ; Set up jump to 1.4 RECTRAN
  414.  MVI L,0
  415.  DAD D
  416.  SHLD RECTRN+1
  417. ;
  418. ;
  419. ; Check for drive specification
  420. ;
  421. GDRIV: LDA FCB  ; Get drive name
  422.  STA ORIGDR  ; Store for use later
  423.  MOV C,A
  424.  ORA A  ; Zero?
  425.  JNZ GD2  ; If not,then go specify drive
  426.  MVI C,25  ; Get logged-in drive
  427.  CALL BDOS
  428.  INR A  ; Make 1-relative
  429.  STA ORIGDR
  430.  MOV C,A
  431. ;
  432. GD2: LDA VER2FL  ; If CP/M version 2.x
  433.  ORA A
  434.  JNZ GD3  ; Seldsk will return select error
  435. ;
  436. ;
  437. ; Is CP/M 1.4, which doesn't return a select error, so we have to do it
  438. ; here
  439. ;
  440.  MOV A,C
  441.  CPI 4+1  ; Check for highest drive number
  442.  JNC SELERR  ; Select error
  443. ;
  444. GD3: DCR C  ; Back off for CP/M
  445.  PUSH B  ; Save disk selection
  446.  MOV E,C  ; Align for BDOS
  447.  MVI C,14  ; Select disk function
  448.  CALL BDOS
  449.  POP B  ; Get back disk number
  450. ;
  451. ;
  452. ; EXPLANATION: Why we do the same thing twice
  453. ;
  454. ; You might notice that we are doing the disk selection twice, once by a
  455. ; BDOS call and once by direct BIOS call.  The BIOS call is necessary in
  456. ; order to get the necessary pointer back from CP/M (2.x) to find the
  457. ; record translate table.  The BDOS call is necessary to keep CP/M in
  458. ; step with the  BIOS. Later the file [UNUSED].BAD may need to be
  459. ; created and CP/M must know which drive is being used.
  460. ;
  461.  CALL SETDSK  ; Direct BIOS call
  462.  LDA VER2FL
  463.  ORA A
  464.  JZ DOLOG  ; Jump if CP/M 1.4
  465.  MOV A,H
  466.  ORA L  ; Check for 2.x
  467.  JZ SELERR  ; Jump if select error
  468.  MOV E,M  ; Get record table pointer
  469.  INX H
  470.  MOV D,M
  471.  INX H
  472.  XCHG
  473.  SHLD RECTBL  ; Store it away
  474.  LXI H,8  ; Offset to DPB pointer
  475.  DAD D
  476.  MOV A,M  ; Pick up DPB pointer
  477.  INX H  ; To use
  478.  MOV H,M  ; As parameter
  479.  MOV L,A  ; To logit
  480. ;
  481. DOLOG: CALL LOGIT  ; Log in drive, get disk parms
  482.  CALL GETDIR  ; Calculate directory information
  483. ;
  484. ;
  485. ; Now set the required user number
  486. ;
  487.  LDA VER2FL
  488.  ORA A
  489.  RZ   ; No users in CP/M 1.4
  490.  LDA BADUSR  ; Get the user number
  491.  MOV E,A  ; BDOS call needs user # in 'E'
  492.  MVI C,32  ; Get/set user code
  493.  CALL BDOS
  494.  RET
  495. ;.....
  496. ;
  497. ;
  498. ; Look for bad blocks
  499. ;
  500. FINDB: LHLD SPT
  501.  SHLD SPTSAV  ; Save data track SPT
  502.  LDA SYSTST
  503.  ORA A
  504.  JZ DODIR  ; Jump if no system tracks to be tested
  505.  CALL CHKSYS  ; Check for bad blocks on track 0 and 1
  506. ;
  507. DODIR: XRA A
  508.  STA RECCNT
  509.  LXI H,0000
  510.  SHLD RECCNT+1 ; Initialize records read to zero
  511.  LHLD SPTSAV
  512.  SHLD SPT  ; Restore data track SPT
  513.  CALL CHKDIR  ; Check for bad blocks in directory
  514.  LXI D,TDAMSG ; Testing data area message
  515.  CALL PSTRNG
  516.  LDA ASTRS  ; Set column count
  517.  STA COLUMN
  518.  CALL ERAB  ; Erase any [UNUSED].BAD file
  519.  LHLD DIRBKS  ; Start at first data block
  520.  MOV B,H  ; Put into 'BC'
  521.  MOV C,L
  522. ;
  523. FINDBA: CALL READB  ; Read the block
  524.  CNZ SETBD  ; If bad, add block to list
  525.  INX B  ; Bump to next block
  526.  LHLD DSM
  527.  MOV D,B  ; Set up for (MAXGRP - CURGRP)
  528.  MOV E,C
  529.  CALL SUBDE  ; Do subtract: (MAXGRP - CURGRP)
  530.  JNC FINDBA  ; Until CURGRP > MAXGRP
  531.  CALL CRLF
  532.  LHLD DMCNT  ; Get number of bad records
  533.  MOV A,H
  534.  ORA L  ; Set zero flag, if no bad blocks
  535.  RET   ; Return from 'FINDB'
  536. ;.....
  537. ;
  538. ;
  539. ; Check system tracks, notify user if bad, but continue
  540. ;
  541. CHKSYS: XRA A
  542.  STA RECCNT
  543.  LXI H,0000
  544.  SHLD RECCNT+1 ; Initialize records read to zero
  545.  LHLD SYSTRK  ; Get # system tracks
  546.  MOV A,H  ; Get high part
  547.  ORA A
  548.  JNZ SKPSYS  ; Skip system track check if non-zero
  549.  MOV A,L  ; Get low part
  550.  ORA A
  551.  JZ NOSYS  ; No system tracks to check
  552.  CPI 5  ; >5 tracks?
  553.  JNC SKPSYS  ; Skip check if so
  554.  LDA ASTRS  ; Set column counter
  555.  STA COLUMN
  556.  LXI D,TSTMSG ; Testing system tracks message
  557.  CALL PSTRNG
  558.  LXI H,0  ; Set track 0,record 1
  559.  SHLD TRACK
  560.  INX H
  561.  SHLD RECORD
  562.  LHLD SPT0  ; Get SPT0 value
  563.  MOV A,H
  564.  ORA L  ; Is it zero?
  565.  JNZ CHK1
  566.  LHLD SPTSAV  ; If yes, use data track SPT
  567. CHK1: SHLD SPT  ; Initialize track 0 SPT
  568. ;
  569. CHKSY0: CALL READS  ; Do track 0
  570.  JNZ SYSERR
  571.  LXI D,1
  572.  LHLD TRACK
  573.  CALL SUBDE
  574.  JC CHKSY0
  575. ;
  576.  LHLD OSTK  ; Get other system track value
  577.  MOV A,H
  578.  ORA L  ; Is it zero?
  579.  JNZ CHK2
  580.  LHLD SPTSAV  ; If yes, use data track SPT
  581. ;
  582. CHK2: SHLD SPT  ; Initialize SPT value
  583. ;
  584. CHKSY1: CALL READS  ; Read a record
  585.  JNZ SYSERR  ; Notify, if bad blocks here
  586.  LHLD SYSTRK  ; Set up
  587.  XCHG
  588.  LHLD TRACK
  589.  CALL SUBDE  ; Do the subtract
  590.  JC CHKSY1  ; Loop while track < SYSTRK
  591. ;
  592.   IF TEST
  593.  CALL CRLF
  594.  MVI A,TAB  ; Get a tab
  595.  CALL TYPE  ; Print it
  596.  LXI H,RECCNT ; Get # records so far
  597.  CALL DECOUT  ; Print it
  598.  LXI D,SYSMSG ; Point to message
  599.  CALL PSTRNG
  600.   ENDIF   ; Test
  601. ;
  602.  RET   ; Return from "CHKSYS"
  603. ;.....
  604. ;
  605. ;
  606. NOSYS: LXI D,ZROSYS ; No system tracks here
  607.  JMP PSTRNG
  608. ;
  609. ZROSYS: DB CR,LF,'No SYSTEM tracks on this disk',CR,LF,'$'
  610. ;
  611. SKPSYS: LXI D,SKPMSG ; Say skipping system tracks
  612.  JMP PSTRNG
  613. ;
  614. SKPMSG: DB CR,LF,'Skipping system tracks...',CR,LF,'$'
  615. ;
  616. SYSERR:
  617.  LXI D,ERMSG5 ; Say no go, and bail out
  618. ;
  619. PSTRNG: MVI C,9  ; BDOS print string function
  620.  CALL BDOS
  621.  RET   ; Return from "SYSERR" or subroutine
  622. ;.....
  623. ;
  624. ;
  625. ; Check for bad blocks in directory area
  626. ;
  627. CHKDIR: LXI D,TDRMSG ; Testing directory area message
  628.  CALL PSTRNG
  629.  LXI B,0  ; Start at block 0
  630. ;
  631. CHKDI1: CALL READB  ; Read a block
  632.  JNZ ERROR6  ; If bad, show error in directory area
  633.  INX B  ; Bump for next block
  634.  LHLD DIRBKS  ; Set up (CURGRP - DIRBKS)
  635.  DCX H  ; Make 0-relative
  636.  MOV D,B
  637.  MOV E,C
  638.  CALL SUBDE  ; Do the subtract
  639.  JNC CHKDI1  ; Loop until CURGRP > DIRGRP
  640.  RET   ; Return from CHKDIR
  641. ;.....
  642. ;
  643. ;
  644. ; Read all records in block, and return zero flag set if none bad
  645. ;
  646. READB: CALL CNVRTB  ; Convert to track/record in 'HL' regs.
  647.  LDA BLM
  648.  INR A  ; Number of records/block
  649.  MOV D,A  ; In 'D' register
  650. ;
  651. READBA: PUSH D
  652.  CALL READS  ; Read skewed record
  653.  POP D
  654.  RNZ   ; Error if not zero
  655.  DCR D  ; Debump record/block
  656.  JNZ READBA  ; Do next, if not finished
  657.  RET   ; Return from 'READBA'
  658. ;.....
  659. ;
  660. ;
  661. ; Convert block number to track and skewed record number
  662. ;
  663. CNVRTB: PUSH B  ; Save current group
  664.  MOV H,B  ; Need it in 'HL'
  665.  MOV L,C  ; For easy shifting
  666.  LDA BSH  ; Dpb value that tells how to
  667. ;
  668. SHIFT: DAD H  ; Shift group number to get
  669.  DCR A  ; Disk-data-area relative
  670.  JNZ SHIFT  ; Record number
  671.  XCHG   ; Rel record # into 'DE'
  672.  LHLD SPT  ; Records per track from DPB
  673.  CALL NEG  ; Faster to DAD than call SUB D
  674.  XCHG
  675.  LXI B,0  ; Initialize quotient
  676. ;
  677. ;
  678. ; Divide by number of records
  679. ; quotient = track
  680. ;      mod = record
  681. ;
  682. DIVLP: INX B  ; Dirty division
  683.  DAD D
  684.  JC DIVLP
  685.  DCX B  ; Fixup last
  686.  XCHG
  687.  LHLD SPT
  688.  DAD D
  689.  INX H
  690.  SHLD RECORD  ; Now have logical record
  691.  LHLD SYSTRK  ; But before we have track #,
  692.  DAD B  ; We have to add system track offset
  693.  SHLD TRACK
  694.  POP B  ; This was our group number
  695.  RET
  696. ;.....
  697. ;
  698. ;
  699. ; Reads a logical record (if it can) and returns zero flag set if no
  700. ; error.
  701. ;
  702. READS: PUSH B  ; Save the group number
  703.  CALL LTOP  ; Convert logical to physical
  704.  LDA VER2FL  ; Now check version
  705.  ORA A
  706.  JZ NOTCP2  ; Skip this stuff if CP/M 1.4
  707.  LHLD PHYREC  ; Get physical record
  708.  MOV B,H  ; Into 'BC'
  709.  MOV C,L
  710.  CALL SETREC  ; BIOS set record call
  711. ;
  712. ;
  713. ; QUICK NOTE OF EXPLANATION:  This code appears as if we skipped the
  714. ; SETREC routine for 1.4 CP/M users.  That is not true.  In CP/M 1.4,
  715. ; the call within the LTOP routine to RECTRAN actually does the set
  716. ; record, so no need to do it twice.
  717. ;
  718. NOTCP2: LHLD TRACK  ; Now set the track
  719.  MOV B,H  ; CP/M wants it in 'BC'
  720.  MOV C,L
  721.  CALL SETTRK  ; BIOS set track
  722. ;
  723. ;
  724. ; Now do the record read
  725. ;
  726.  CALL DREAD  ; BIOS disk read
  727.  ORA A  ; Set flags
  728.  PUSH PSW  ; Save error flag
  729. ;
  730.   IF TEST
  731.  CALL INCREC  ; Increment record count
  732.   ENDIF   ; Test
  733. ;
  734.  LHLD RECORD  ; Get logical record #
  735.  INX H  ; We want to increment to next
  736.  XCHG   ; But first,check overflow
  737.  LHLD SPT  ; By doing (recpertrk-record)
  738.  CALL SUBDE  ; Do the subtraction
  739.  XCHG
  740.  JNC NOOVF  ; Jump if not record > recpertrk
  741. ;
  742. ;
  743. ; Record overflow...bump track number, reset record
  744. ;
  745.  LHLD TRACK
  746.  INX H
  747.  SHLD TRACK
  748.  LDA ASTRS  ; Check if column length set
  749.  ORA A
  750.  JNZ HDCOPY  ; Non-zero - do *'s
  751.  SHLD DECWRK  ; Decout destroys
  752.  LXI D,TRKMSG
  753.  CALL PSTRNG
  754.  LXI H,DECWRK
  755.  CALL DECOUT
  756.  MVI A,' '
  757.  CALL TYPE
  758.  JMP NOCRLF
  759.  
  760. HDCOPY: MVI A,'*'  ; Tell console another track done
  761.  CALL TYPE
  762.  LDA COLUMN  ; Check column
  763.  ORA A  ; Skip if zero
  764.  JZ NOCRLF
  765.  DCR A
  766.  STA COLUMN
  767.  JNZ NOCRLF  ; Jump if less than 80
  768.  LDA ASTRS  ; Reset column
  769.  STA COLUMN
  770.  CALL CRLF
  771. ;
  772. NOCRLF: CALL STOP  ; See if console wants to quit
  773.  LXI H,1  ; New record number on next track
  774. ;
  775. NOOVF: SHLD RECORD  ; Put record away
  776.  POP PSW  ; Get back error flags
  777.  POP B  ; Restore group number
  778.  RET
  779. ;.....
  780. ;
  781. ;
  782. ; Convert logical record # to physical
  783. ;
  784. LTOP: LHLD RECTBL  ; Set up parameters
  785.  XCHG   ; For call to RECTRAN
  786.  LHLD RECORD
  787.  MOV B,H
  788.  MOV C,L
  789.  DCX B  ; Always call RECTRN w/zero-rel sec #
  790. ;
  791.  PUSH H
  792.  PUSH D
  793.  LHLD SYSTRK
  794.  XCHG
  795.  LHLD TRACK
  796.  CALL SUBDE  ; No translation..
  797.  POP D
  798.  POP H
  799.  JC LTOP1  ; If doing system tracks
  800. ;
  801. RECT1: CALL RECTRN  ; Do the record translation
  802.  LDA SPT+1  ; Check if big tracks
  803.  ORA A  ; Set flags (tracks > 256 records)
  804.  JNZ LTOP1  ; No so skip
  805.  LDA TRACK  ; Check for track 0
  806.  MOV B,A
  807.  LDA TRACK+1  ; High order
  808.  ORA B
  809.  JNZ LTOP1  ; Not track 0
  810. ;; MOV H,A  ; Zero out upper 8 bits
  811. ;
  812. LTOP1: SHLD PHYREC  ; Put away physical record
  813.  RET
  814. ;.....
  815. ;
  816. ;
  817. ; Direct BIOS calling is done here...
  818. ;
  819. SETDSK: JMP $-$  ; Filled in by SETUP
  820. SETTRK: JMP $-$  ; Filled in by SETUP
  821. SETREC: JMP $-$  ; Filled in by SETUP
  822. DREAD: JMP $-$  ; Filled in by SETUP
  823. RECTRN: JMP $-$  ; Filled in by SETUP
  824. ;
  825. ;
  826. ; Put bad block in bad block list
  827. ;
  828. SETBD: PUSH B
  829.  LXI D,BBMSG  ; Bad block message
  830.  CALL PSTRNG
  831.  POP B  ; Get back block number
  832.  MOV A,B
  833.  CALL HEXO  ; Print in hex
  834.  MOV A,C
  835.  CALL HEXO
  836.  CALL CRLF
  837.  LXI H,DM  ; Point to exitsing bad blocks
  838. ;
  839. SETBD2: MOV A,M  ; Get first 8 bits of bad map entry
  840.  INX H
  841.  CMP C  ; Is new entry already there ?
  842.  JZ SETBD4  ; Maybe
  843.  LDA DSM+1  ; Check size of block entries
  844.  ORA A
  845.  JZ SETBD3  ; Small blocks
  846.  INX H  ; Skip over high order half
  847. ;
  848. SETBD3: PUSH H
  849.  XCHG   ; Save 'HL'
  850.  LHLD DMPTR
  851.  XCHG
  852.  CALL SUBDE  ; Scan pointer-(DMPTR)
  853.  POP H  ; Restore scan pointer
  854.  JC SETBD2  ; Continue searching
  855.  JMP SETBD9  ; Put it away
  856. ;
  857. SETBD4: LDA DSM+1
  858.  ORA A
  859.  RZ   ; Small blocks = done
  860.  MOV A,M  ; Get high order half of existing block
  861.  CMP B  ; Compare with block to be added
  862.  RZ   ; Really already there
  863.  INX H  ; Point to low order of next block
  864.  JMP SETBD3  ; Check if done
  865. ;
  866. SETBD9: LHLD DMCNT  ; Get number of records
  867.  LDA BLM  ; Get block shift value
  868.  INR A  ; Makes record/group value
  869.  MOV E,A  ; We want 16 bits
  870.  MVI D,0
  871.  DAD D  ; Bump by number in this block
  872.  SHLD DMCNT  ; Update number of records
  873.  LHLD BADBKS  ; Increment number of bad blocks
  874.  INX H
  875.  SHLD BADBKS
  876.  LHLD DMPTR  ; Get pointer into DM
  877.  MOV M,C  ; And put bad block number
  878.  INX H  ; Bump to next available extent
  879.  LDA DSM+1  ; Check if 8 or 16 bit block size
  880.  ORA A
  881.  JZ SMGRP  ; Jump if 8 bit blocks
  882.  MOV M,B  ; Else store hi byte of block #
  883.  INX H  ; And bump pointer
  884. ;
  885. SMGRP: SHLD DMPTR  ; Save DM pointer, for next time
  886.  RET   ; Return from 'SETBD'
  887. ;.....
  888. ;
  889. ;
  890. ; Eliminate any previous [UNUSED].BAD entries
  891. ;
  892. ERAB: LXI D,BFCB  ; Bad FCB
  893.  XRA A
  894.  STA BFCB+12  ; Clear extent
  895.  MVI C,15  ; Open file
  896.  CALL BDOS  ; Try to open file
  897.  CPI 0FFH  ; Not found ?
  898.  RZ   ; Yes, no need to delete it
  899.  STA DIROFS  ; Directory offset in DMA buffer
  900. ;
  901. ERAB0: LXI D,ERABMS
  902.  CALL PSTRNG
  903.  MVI C,1  ; Console input
  904.  CALL BDOS
  905.  CPI 'a'
  906.  JC ERAB1  ; Already upper case
  907.  ANI 05FH  ; Force upper case
  908. ;
  909. ERAB1: CPI 'Y'
  910.  JZ ERAB4  ; Do it
  911.  CPI 'N'
  912.  JNZ ERAB0  ; Invalid response
  913.  CALL ERAB2  ; Load tables, then erase old file
  914. ;
  915. ERAB4: CALL CRLF
  916.  LXI D,BFCB  ; Point to bad FCB
  917.  MVI C,19  ; BDOS delete file function
  918.  CALL BDOS
  919.  RET
  920. ;.....
  921. ;
  922. ;
  923. ERABMS: DB 'Erase Existing [UNUSED].BAD file ? (Y/N) $'
  924.     ; Flag old bad blocks for this run
  925. ;
  926. ;
  927. ;Move bad allocated blocks to DM area
  928. ;
  929. ERAB2: LXI H,DM  ; Get DM
  930.  SHLD DMPTR  ; Save as new pointer
  931.  LDA EXM  ; Get the extent shift factor
  932.  MVI C,0  ; Init bit count
  933.  CALL COLECT  ; Get shift value
  934.  LXI H,128  ; Starting extent size
  935.  MOV A,C  ; First see if any shifts to do
  936.  ORA A
  937.  JZ ERAB2B  ; Jump if none
  938. ;
  939. ERAB2A: DAD H  ; Shift
  940.  DCR A  ; Bump
  941.  JNZ ERAB2A  ; Loop
  942. ;
  943. ERAB2B: PUSH H  ; Save this, it is records per extent
  944.  LDA BSH  ; Get block shift
  945.  MOV B,A
  946. ;
  947. ERAB2C: CALL ROTRHL  ; Shift right
  948.  DCR B
  949.  JNZ ERAB2C  ; To get blocks per extent
  950.  MOV A,L  ; It's in 'L' (can't be >16)
  951.  STA BLKEXT  ; SETDME will need this later
  952.  POP H  ; Get back recrds/extent
  953. ;
  954. ERAB2D: XCHG   ; Now have records/extent in 'DE'
  955.  LHLD DMCNT  ; Count of bad records
  956. ;
  957. ERAB2E: PUSH H
  958.  PUSH D
  959.  LDA BFCB+15  ; Record count
  960.  MOV L,A
  961.  MVI H,0
  962.  MOV B,H  ; Save record count in 'BC'
  963.  MOV C,L
  964.  POP D
  965.  CALL SUBDE  ; Have to subtract first
  966.  POP H  ; This pop makes it compare only
  967.  PUSH PSW
  968.  DAD B  ; New count of bad records
  969.  SHLD DMCNT  ; Save count of bad records
  970.  POP PSW
  971.  JC ERAB2G  ; Jump if less than 1 extent worth
  972.  MOV A,B
  973.  ORA C  ; Test if subtract was 0
  974.  RZ   ; Extent is empty (special case)
  975.  PUSH H  ; Save total
  976.  PUSH D  ; And records/extent
  977.  CALL ERAB2G  ; Get next extent
  978.  POP D  ; Get back records/extent
  979.  POP H  ; And count of bad records
  980.  CPI 0FFH  ; Check return from search next
  981.  RZ
  982.  JMP ERAB2E  ; And loop
  983. ;
  984. ;
  985. ; Load an extent's worth of bad records/block numbers. 'BC' contains
  986. ; the number of records in this extent
  987. ;
  988. ERAB2G: MOV A,B
  989.  ORA C  ; Check record count
  990.  MVI A,0FFH  ; Assume zero
  991.  RZ   ; No more to process
  992.  PUSH B  ; Save records in this extent
  993.  LDA BLM  ; Block mask
  994.  INR A  ; Make records/block
  995.  CMA   ; Make negative
  996.  MOV E,A
  997.  MVI D,0FFH
  998.  INX D  ; Make 2's complement
  999.  POP H  ; Hl = number of records
  1000.  LXI B,0
  1001. ;
  1002. ;
  1003. ; Divide records in this extent by recs/block giving blocks
  1004. ;
  1005. ERAB2I: DAD D
  1006.  INX B  ; Bump quotient
  1007.  JC ERAB2I  ; Not done yet
  1008.  DCX B  ; Account for overshoot
  1009.  LHLD BADBKS  ; Bad block count
  1010.  DAD B
  1011.  SHLD BADBKS
  1012. ;
  1013. ERAB2K: LXI H,BFCB+16 ; Disk alloc map in FCB
  1014.  XCHG
  1015.  LHLD DMPTR  ; Point to bad allocation map
  1016. ;
  1017. ERAB2L: LDAX D
  1018.  MOV M,A
  1019.  INX H
  1020.  INX D
  1021. ;
  1022. ;
  1023. ; Now see if 16 bit groups...if so, we have to move another byte
  1024. ;
  1025.  LDA DSM+1  ; This tells us
  1026.  ORA A
  1027.  JZ ERAB2M  ; If zero, then not
  1028.  LDAX D  ; Is 16 bits, so do another
  1029.  MOV M,A
  1030.  INX H
  1031.  INX D
  1032. ;
  1033. ERAB2M: DCR C  ; Count down
  1034.  JNZ ERAB2L
  1035.  SHLD DMPTR
  1036.  LDA BFCB+12
  1037.  INR A
  1038.  STA BFCB+12  ; Next extent number
  1039.  LXI D,BFCB
  1040.  MVI C,15  ; Open next extent
  1041.  CALL BDOS
  1042.  STA DIROFS
  1043.  RET
  1044. ;.....
  1045. ;
  1046. ;
  1047. ; Create [UNUSED].BAD file entry
  1048. ;
  1049. OPENB: LXI D,BFCB  ; Point to bad FCB
  1050.  MVI C,22  ; BDOS make file function
  1051.  CALL BDOS
  1052.  CPI 0FFH  ; Check for open error
  1053.  RNZ   ; Return from 'OPENB', if no error
  1054.  JMP ERROR7  ; Bail out...cannot create [UNUSED].BAD
  1055. ;
  1056. CLOSEB: XRA A
  1057.  LDA BFCB+14  ; Get CP/M 2.x 's2' byte
  1058.  ANI 1FH  ; Zero update flags
  1059.  STA BFCB+14  ; Restore it to our FCB (won't hurt 1.4)
  1060.  LXI D,BFCB  ; FCB for [UNUSED].BAD
  1061.  MVI C,16  ; BDOS close file function
  1062.  CALL BDOS
  1063.  RET   ; Return from 'CLOSEB'
  1064. ;.....
  1065. ;
  1066. ;
  1067. ; Move bad area DM to BFCB
  1068. ;
  1069. SETDM: LXI H,DM  ; Get DM
  1070.  SHLD DMPTR  ; Save as new pointer
  1071.  LDA EXM  ; Get the extent shift factor
  1072.  MVI C,0  ; Initialize bit count
  1073.  CALL COLECT  ; Get shift value
  1074.  LXI H,128  ; Starting extent size
  1075.  MOV A,C  ; First see if any shifts to do
  1076.  ORA A
  1077.  JZ NOSHFT  ; Jump if none
  1078. ;
  1079. ESHFT: DAD H  ; Shift
  1080.  DCR A  ; Bump
  1081.  JNZ ESHFT  ; Loop
  1082. ;
  1083. NOSHFT: PUSH H  ; Save this, it is records per extent
  1084.  LDA BSH  ; Get block shift
  1085.  MOV B,A
  1086. ;
  1087. BSHFT: CALL ROTRHL  ; Shift right
  1088.  DCR B
  1089.  JNZ BSHFT  ; To get blocks per extent
  1090.  MOV A,L  ; It is in 'L' (cannot be 16)
  1091.  STA BLKEXT  ; SETDME will need this later
  1092.  POP H  ; Get back rececord/extent
  1093. ;
  1094. SET1: XCHG   ; Now have records/extent in 'DE'
  1095.  LHLD DMCNT  ; Count of bad records
  1096. ;
  1097. SETDMO: PUSH H  ; Set flags on (DMCNT-BADCNT)
  1098.  CALL SUBDE  ; Have to subtract first
  1099.  MOV B,H  ; Save result in 'BC'
  1100.  MOV C,L
  1101.  POP H  ; This pop makes it compare only
  1102.  JC SETDME  ; Jump if less than 1 extent worth
  1103.  MOV A,B
  1104.  ORA C  ; Test if subtract was 0
  1105.  JZ EVENEX  ; Extent is exactly filled (special case)
  1106.  MOV H,B  ; Restore result to 'HL'
  1107.  MOV L,C
  1108.  PUSH H  ; Save total
  1109.  PUSH D  ; And records/extent
  1110.  XCHG
  1111.  CALL SETDME  ; Put away one extent
  1112.  XCHG
  1113.  SHLD DMPTR  ; Put back new DM pointer
  1114.  POP D  ; Get back records/extent
  1115.  POP H  ; And count of bad records
  1116.  JMP SETDMO  ; And loop
  1117. ;
  1118. ;
  1119. ; Handle the special case of a file that ends on an extent boundary.
  1120. ; CP/M requires that such a file have a succeeding empty extent in order
  1121. ; for the BDOS to properly access the file.
  1122. ;
  1123. EVENEX: XCHG   ; First set extent with bad blocks
  1124.  CALL SETDME
  1125.  XCHG
  1126.  SHLD DMPTR
  1127.  LXI H,0  ; Now set one with no data blocks
  1128. ;
  1129. ;
  1130. ; Fill in an extent's worth of bad records/block numbers.  Also fill in
  1131. ; the extent number in the FCB.
  1132. ;
  1133. SETDME: PUSH H  ; Save record count
  1134.  LDA EXTNUM  ; Update extent byte
  1135.  INR A
  1136.  STA EXTNUM  ; Save for later
  1137.  STA BFCB+12  ; And put in FCB
  1138.  CALL OPENB  ; Open this extent
  1139.  POP H  ; Retrieve record count
  1140. ;
  1141. ;
  1142. ; Divide record count by 128 to get the number of logical extents to put
  1143. ; in the EX field
  1144. ;
  1145.  MVI B,0  ; Initialize quotient
  1146.  LXI D,-128
  1147.  MOV A,H  ; Test for special case
  1148.  ORA L  ; Of no records
  1149.  JZ SKIP
  1150. ;
  1151. DIVLOP: DAD D  ; Subtract
  1152.  INR B  ; Bump quotient
  1153.  JC DIVLOP
  1154.  LXI D,128  ; Fix up overshoot
  1155.  DAD D
  1156.  DCR B
  1157.  MOV A,H  ; Test for wraparound
  1158.  ORA L
  1159.  JNZ SKIP
  1160.  MVI L,80H  ; Record length
  1161.  DCR B
  1162. ;
  1163. SKIP: LDA EXTNUM  ; Now fix up extent num
  1164.  ADD B
  1165.  STA EXTNUM
  1166.  STA BFCB+12
  1167.  MOV A,L  ; Mod is record count
  1168.  STA BFCB+15  ; That goes in receive byte
  1169. ;
  1170. MOVDM: LDA BLKEXT  ; Get blocks per extent
  1171.  MOV B,A  ; Into 'B'
  1172. ;
  1173. SETD1: LHLD DMPTR  ; Point to bad allocation map
  1174.  XCHG
  1175.  LXI H,BFCB+16 ; Disk alloc map in FCB
  1176. ;
  1177. SETDML: LDAX D
  1178.  MOV M,A
  1179.  INX H
  1180.  INX D
  1181. ;
  1182. ;
  1183. ; Now see if 16 bit groups...if so, we have to move another byte
  1184. ;
  1185.  LDA DSM+1  ; This tells us
  1186.  ORA A
  1187.  JZ BUMP1  ; If zero, then not
  1188.  LDAX D  ; Is 16 bits, so do another
  1189.  MOV M,A
  1190.  INX H
  1191.  INX D
  1192. ;
  1193. BUMP1: DCR B  ; Count down
  1194.  JNZ SETDML
  1195.  PUSH D
  1196.  CALL CLOSEB  ; Close this extent
  1197.  POP D
  1198.  RET
  1199. ;.....
  1200. ;
  1201. ;
  1202. ; Error messages
  1203. ;
  1204. SELERR: LXI D,SELEMS ; Say no go, and bail out
  1205.  JMP PMSG
  1206. SELEMS: DB CR,LF,'Drive specifier out of range$'
  1207. ERMSG5: DB CR,LF,BELL,'+++ Warning...system tracks'
  1208.  DB ' bad +++',BELL,CR,LF,'$'
  1209. ERROR6: LXI D,ERMSG6 ; Oops...clobbered directory
  1210.  JMP PMSG
  1211. ERMSG6: DB CR,LF,BELL,'Bad directory area, try reformatting'
  1212.  DB CR,LF,BELL,'$'
  1213. ERROR7: LXI D,ERMSG7 ; Say no go, and bail out
  1214.  JMP PMSG
  1215. ERMSG7: DB CR,LF,BELL,'Can''t create [UNUSED].BAD$'
  1216. BELLS: DB BELL,BELL,BELL,'$'
  1217. ;
  1218. ;
  1219. HELP: LXI D,USAGE
  1220.  CALL PSTRNG
  1221.  JMP WBOOT
  1222. USAGE: DB CR,LF
  1223.  DB 'Usage:',CR,LF,CR,LF
  1224.  DB ' FBAD [d:] ['
  1225.  DB '$' OR 80H
  1226.  DB 'options]',CR,LF,CR,LF
  1227.  DB '     d:  =  drive to check',CR,LF
  1228.  DB 'options  =  S       check system tracks',CR,LF
  1229.  DB '            Sn0     n0 =  sector count for track 0',CR,LF
  1230.  DB '            Sn0,n1  n1 =  sector count for sys trk > 0',CR,LF
  1231.  DB '                     (default is '
  1232.  DB DFSPT0/10+'0',(DFSPT0 MOD 10)+'0'
  1233.  DB ', '
  1234.  DB DFOSTK/10+'0',(DFOSTK MOD 10)+'0'
  1235.  DB ')',CR,LF
  1236.  DB '            X       same as  S  with different default SPT'
  1237.  DB CR,LF
  1238.  DB '                     (default is '
  1239.  DB XSPT0/10+'0',(XSPT0 MOD 10)+'0'
  1240.  DB ', '
  1241.  DB XOSTK/10+'0',(XOSTK MOD 10)+'0'
  1242.  DB ')',CR,LF
  1243.  DB '            A       print "*" instead of track number'
  1244.  DB CR,LF
  1245.  DB '$'
  1246. ;
  1247. ;
  1248. ; Look for opitons flag (SPCHR)
  1249. ;
  1250. GETOPT: LXI H,TBUFF  ; Start at beginning of line
  1251.  MOV A,M
  1252.  ORA A  ; If it is empty
  1253.  RZ   ; Exit
  1254.  INX H  ; Point to first character
  1255.  LDA FCB
  1256.  ORA A  ; Was there a drive specified?
  1257.  JZ FINDIT
  1258.  INX H  ; Yes, skip over it
  1259.  INX H
  1260. ;
  1261. FINDIT: INX H
  1262.  MOV A,M  ; Get a character
  1263.  CPI 00H  ; End of line ?
  1264.  RZ   ; Continue.
  1265.  CPI ' '
  1266.  JZ FINDIT  ; Skip spaces.
  1267.  CPI SPCHR  ; Option flag ?
  1268.  JNZ HELP  ; No, error
  1269. ;
  1270. FINDIT1:INX H  ; Check next character
  1271.  MOV A,M
  1272.  CPI 00H  ; End of line ?
  1273.  RZ   ; Test disk.
  1274.  CPI ' '
  1275.  JZ FINDIT1  ; Skip spaces
  1276.  CPI 'A'
  1277.  JZ AOPT  ; Toggle asterisk/track number flag
  1278.  CPI 'S'
  1279.  JZ SOPT  ; Test system tracks
  1280.  CPI 'X'
  1281.  JZ XOPT  ; Set special value for system tracks spt
  1282.  JMP HELP
  1283. ;
  1284. AOPT: MVI A,76
  1285.  STA ASTRS  ; Initialize column count
  1286.  JMP FINDIT1  ; Look for more options
  1287. ;
  1288. SOPT: MVI A,1
  1289.  STA SYSTST  ; Set system track test flag.
  1290. ;
  1291. ;
  1292. ; If numeric after S, must be track zero's record count.
  1293. ;
  1294.  INX H
  1295.  MOV A,M
  1296.  CALL VALDEC  ; Number from 0 to 9 ?
  1297.  JC SOPT1  ; No, check next option
  1298.  XCHG   ; Get address in de
  1299.  LXI H,00  ; Initialize hl to 0
  1300.  CALL ATOH  ; Convert to hex
  1301.  SHLD SPT0  ; Save sector count for first track
  1302.  XCHG   ; Restore hl
  1303. ;
  1304. ;
  1305. ; If comma after first number, must be record count for other system
  1306. ; tracks.
  1307. ;
  1308.  MOV A,M
  1309.  CPI ','
  1310.  JNZ SOPT1
  1311.  INX H
  1312.  MOV A,M
  1313.  CALL VALDEC
  1314.  JC HELP  ; Not a valid number, give help
  1315.  XCHG
  1316.  LXI H,00
  1317.  CALL ATOH
  1318.  SHLD OSTK
  1319.  XCHG
  1320. ;
  1321. SOPT1: DCX H  ; Back up to next char.
  1322.  JMP FINDIT1  ; See if more options
  1323. ;.....
  1324. ;
  1325. ;
  1326. XOPT: MVI A,1
  1327.  STA SYSTST  ; Set system track test flag.
  1328.  PUSH H
  1329.  LXI H,XSPT0  ; Special value for spt0
  1330.  SHLD SPT0
  1331.  LXI H,XOSTK  ; Special value for other system track(s)
  1332.  SHLD OSTK
  1333.  POP H
  1334.  JMP FINDIT1  ; Look for more options
  1335. ;.....
  1336. ;
  1337. ;
  1338. ; ASCII to hex conversion
  1339. ;
  1340. ATOH: MOV B,H  ; B = 00
  1341. ;
  1342. ATOH1: LDAX D  ; Get character
  1343.  CALL VALDEC  ; Make 0-9, see if valid
  1344.  RC   ; Not decimal, stop conversion
  1345.  PUSH D
  1346.  MOV D,H
  1347.  MOV E,L  ; De = hl
  1348.  DAD H  ; 2*n
  1349.  DAD H  ; 4*n
  1350.  DAD D  ; 5*n
  1351.  DAD H  ; 10*n
  1352.  POP D
  1353.  MOV C,A
  1354.  DAD B  ; Add in next char
  1355.  INX D  ; Point to next char
  1356.  JMP ATOH1  ; Process it
  1357. ;....
  1358. ;
  1359. ;
  1360. ; Convert ASCII digit to decimal number.  Return with carry = 0 if valid
  1361. ; digit from 0-9.
  1362. ;
  1363. VALDEC: SUI '0'  ; Subtract ascii offset
  1364.  RC   ; < 0
  1365.  CPI 10  ; > 9
  1366.  CMC   ; Adjust carry flag
  1367.  RET
  1368. ;
  1369. ;
  1370. ;=======================================================================
  1371. ;
  1372. ;      SUBROUTINES
  1373. ;
  1374. ;=======================================================================
  1375. ;
  1376. ;
  1377. ADD24: PUSH H
  1378.  ORA A  ; Clear carry
  1379.  MOV A,M  ; Do 24-bit add
  1380.  ADC C
  1381.  MOV M,A
  1382.  INX H
  1383.  MOV A,M
  1384.  ADC B
  1385.  MOV M,A
  1386.  INX H
  1387.  MOV A,M
  1388.  ADC B
  1389.  MOV M,A
  1390.  POP H
  1391.  RET
  1392. ;.....
  1393. ;
  1394. ;
  1395. ; CR/LF to the console
  1396. ;
  1397. CRLF: MVI A,CR
  1398.  CALL TYPE
  1399.  MVI A,LF  ; Fall into 'type'
  1400. ;
  1401. TYPE: PUSH B
  1402.  PUSH D
  1403.  PUSH H
  1404.  MOV E,A  ; Character to 'e' for cp/m
  1405.  MVI C,2  ; Print console function
  1406.  CALL BDOS  ; Print character
  1407.  POP H
  1408.  POP D
  1409.  POP B
  1410.  RET
  1411. ;.....
  1412. ;
  1413. ;
  1414. ; Decimal output routine - If the disk size is at or near the maximum of
  1415. ; 65,536 records, the record addition overflows 16 bits.  The counter
  1416. ; has been increased to 24 bits.  This routine destroys the counter-
  1417. ; value it is called with.
  1418. ;
  1419. DECOUT: PUSH B
  1420.  PUSH D
  1421.  LXI B,-10
  1422.  LXI D,-1
  1423. ;
  1424. DECOU2: CALL ADD24
  1425.  INX D
  1426.  JC DECOU2
  1427.  LXI B,10
  1428.  CALL ADD24
  1429.  MOV A,D
  1430.  ORA E
  1431.  PUSH H
  1432.  MOV A,M
  1433.  MOV M,E
  1434.  MOV E,A
  1435.  INX H
  1436.  MOV A,M
  1437.  MOV M,D
  1438.  MOV D,A
  1439.  INX H
  1440.  MVI M,0
  1441.  POP H
  1442.  CNZ DECOUT  ; Recursive call..
  1443.  MOV A,E
  1444.  ADI '0'
  1445.  CALL TYPE
  1446.  POP D
  1447.  POP B
  1448.  RET
  1449. ;.....
  1450. ;
  1451. ;
  1452. ; Collect the number of '1' bits in 'A' as a count in 'C'
  1453. ;
  1454. COLECT: MVI B,8
  1455. ;
  1456. COLOP: RAL
  1457.  JNC COSKIP
  1458.  INR C
  1459. ;
  1460. COSKIP: DCR B
  1461.  JNZ COLOP
  1462.  RET
  1463. ;.....
  1464. ;
  1465. ;
  1466. ; Subroutine to determine the number of groups reserved for the directory
  1467. ;
  1468. GETDIR: MVI C,0  ; Initialize bit count
  1469.  LDA AL0  ; Read directory group bits
  1470.  CALL COLECT  ; Collect count of directory groups..
  1471.  LDA AL1  ; In 'c'
  1472.  CALL COLECT
  1473.  MOV L,C
  1474.  MVI H,0  ; 'bc' now has a default start group #
  1475.  SHLD DIRBKS  ; Save for later
  1476.  RET
  1477. ;.....
  1478. ;
  1479. ;
  1480. ; Print byte in accumulator in hex
  1481. ;
  1482. HEXO: PUSH PSW  ; Save for second half
  1483.  RRC   ; Move into position
  1484.  RRC
  1485.  RRC
  1486.  RRC
  1487.  CALL NYBBLE  ; Print most significant nybble
  1488.  POP PSW
  1489. ;
  1490. NYBBLE: ANI 0FH  ; Low nybble only
  1491.  ADI 90H
  1492.  DAA
  1493.  ACI 40H
  1494.  DAA
  1495.  JMP TYPE  ; Print in hex
  1496. ;.....
  1497. ;
  1498. ;
  1499. ; Total record count routine (24-bit)
  1500. ;
  1501. INCREC: LXI H,RECCNT ; Point to record count
  1502.  MVI A,1  ; Add 1
  1503.  ORA A  ; Clear carry
  1504.  ADC M  ; Add current total
  1505.  MOV M,A  ; And save 1st byte
  1506.  RNC   ; Return if no carry
  1507.  INX H  ; Increment pointer
  1508.  MVI A,0  ; Reset 'a'
  1509.  ADC M  ; Add 2nd byte
  1510.  MOV M,A  ; And save
  1511.  RNC   ; Return if no carry
  1512.  INX H  ; Increment pointer
  1513.  MVI A,0  ; And again for 3rd byte
  1514.  ADC M
  1515.  MOV M,A
  1516.  RET
  1517. ;.....
  1518. ;
  1519. ;
  1520. ; Routine to fill in disk parameters
  1521. ;
  1522. LOGIT: LDA VER2FL
  1523.  ORA A  ; If not cp/m 2.x then
  1524.  JZ LOG14  ; Do it as 1.4
  1525.  LXI D,DPB  ; Then move to local
  1526.  MVI B,DPBLEN ; Workspace
  1527.  CALL MOVE
  1528.  RET
  1529. ;.....
  1530. ;
  1531. ;
  1532. LOG14: LHLD BDOS+1  ; First find 1.4 bdos
  1533.  MVI L,0
  1534.  LXI D,DPBOFF ; Then offset to 1.4's dpb
  1535.  DAD D
  1536.  MVI D,0  ; So 8 bit parms will be 16
  1537.  MOV E,M  ; Now move parms
  1538.  INX H  ; Down from bdos disk parameter block
  1539.  XCHG   ; To ours
  1540.  SHLD SPT
  1541.  XCHG
  1542.  MOV E,M
  1543.  INX H
  1544.  XCHG
  1545.  SHLD DRM
  1546.  XCHG
  1547.  MOV A,M
  1548.  INX H
  1549.  STA BSH
  1550.  MOV A,M
  1551.  INX H
  1552.  STA BLM
  1553.  MOV E,M
  1554.  INX H
  1555.  XCHG
  1556.  SHLD DSM
  1557.  XCHG
  1558.  MOV E,M
  1559.  INX H
  1560.  XCHG
  1561.  SHLD AL0
  1562.  XCHG
  1563.  MOV E,M
  1564.  XCHG
  1565.  SHLD SYSTRK
  1566.  RET
  1567. ;.....
  1568. ;
  1569. ;
  1570. ; Move from (HL) to (DE), with count in (BC)
  1571. ;
  1572. MOVE: MOV A,M
  1573.  STAX D
  1574.  INX H
  1575.  INX D
  1576.  DCR B
  1577.  JNZ MOVE
  1578.  RET
  1579. ;.....
  1580. ;
  1581. ;
  1582. ; Negate HL
  1583. ;
  1584. NEG: MOV A,L
  1585.  CMA
  1586.  MOV L,A
  1587.  MOV A,H
  1588.  CMA
  1589.  MOV H,A
  1590.  INX H
  1591.  RET
  1592. ;.....
  1593. ;
  1594. ;
  1595. ; Shift HL right one place
  1596. ;
  1597. ROTRHL: ORA A  ; Clear carry
  1598.  MOV A,H  ; Get hihg byte
  1599.  RAR   ; Shift right
  1600.  MOV H,A  ; Put back
  1601.  MOV A,L  ; Get low byte
  1602.  RAR   ; Shift with carry
  1603.  MOV L,A  ; Put back
  1604.  RET
  1605. ;.....
  1606. ;
  1607. ;
  1608. ; Subroutine to test console for CTL-C abort
  1609. ;
  1610. STOP: CALL CSTS  ; Get console status
  1611.  ORA A  ; Test flags on zero
  1612.  RZ   ; Return if no character
  1613.  CALL CI  ; Get the character
  1614.  ANI 7FH  ; Some teminals set parity bit
  1615.  CPI 'C'-40H  ; Is it ctl-c?
  1616.  RNZ   ; Return if not
  1617.  LXI D,ABORTM ; Exit with message
  1618.  CALL PSTRNG
  1619.  JMP WBOOT  ; Then leave
  1620. ;
  1621. CSTS: LDA VER2FL  ; One way for 2.x or higher...
  1622.  ORA A  ; Check for non-zero
  1623.  JZ CSTS14
  1624.  MVI C,11
  1625.  JMP BDOS
  1626. ;
  1627. CSTS14: LHLD 0001H  ; Find bios in memory
  1628.  MVI L,6  ; Offset to console status
  1629.  PCHL
  1630. ;
  1631. CI: LDA VER2FL
  1632.  ORA A
  1633.  JZ CI14
  1634.  MVI C,1
  1635.  JMP BDOS
  1636. ;
  1637. CI14: LHLD 1  ; Now find console input
  1638.  MVI L,9  ; Offset for conin
  1639.  PCHL
  1640. ;.....
  1641. ;
  1642. ;
  1643. ; Subtract DE from HL
  1644. ;
  1645. SUBDE: MOV A,L
  1646.  SUB E
  1647.  MOV L,A
  1648.  MOV A,H
  1649.  SBB D
  1650.  MOV H,A
  1651.  RET
  1652. ;.....
  1653. ;
  1654. ;
  1655. ; Zero all of memory to hold DM values
  1656. ;
  1657. ZMEM: LHLD BDOS+1  ; Get top-of-memory pointer
  1658.  LXI D,DM  ; Starting point
  1659.  CALL SUBDE  ; Get number of bytes
  1660.  MOV B,H
  1661.  MOV C,L
  1662.  XCHG   ; Begin in 'hl', count in 'bc'
  1663. ;
  1664. ZLOOP: MVI M,0  ; Zero a byte
  1665.  INX H  ; Point past
  1666.  DCX B  ; Count down
  1667.  MOV A,B
  1668.  ORA C
  1669.  JNZ ZLOOP
  1670.  RET
  1671. ;.....
  1672. ;
  1673. ;
  1674. SIGNON: DB CR,LF,'FBAD V',(VERS/10 + '0'),(VERS MOD 10)+'0','A'
  1675.  DB ' - Bad record lockout program - (^C to abort)',CR,LF,'$'
  1676. ABORTM: DB CR,LF,'Test aborted by CTL-C','$'
  1677. BBMSG: DB CR,LF,'Bad block: $'
  1678. SYSMSG: DB ' system records read',CR,LF,'$'
  1679. RECMSG: DB ' directory+data records read',CR,LF,'$'
  1680. TRKMSG: DB CR,'Track $'
  1681. NOMSG: DB 'No$'
  1682. ENDMSG: DB ' bad blocks found',CR,LF,'$'
  1683. TDAMSG: DB CR,LF,'Testing data area...',CR,LF,'$'
  1684. TSTMSG: DB CR,LF,'Testing system tracks...',CR,LF,'$'
  1685. TDRMSG: DB CR,LF,'Testing directory area...',CR,LF,'$'
  1686. DIROFS: DB 0  ; Offset of directory entry in dma buffer
  1687. BFCB: DB 0,'[UNUSED]BAD',0,0,0,0
  1688. FCBDM: DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  1689. CMSG1: DB CR,LF,'Press C to continue on the same drive'
  1690.  DB CR,LF,'Any other key aborts to CP/M $'
  1691. ;
  1692. ;
  1693. SPTSAV: DW 00  ; Save spt for data tracks
  1694. ;
  1695. ; The disk parameter block is moved here from CP/M
  1696. ;
  1697. DPB EQU $  ; Disk parameter block (copy)
  1698. SPT: DS 2  ; Records per track
  1699. BSH: DS 1  ; Block shift
  1700. BLM: DS 1  ; Block mask
  1701. EXM: DS 1  ; Extent mask
  1702. DSM: DS 2  ; Maximum block number
  1703. DRM: DS 2  ; Maximum directory block number
  1704. AL0: DS 1  ; Directory allocation vector
  1705. AL1: DS 1  ; Directory allocation vector
  1706. CKS: DS 2  ; Checked directory entries
  1707. SYSTRK: DS 2  ; System tracks
  1708. DPBLEN EQU $-DPB  ; Length of disk parameter block
  1709. ;
  1710. BLKEXT: DB 0  ; Blocks per extent
  1711. DIRBKS: DW 0  ; Calculated # of directory blocks
  1712. VER2FL: DB 0  ; Version 2.x flag, non-0 ==> 2.x or higher
  1713. BADBKS: DB 0,0,0  ; Count of bad blocks
  1714. RECORD: DW 0  ; Current record number
  1715. TRACK: DB 0,0  ; Current track number
  1716. PHYREC: DW 0  ; Current physical record number
  1717. RECTBL: DW 0  ; Record skew table pointer
  1718. EXTNUM: DB 0FFH  ; Used for updating extent number
  1719. DMCNT: DW 0  ; Number of bad records
  1720. DMPTR: DW DM  ; Pointer to next block identification
  1721. RECCNT: DB 0,0,0  ; Number of records read
  1722. COLUMN: DB 0  ; Crt column counter
  1723. DECWRK: DB 0,0,0  ; Track-storage for decout
  1724. ORIGDR: DS 1  ; Original drive spec from fcb stored here
  1725. ;
  1726. ;
  1727.  DS 200  ; Room for 100 level stack
  1728. STACK EQU $  ; Our stack
  1729. ;
  1730. DM EQU $  ; Bad block allocation map
  1731. ;
  1732. ;
  1733.  END
  1734.  
  1735.