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 / BEEHIVE / UTILITYS / FINDBAD.ARC / FINDBD54.ASM < prev   
Assembly Source File  |  1990-07-21  |  33KB  |  1,203 lines

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