home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PROGRAMS / UTILS / HARDDISK / TOADPURG.ZIP / TOADPURG.ASM < prev    next >
Encoding:
Assembly Source File  |  1986-05-20  |  30.0 KB  |  1,052 lines

  1. TITLE    TOADPURG v1.2 20 May 86
  2. ; Floppy disk purge program.
  3. ; Copyright (C) 1986 David P Kirschbaum All Rights Reserved
  4. ;             Toad Hall
  5. ;             7573 Jennings Lane
  6. ;            Fayetteville NC  28303
  7. ;            tel (919) 868-3471
  8. ;            ARPAnet ABN.ISCAMS@USC-ISID
  9.  
  10. ; Copying permitted for all non-sales applications.
  11. ; Copyright information and all credits must remain with the code.
  12.  
  13. ; This means if you've figured a way to sell it,
  14. ; I want a share!
  15.  
  16. ; Credits:
  17. ;    Portions of this code (or the idea anyway) from VERDISK.COM.
  18. ;    BIOS disk call speedup from "Speeding MS DOS" by Gregg Weissman,
  19. ;    Dr Dobbs, Mar 86
  20.  
  21. ; Program Justification and Use:
  22. ;
  23. ;   NSA and other security agencies specify the method of erasing
  24. ; sensitive data from floppy (and other) disks.  Similar to core memory
  25. ; purging, you must write each track with 1's, then with 0's, then with
  26. ; random data.  (They really meant binary 1's.)
  27.  
  28. ; Each track write must be confirmed with a track read to insure the actual
  29. ; data was written.  (No, a disk or track verify is NOT sufficient since
  30. ; that only does a CRC of the data to insure any data there is not damaged.
  31. ; It does NOT compare with a buffer read and verify.)
  32. ;
  33. ;   NSA recommends you use a debugger to physically look at the data
  34. ; on each track.  I did that plenty during the debugging of this program.
  35. ; If you don't get any error reports, you can rest assured that disk gets
  36. ; written with 1's, then 0's, then random.
  37. ; If you want to check for yourself, break it with a Ctrl C part way through,
  38. ; and look at the parts that have been purged.
  39.  
  40. ; The TOADPURGE Process:
  41. ;
  42. ;   It uses a large memory buffer, which it fills with the required
  43. ; fill data (1's, 0's, or random).
  44. ;   It then writes the full buffer to disk, one track at a time (processing
  45. ; 10 tracks in sequence for efficiency).
  46. ;   After each buffer write, it purges the memory buffer by filling it with
  47. ; the OTHER fill data (1's and 0's only).
  48. ;   It then physically reads the same tracks to the buffer, and scans that
  49. ; buffer to insure EVERY byte matches the current fill data.
  50.  
  51. ; Diskette Formats:
  52. ;
  53. ;   TOADPURGE uses the FAT disk format byte from the disk to obtain the
  54. ; floppy diskette format.  If that byte is not standard, TOADPURGE will
  55. ; assume a 9-sector track format and try the purge that way.
  56.  
  57. ;   If the disk ID byte says 8-sector tracks, TOADPURGE will try to force
  58. ; to 9-sector.  If 9-sector fails, TOADPURGE will drop back to 8-sector
  59. ; and try that way.
  60. ;
  61. ;   Even if the disk format byte says single-sided, TOADPURGE will try to
  62. ; purge the other side (just in case any data from a previous format remains).
  63. ; The second side is also forced to 9-sector format (even if side 0 proved
  64. ; to be 8-sector format) just in case.
  65.  
  66. ; Disk Errors:
  67. ;
  68. ;   TOADPURGE will report errors as they occur:
  69. ;    If a write error, attempt to write that track up to 5 times.
  70. ;    If a read error, revert back to write mode and try that track again.
  71. ;    If trying to force double-sided purging on a single-sided disk,
  72. ;    and significant errors occur on the "back side", TOADPURGE will
  73. ;    give up and revert to single-sided purging.
  74.  
  75. ; Diskette formats supported:
  76. ;
  77. ;   S8, D8, S9, D9, QD15 (untested).
  78.  
  79. ; Drives supported:
  80. ;
  81. ;   TOADPURGE supports A and B drives as specified from the command line.
  82. ;    If a drive is not given, TOADPURGE explains what's happening
  83. ;    and exits.
  84. ;    If the user attempts to command other than A or B Drives, TOADPURGE
  85. ;    presents a warning ('No way, Clyde') and reverts to A drive.
  86.  
  87. ; System requirements:
  88. ;
  89. ;   Uses PC-DOS ROM-BIOS Interrupt 13H (absolute disk read/write).
  90. ;   Could be rewritten to DOS interrupts 25H and 26H easily enough...
  91. ;   I just hated all the absolute disk address conversion math.
  92. ;   Also uses disk parameters at base memory for the disk delay speedup,
  93. ;   but this should be common to all DOS systems.
  94.  
  95. ; Redirection:
  96. ;   If you wish redirection of the screen reports to printer or file,
  97. ;   you can do that just as always.  Only problem is .. the fancy screen
  98. ;   display uses isolated carriage returns, so your file may need some
  99. ;   processing in a text editor.  If going to a printer, make sure your
  100. ;   printer is set to convert isolated carriage returns into the Cr/Lf
  101. ;   combination.
  102.  
  103. ; Improvements:
  104. ;   Plans for a "silent" mode are in the workings ... doesn't do fancy
  105. ;   screen stuff, just writes a nice neat formatted report, date/time
  106. ;   stamped, to the standard I/O (screen, printer, file).
  107.  
  108.  
  109. int21    MACRO    function        ; Call the DOS interrupt
  110.     mov    ah,function        ; Put function number in AH
  111.     int    21h
  112.     ENDM
  113.  
  114. dskint    MACRO    function        ; Call the PC disk interrupt
  115.     mov    AH,function        ; put function number in AH
  116.     int    13H            ; PC interrupt
  117.     ENDM
  118.  
  119. string    MACRO    msg
  120.     mov    DX,offset msg
  121.     ENDM
  122.  
  123. Bell    equ    07H            ;beep
  124. Tab    equ    09H
  125. Cr    equ    0DH
  126. Lf    equ    0AH
  127. AscMask    equ    2710H            ;constant for Asciizing numbers
  128. FWORD    equ    0FFFFH
  129. RWORD    equ    07FFFH
  130.  
  131. FSTATE    equ    'FF'
  132. RSTATE    equ    'RR'
  133. ZSTATE    equ    '00'
  134.  
  135. ; Structure of BIOS diskette parameters
  136. ; (from "Speeding MS DOS" by Gregg Weissman, Dr Dobbs, Mar 86)
  137. ; When we reset the drive delay parameters to the faster values suggested
  138. ; in the referenced article, the purge time for a D9 disk decreases from
  139. ; 2:43 to 2:26.  Donno if it's worth the hassle or not, but it's here for
  140. ; your edification.
  141.  
  142. ; Only problem is .. if you break this program with a Ctrl C, I don't
  143. ; exactly know WHAT happens to the disk parameter table values maintained
  144. ; by DOS.  It appears they don't get corrected by anything short of a reboot,
  145. ; and many programs can have problems.
  146.  
  147. ; For this reason, the drive delay speedup has NOT been engaged in this
  148. ; version.
  149.  
  150. ; The code is still there, though.  Set_Parms is what does it.
  151.  
  152. DiskParms    struc
  153. Spec1        db    ?
  154. Spec2        db    ?
  155. Spec3        db    ?
  156. Spec4        db    ?
  157. Spec5        db    ?
  158. Spec6        db    ?
  159. Spec7        db    ?
  160. Spec8        db    ?
  161. Spec9        db    ?
  162. ;Head_Settle    db    ?
  163. ;Motor_Wait    db    ?
  164. DiskTimes    dw    ?
  165. DiskParms    ends
  166.  
  167. ; Define location of the pointer to the parameters
  168. Sys0    Segment at 0000
  169.         org    78H
  170. Disk_Ptr    label    dword
  171. Sys0    ends
  172.  
  173. CSeg    Segment Public Para 'CODE'
  174.     Assume  CS:Cseg,DS:Cseg,ES:CSeg
  175.  
  176.     Org    5CH            ;FCB1
  177. Fcb1    label    BYTE
  178.  
  179.     Org    6CH            ;FCB2
  180. Fcb2    label    BYTE
  181.  
  182.     Org    100H
  183.  
  184. Purg    proc    Far
  185.     jmp    Start
  186.  
  187. FAT_ID_Table    db    0FFH        ;D8    FAT IDs
  188.         db    0FEH        ;S8
  189.         db    0FDH        ;D9
  190.         db    0FCH        ;S9
  191.         db    0F9H        ;QD15 !!
  192.  
  193. ; Different FAT ID byte disk formats
  194.  
  195. Side_Table    db    1        ;D8    nr sides
  196.         db    0        ;S8
  197.         db    1        ;D9
  198.         db    0        ;S9
  199.         db    1        ;QD15
  200.  
  201. Trak_Table    db    27H        ;D8    nr tracks
  202.         db    27H        ;S8
  203.         db    27H        ;D9
  204.         db    27H        ;S9
  205.         db    4FH        ;QD15
  206.  
  207. Sec_Table    db    8        ;D8    sectors per track
  208.         db    8        ;S8
  209.         db    9        ;D9
  210.         db    9        ;S9
  211.         db    0FH        ;QD15
  212.  
  213. ErMsgTblSiz    db    0F8H
  214.  
  215. Err0S    db    'ToadPurg V1.2, Toad Hall, 20 May 86'
  216. Err1S    db    'Bad command$'                    ;1
  217. Err2S    db    'Address mark not found$'            ;2
  218. Err3S    db    'Write attempted on ',Cr,Lf            ;3
  219.     db    'write-protected disk.  '
  220.     db    'Correct and hit any key to continue.$'
  221. Err4S    db    'Sector not found$'                ;4
  222. Err5S    db    'Unknown Error$'                ;no 5
  223. Err6S    db    'Diskette removed$'                ;6
  224. Err7S    db    'Unknown Error$'                ;no 7
  225. Err8S    db    'DMA overrun$'                    ;8
  226. Err9S    db    'DMA across 64Kb boundary$'            ;9
  227. Err10S    db    'Bad CRC$'                    ;10H
  228. Err11S    db    'NEC controller failed$'            ;20H
  229. Err12S    db    'Seek failed$'                    ;40H
  230. Err13S    db    'NEC Controller time out$'            ;80H
  231. Err14S    db    'Table overrun$'
  232. ErAdrTbl dw    Err0S,Err1S,Err2S,Err3S,Err4S,Err5S,Err6S
  233.     dw    Err7S,Err8S,Err9S,Err10S,Err11S,Err12S,Err13S
  234.     dw    Err14S
  235.  
  236. BadDrMsg db    Cr,Lf,'Invalid drive specified.',Cr,Lf,'$'
  237. BadFatS    db    Cr,Lf,'Error: disk not supported, or invalid FAT ID found.'
  238.     db    Cr,Lf,'Will attempt to purge as a DS9 format.',Cr,Lf,Lf,'$'
  239. Try8S    db    "9-sector tracks failed.  Switching to 8...",Cr,Lf,'$'
  240. TrySS    db    "Double-sided failed.  Switching to single...",Cr,Lf,'$'
  241. FlopOnly db    Cr,Lf,'This program purges FLOPPY DISKS ONLY!'
  242.     db    Cr,Lf,'(See the author for the special hard disk version.)'
  243.     db    Cr,Lf,'Forcing to Drive A:',Cr,Lf,'$'
  244.  
  245. FormatS        db    'Format reported as $'
  246. GonnaTryS    db    '        Attempting $'
  247. PurgS        db    Cr,'Purging '
  248. DriveA        db    'n Drive, '
  249. SideCntA    db    '% side(s), '
  250. TrkCntA        db    '%% tracks, '
  251. SecCntA        db    '%% sectors per track.',Cr,Lf,'$'
  252. WriteS        db    Cr,'  Writing$'
  253. VerifyS        db    Cr,'Verifying$'
  254. ErrStat        db    ' track '
  255. CurTrkA        db    '%%, $'
  256. SecRetA        db    '%% sectors returned$'
  257.  
  258. ErrorS        db    ', Error: '
  259. DerrA        db    '%%%, $'
  260. WerrA        db    '%$'
  261. VerrA        db    '%$'
  262.  
  263. SideS        db    'Processing side '
  264. CurSidA        db    '%, Purge Write value: '
  265. FillValA    dw    FSTATE            ;start fill state is FF
  266. CrLfS        db    Cr,Lf,'$'
  267.  
  268. VErrChkS    db    Cr,Lf,'Will attempt to verify the read...$'
  269. RdOkS        db    ', read verify ok.$'
  270. RdFailS        db    ', read verify FAILED!',Cr,Lf,'$'
  271. RetryMsg    db    ', OK on retry.       ',Cr,Lf,'$'
  272. BadMsg        db    ', still bad after 5 retries.',Cr,Lf,'$'
  273.  
  274. DonMsg        db    Cr,Lf,'Purgdisk done.$'
  275. PurgNxtS    db    Cr,Lf,'Purge another?  (Y/N): $'
  276. NewDiskS    db    Cr,Lf,'Insert new disk...$'
  277.  
  278. ; "Constants"
  279.  
  280. BufTrkSiz    db    10        ;default max of 10 tracks in buffer
  281. MaxTrkCnt    db    10        ;target track count when using buffer
  282. BufferSiz    dw    ?        ;512*9*5 worst case,
  283.                     ;256 words * 9 sectors max
  284.  
  285. TrkSiz        dw    ?        ;words in one full track
  286.  
  287. DrvSidW        LABEL    word        ;for loading DX
  288. CurDrv        db    ?        ;uninitialized drive number
  289. CurSid        db    0        ;start with side 0
  290. TrkSecW        LABEL    word        ;for loading CX
  291. StartSec    db    1        ;this is constant for CL (Sector Nr)
  292. CurTrk        db    0        ;current track for CH, init to 0
  293.  
  294. BuffPtr        dw    ?        ;buffer pointer
  295.  
  296. SeedInt        dw    ?
  297.  
  298. ;for fast disk parameters
  299.  
  300. Times        label    word
  301. HeadSettle    db    1        ;millisecond, down from 15 in DOS 3.1
  302. MotorWait    db    15        ;1/8ths of a second, down from 25
  303.  
  304. FillVal        dw    FWORD        ;first time is 1's
  305.  
  306. NrSides        db    ?
  307. NrTraks        db    ?
  308. SecsPerTrk    db    ?
  309.  
  310. Start    proc    near
  311.     xchg    BX,AX            ;save entering AL
  312.     string    CopyRite
  313.     int21    9
  314.     xchg    BX,AX            ;get back
  315.     cmp    AL,0FFH            ;donno WHAT this means!
  316.     jnz    Continue        ; yep (ff means bad)
  317.     string    BadDrMsg        ;tell the sad news
  318.     int21    9
  319.     mov    AH,8            ;return error code 8
  320.     int21    4CH            ;..and terminate
  321. Start    endp
  322.  
  323. Purge_Loop proc    near
  324.  
  325. Continue:
  326.     mov    AL,Fcb1            ;user-specified drive?
  327.     mov    CurDrv,AL        ;save it here for now
  328.     dec    AL            ;adjust
  329.     jns    Cont0            ; yep, got one
  330.     jmp    Hi_There        ; Go say what's happening and exit.
  331.  
  332. Cont0:    string    TellMeTwice
  333.     call    GetYes
  334.     je    Cont1            ; ok, do it
  335.     jmp    Exeunt            ; nope, exit
  336.  
  337. Cont1:    string    CrLfS            ;new line
  338.     int21    9
  339. ;;;;;    call    Set_Parms        ;set disk parms to fast values
  340.     call    Get_Drv            ;try to get drive/FAT stuff
  341.     call    Compute_Size        ;actual size of disk buffer
  342.     call    Update            ;update data string
  343.     string    PurgS            ;write the whole thing
  344.     int21    9
  345.  
  346. State_Lup:
  347.     xor    AX,AX            ;zeroes
  348.  
  349. Buffer_Lup:
  350.     mov    CurSid,AL        ;save bumped val
  351.     mov    BL,'0'
  352.     or    AL,BL            ;asciify
  353.     mov    CurSidA,AL        ;stuff in string
  354.     mov    CurTrk,AH
  355.     cmp    AL,BL            ;'0' = side 0?
  356.     je    Buffer1            ; yep, forget it
  357.     cmp    FillVal,FWORD        ;first cycle?
  358.     jne    Buffer1            ; nope, forget it
  359.     cmp    SecsPerTrk,9        ;already 9 or above?
  360.     jae    Buffer1            ; yep, forget it
  361.     mov    SecsPerTrk,9        ;force to 9 for 2d side
  362.     call    Compute_Size
  363.     call    Update
  364. Buffer1:
  365.     mov    Werra,'0'        ;write error
  366.     mov    Verra,'0'        ;verify error
  367.     string    SideS            ;'purging side %, fill value nn'
  368.     int21    9            ;display string
  369.  
  370. ; First try to write a full track.
  371.  
  372. One_Side_Lup:
  373.     call    W_Track            ;Write a track
  374.  
  375.     mov    AL,MaxTrkCnt        ;current count
  376.     mov    AH,BufTrkSiz        ;max tracks per buff
  377.     add    AL,AH            ;new target number
  378.     mov    MaxTrkCnt,AL        ;save again
  379.  
  380.     sub    AL,AH            ;put back
  381.     cmp    AL,NrTraks        ;done whole disk?
  382.     jb    One_Side_Lup        ;nope, next cycle
  383.  
  384. ; We've done side 0.  Now to reset for side 1.  We do one full side,
  385. ; and then the other for a reason:  If the disk format byte says
  386. ; double sided, but that back side is blasted or a different format ...
  387. ; at least the front side purge will go cleanly enough, and you can
  388. ; decide whether or not to sit through all the error messages on the other!
  389.  
  390.     mov    MaxTrkCnt,AH        ;reset starting track back to 10
  391.     mov    AL,CurSid
  392.     cmp    AL,NrSides        ;done all the sides?
  393.     je    NextFill        ; nope, greater
  394.     inc    AL
  395.     xor    AH,AH
  396.     jmp    short Buffer_Lup    ;go do side 1
  397.  
  398. NextFill:
  399.     call    Change_State        ;next state,please
  400.     cmp    AX,FSTATE        ;this time back to F's?
  401.     jne    State_Lup
  402.  
  403.     string    DonMsg            ;'Purge complete'
  404.     Int21    9            ;print string
  405. Anothr:    string    PurgNxtS        ;'purge another?'
  406.     call    GetYes
  407.     Int21    9
  408.     jne    Main_Exit
  409.     string    NewDiskS        ;'insert new disk...'
  410.     int21    9
  411.     mov    AH,1            ;1 = get kbd response
  412.     int21    0CH            ;get kbd response
  413.     jmp    Continue        ;restart with state of 1's again
  414.  
  415. Hi_There:
  416.     string    WhatItIs        ;'what this is is ...'
  417.     int21    9
  418.     jmp    Exeunt            ;die
  419.  
  420. Main_Exit:
  421. ;;;;;;    call    Set_Parms        ;put disk parms back to orig vals
  422. Exeunt:    xor    AL,AL            ;return error code 0 in any case
  423.     Int21    4CH            ;terminate
  424. Purge_Loop    endp
  425.  
  426. ; Change current fill value state in sequence 1's, 0's, random.
  427.  
  428. Change_It proc    near
  429. Change_State:
  430.     mov    BX,FillValA        ;get last time's state
  431. ;
  432.     mov    AX,ZSTATE        ;assume this time is 00's
  433.     xor    CX,CX            ;0's this time
  434.     cmp    BX,FSTATE        ;last time F's?
  435.     je    Change1            ; yep, do 0's
  436. ;
  437.     mov    AX,RSTATE        ;assume this time is RR
  438.     mov    CX,RWORD        ;special randomizer
  439.     cmp    BX,ZSTATE        ;last time 0's?
  440.     je    Change1            ; yep, time for random
  441. ;
  442.     mov    AX,FSTATE        ;assume this time is FF's
  443.     mov    CX,FWORD
  444. Change1:
  445.     mov    FillValA,AX        ;save this time's state
  446.     mov    FillVal,CX        ;and value
  447.     ret
  448. Change_it    endp
  449.  
  450. ; Updates display string of drive,track,sector data.
  451. ; Does NOT print it.
  452.  
  453. UpdateP    proc    near
  454. Update:
  455.     mov    AL,CurDrv        ;active drive
  456.     inc    AL            ;change 0-3 to 1-4
  457.     or    AL,'@'            ;Asciify it, no confusion
  458.     mov    DriveA,AL        ;stuff
  459.  
  460.     mov    AL,NrSides        ;nr sides
  461.     inc    AL            ;change 0-1 to 1-2
  462.     or    AL,'0'            ;make visible, no confusion
  463.     mov    SideCntA,AL        ;stuff
  464.  
  465.     mov    DI,offset TrkCntA    ;disk track Ascii val addr
  466.     mov    AL,NrTraks        ;disk tracks (2 digits)
  467.     inc    AL            ;so the 39 doesn't confuse them
  468.     call    Asciiz_99        ;make it ASCII
  469.  
  470.     mov    DI,offset SecCntA    ;might be more than 8 or 9
  471.     mov    AL,SecsPerTrk        ;nr sectors per track
  472.     call    Asciiz_99        ;stuff it
  473.     ret
  474. UpdateP    endp
  475.  
  476. ; Write a buffer full of tracks, then go right ahead and verify them.
  477. ; If Verify fails, we attempt to rewrite and verify until maxed out.
  478. ; If failures, a track by track report is made.
  479. ; Real spagetti code here, but it works.  YOU make it cleaner.
  480. ; Structured programming .. phoooey!
  481.  
  482. Write_It proc    near
  483. W_Track:
  484. ;    clc                ;NC = fill
  485.     mov    AX,FillVal
  486.     call    Fill_Buff        ;fill buffer with current fill val,
  487.                     ;initialize starting regs
  488. W_Trk_Lup:
  489.     string    WriteS            ;'Writing'
  490.     call    Post_Trk        ;display, plus track counters
  491.     
  492.     mov    AH,03H            ;write sectors
  493.     int    13H
  494.     call    Say_Secs        ;say how many recs written (adjusted)
  495.     jb    WrErr_Tst        ;had an error
  496.  
  497.     cmp    Werra,'0'        ;no errors to date?
  498.     je    W_Trk1            ; that's right
  499.     string    RetryMsg        ;'ok after retry.'
  500. W_Trk0:    int21    9
  501. W_Trk1:
  502.     mov    Werra,'0'        ;insure Ascii write err val is reset
  503.     call    Add_BX            ;bump buffer ptr n sectors worth
  504.     inc    CH            ;bump track
  505.     cmp    CH,MaxTrkCnt        ;done a buffer's worth of tracks?
  506.     jb    W_Trk_Lup        ; nope, loop
  507.     cmp    FillValA,'RR'        ;random? (no testing that!)
  508.     je    W_Trk2            ; yep, forget it
  509.     call    Verify            ; go verify what we wrote
  510.  
  511. W_Trk2:    cmp    VerrA,'0'        ;went ok?
  512.     jne    W_Trk_Lup        ; nope, try it again,
  513.                     ; maybe with reduced current track
  514.     mov    AH,MaxTrkCnt        ;the end track count
  515.     mov    CurTrk,AH        ;is now new starting track
  516.     jmp    short Wrt_Exit        ;return to disk loop
  517.  
  518. WrErr_Tst:
  519.     call    Disp_Err        ;display the error in AH
  520.     jnc    W_Trk_Lup        ; Write-prot or disk-removed, retry
  521.  
  522.     mov    DL,Werra        ;current Ascii write error val
  523.     inc    DL            ;bump ascii error cnt
  524.     int21    2            ;display the value
  525.     cmp    DL,'5'            ;maxed out?
  526.     jae    Wr_Maxed        ; yep, give it up on this track
  527.     mov    Werra,DL        ;save bumped value
  528.     jmp    W_Trk_Lup        ;try same track again
  529.  
  530. Wr_Maxed:
  531.     string    BadMsg            ;'still bad after 5 retries'
  532.     jmp    short W_Trk0        ;go back
  533.  
  534. Wrt_Exit:
  535.     string    CrLfS            ;force Lf for next cycle
  536.     int21    9
  537.     ret
  538. Write_It    endp
  539.  
  540. ; Wrote a buffer full to target disk.
  541. ; Now read it back to verify the write was correct.
  542. ; Protect CX, which has current track data.
  543.  
  544. Verify_It proc    near
  545. Verify:
  546.     mov    AX,FillVal
  547.     not    AX
  548.     call    Fill_Buff        ;prime buffer for read,
  549.                     ;initialize starting regs
  550. Ver_Lup:
  551.     string    VerifyS            ;'Verifying'
  552.     call    Post_Trk        ;display, plus track/error data
  553.  
  554.     mov    AH,02H            ;read sectors
  555.     int    13H
  556.     call    Say_Secs        ;show secs verified, won't hurt CF
  557.     jnc    Ver_0            ; no disk read error, fine
  558.  
  559.     call    Disp_Err        ;display the error in AH
  560.     string    VErrChkS        ;"will check buff anyway"
  561.     int21    9            ;display it, fall thru to Chek_Buff
  562.  
  563. Ver_0:    call    Chek_Buff        ;see how the read verifies
  564.     jnc    Ver_Ok            ; yep, just fine
  565.  
  566.     mov    DL,VerrA        ;snarf the Ascii verify error val
  567.     inc    DL            ;inx it
  568.     mov    VerrA,DL        ;save it
  569.     int21    2            ;display that char
  570.     cmp    DL,'5'            ;maxed out?
  571.     jb    Ver_Lup            ; nope, retry
  572.  
  573.     string    BadMsg            ;'still bad after 5 retries'
  574.     jmp    short Ver_1        ;give it up
  575.  
  576. Ver_Ok:    cmp    VerrA,'0'        ;no errors to date?
  577.     string    RetryMsg        ;'ok after retry'
  578.     jne    Ver_1            ; old errors, so skip the stroke
  579.     string    RdOkS            ;'read verified ok'
  580. Ver_1:    int21    9
  581.     mov    VerrA,'0'        ;clear the flag in any case
  582.     call    Add_BX            ;bump buffer pointer n sectors worth
  583.     inc    CH            ;bump track
  584.     cmp    CH,MaxTrkCnt        ;max for this buffer?
  585.     jb    Ver_Lup            ; nope, loop
  586.  
  587.     ret
  588. Verify_It    endp
  589.  
  590. ; Update our progress report with Ascii side and track values.
  591. ; Enters  with activity string address in DX ('Reading', 'Writing',
  592. ; or 'Verifying').
  593. ; Reloads DX with current drive/side information for the next read/write,
  594. ;      BX with current buffer pointer,
  595. ;      AL with nr of sectors to read/write.
  596.  
  597. Post_It    proc    near
  598. Post_Trk:
  599.     push    CX            ;save track/sector stuff
  600.     int21    9            ;print present activity in DX
  601.     mov    DI,offset CurTrkA    ;here's where the track goes
  602.     mov    AL,CH            ;current track
  603.     inc    AL            ;adjust
  604.     call    Asciiz_99        ;stuff in string
  605.     string    ErrStat            ;'track nn'
  606.     int21    9
  607.  
  608.     mov    AL,SecsPerTrk        ;nr of sectors/track
  609.     mov    BX,BuffPtr        ;get pointer (easier than
  610.                     ;always protecting BX)
  611.     mov    DX,DrvSidW        ;current drive/side
  612.     pop    CX            ;restore
  613.     ret
  614. Post_It        endp
  615.  
  616. ; Compute how big the buffer must be, using current disk format's
  617. ; sectors per track and the constant tracks per buffer.
  618. ; Update variable word BufferSiz.
  619.  
  620. Compute_It proc    near
  621. Compute_Size:
  622.     mov    AX,256            ;words/sector
  623.     xor    CX,CX            ;clear msb
  624.     mov    CL,SecsPerTrk        ;sectors/track
  625.     mul    CX            ;= words/track
  626.     mov    TrkSiz,AX        ;save that val
  627.     mov    CL,BufTrkSiz        ;max tracks/buffer
  628.     mul    CX            ;= bytes/this read/write
  629.     mov    BufferSiz,AX        ;save as new buffer size
  630.     ret
  631. Compute_It    endp
  632.  
  633. ; Bump our buffer pointer BX by the appropriate amount
  634. ; for this disk format (e.g., 512 bytes per sector for 8/9 sectors)
  635. ; each time we read/write a track.
  636.  
  637. Add_It    proc    near
  638. Add_BX:
  639.     mov    BX,BuffPtr        ;get previous value
  640.     mov    AX,TrkSiz        ;words per track
  641.     shl    AX,1            ;change to bytes
  642.     add    BX,AX            ;new buffer offset
  643.     mov    BuffPtr,BX        ;save new value, return new val in BX
  644.     ret
  645. Add_It    endp
  646.  
  647. ; Shows the returned sector count (after a track write or verify)
  648. ; as a double check of what's been accomplished.
  649.  
  650. Sector_It proc    near
  651. Say_Secs:
  652.     pushf                ;save flags a sec
  653.     mov    DI,offset SecRetA    ;'%% sectors reported'
  654.     mov    DX,DI            ;string offset
  655.     push    AX            ;save errors
  656.     call    Asciiz_99        ;asciify it
  657.     string    SecRetA
  658.     int21    9
  659.     pop    AX
  660.     popf                ;if this breaks your 80286,
  661.                     ;there's ways to replace it.
  662.                     ;don't break mine!
  663.     ret
  664. Sector_It    endp
  665.  
  666. ; Fill buffer with whatever our current fill activity is.
  667. ; No registers to preserve.
  668. ; Updates CX (track/sector) in each case.
  669.  
  670. Fill_It    proc    near
  671. Fill_Buff:
  672.     mov    DI,offset BigBuff
  673.     mov    CX,BufferSiz        ;256 words * 9 sectors max
  674.     cld                ;insure left to right
  675.     cmp    AX,RWORD        ; random?
  676.     je    Random            ; yep, go generate some
  677.  
  678.     rep stosw            ;fill with fill word
  679.     jmp    short Fill_Exit
  680.  
  681. ; From Prof. Arne Thesen et al, Univ of Wisconsin-Madison
  682. ; (but his paper had Fortran and Pascal .. ugh ..)
  683. ; I don't know how great this is for a random number generator,
  684. ; but it certainly is fast enough!
  685. ;CX=buffer size,
  686. ;AX=7FFFH
  687.  
  688. Random:    mov    BP,3993            ;suggested constant multiplier
  689.     mov    BX,AX            ;enters with 7FFFH in AX
  690.     mov    AX,SeedInt        ;get seed.int
  691. Rnd_Lup:
  692.     mul    BP            ;seed.int = multiplier * seed.int
  693.     add    AX,1            ;..+1
  694.     mov    DL,AH            ;rbyte (only use msb)
  695.     jns    Rnd1            ;if seed.int < 0 .. nope, skip
  696.     add    AX,BX            ;seed.int = seed.int + maxint...
  697.     inc    AX            ;...+1
  698. Rnd1:    mul    BP            ;again
  699.     add    AX,1
  700.     mov    DH,AH            ;the other rbyte
  701.     jns    Rnd2
  702.     add    AX,BX
  703.     inc    AX
  704. Rnd2:    xchg    DX,AX            ;get the Rword
  705.     stosw
  706.     loop    Rnd_Lup            ;until done
  707.     mov    SeedInt,DX        ;save the seed.int
  708.  
  709. Fill_Exit:                ;load starting parms
  710.     mov    CX,TrkSecW        ;starting track,sector 1
  711.     mov    BX,offset BigBuff    ;point to buffer
  712.     mov    BuffPtr,BX
  713.     ret
  714. Fill_It        endp
  715.  
  716. ; Verifying each track (as I did in the first version of PURGE)
  717. ; doesn't hack it .. only shows disk errors or CRC errors,
  718. ; NOT that we successfully wrote as planned.  So we do it the hard way.
  719.  
  720. ; Purge the buffer, read in a buffer full, call this procedure.
  721. ; This scans the buffer (starting at the current track offset)
  722. ; for one track's worth.  It scans for the current fill value,
  723. ; reporting success or failure as appropriate.
  724.  
  725. ; This is useless for random writes, of course, but plan on doing something
  726. ; clever to see if the buffer has big chunks of the previous fill (0's).
  727.  
  728. Check_It proc    near
  729. Chek_Buff:
  730.     push    CX
  731.     mov    DI,Buffptr        ;ES:DI current buffer psn
  732.     mov    CX,TrkSiz        ;words in one track
  733.     mov    AX,FillVal        ;current fill value
  734. ChekLup:
  735.     scasw
  736.     jne    Bad_Chk
  737.     loop    ChekLup
  738.     clc                ;say ok
  739.     jmp    short Chek_Exit
  740.  
  741. Bad_Chk:
  742.     string    RdFailS            ;'failed read verify..'
  743.     int21    9
  744.     stc                ;CF set to show failure
  745. Chek_Exit:
  746.     pop    CX
  747.     ret
  748. Check_It    endp
  749.  
  750. ; Process errors from ROM-BIOS call.
  751. ; Error arrives in AH.
  752. ;    1 = bad command
  753. ;    2 = address mark not found
  754. ;    3 = write attempted on write-protected disk
  755. ;    4 = sector not found
  756. ;    6 = diskette removed
  757. ;    8 = DMA overrun
  758. ;    9 = DMA across 64Kb boundary
  759. ;    10H = bad CRC
  760. ;    20H = NEC controller failed
  761. ;    40H = seek failed
  762. ;    80H = time out
  763.  
  764. Disk_Error proc    near
  765. Disp_Err:
  766.     push    CX            ;track/sector stuff
  767.     push    DX            ;head/drive
  768.  
  769.     xchg    BX,AX            ;save that error val
  770.     
  771.     mov    DI,offset DerrA        ;point to where we stuff it
  772.     mov    AL,AH            ;Asciize wants it in AL
  773.     call    Asciiz_255        ;stuff the error in AL
  774.     string    ErrorS            ;'Error nnn'
  775.     int21    9            ;print char
  776.     dskint    0            ;reset drive
  777.  
  778.     xchg    BX,AX            ;get error back
  779.  
  780.     xor    BX,BX            ;clear BX
  781.     cmp    AH,80H            ;max allowable error
  782.     ja    Disp2            ;bogus, treat as 0
  783.     mov    BL,AH            ;here's the raw error
  784.     cmp    AH,9            ;1..9H?
  785.     jbe    Disp2            ; yes, fine
  786.     mov    BL,9            ; nope, start at 9 (+inc)
  787.  
  788. Disp1:    inc    BL            ;bump processed counter
  789.     cmp    AH,10H            ;raw error 10H or above?
  790.     jbe    Disp2            ; all adjusted, done
  791.     shr    AH,1            ; divide adjusted error in half
  792.     jmp    short Disp1        ;loop and try again
  793.  
  794. ; Error checking here isn't real strong .. we'd better hope a fairly legal
  795. ; error number comes in here, or no telling WHERE our table offset
  796. ; will end up!
  797.  
  798. Disp2:    mov    SI,offset ErAdrTbl    ;header of error msg address table
  799.     shl    BL,1            ;*2 for word addresses
  800.     add    SI,BX            ;add in offset
  801.     mov    DX,[SI]
  802.     push    AX            ;save processed error
  803.     int21    9            ;print it
  804.     string    CrLfS            ;and terminate with Cr/Lf
  805.     int21    9
  806.     pop    AX            ;restore processed error
  807.  
  808.     cmp    AH,2            ;no address (e.g., unformatted)
  809.     jb    Disp4            ;forget it
  810.     jne    Disp2a            ; maybe larger
  811.     cmp    NrSides,1        ;are we set as a single-sided disk?
  812.     jb    Disp4            ; Yep, no use trying to switch
  813.     string    TrySS            ;'will try single-sided'
  814.     int21    9
  815.     mov    NrSides,0        ;hope this works
  816.     call    Update            ;post the strings
  817.     clc                ;so we retry
  818.     jmp    short Disp4        ;exit
  819.  
  820. Disp2a:    cmp    AH,3            ;write-protected?
  821.     jb    Disp4            ; Nope
  822.     cmp    AH,4            ;sector not found?
  823.     jne    Disp2b            ; nope
  824.     string    Try8S            ;'will try 8-sector format'
  825.     int21    9
  826.     mov    SecsPerTrk,8        ;try 8-sector format
  827.     call    Update
  828.     call    Compute_Size        ;gotta recompute this
  829.     clc                ;to attempt retry
  830.     jmp    short Disp4        ;exit
  831.  
  832. Disp2b:    cmp    AH,6            ;diskette removed?
  833.     ja    Disp4            ; nope
  834.     mov    AH,1            ;1 = get kbd response
  835.     int21    0CH            ;get kbd response
  836.  
  837. Disp4:    pop    DX            ;restore regs
  838.     pop    CX
  839.     ret
  840. Disk_Error    endp
  841.  
  842. ; Get the specifics about the target disk.  I can NOT test the
  843. ; AT portion of this code (since I ain't got one).
  844. ; Normal PC disk formats work just fine (8 track SS, 9 track DSDD, etc.)
  845.  
  846. Drive_It proc    near
  847. Get_Drv:
  848.     mov    DL,CurDrv        ;user-specified drive
  849.     dec    DL            ;A or B?
  850.     jns    Drv_Ok            ; fine
  851.     string    FlopOnly        ;'floppies only, Clyde'
  852.     int21    9
  853.     inc    AH            ;1 = get kbd response
  854.     int21    0CH            ;get kbd response
  855.     xor    DL,DL            ;sigh... force A
  856.  
  857. Drv_Ok:    mov    CurDrv,DL        ;save drive ID
  858.     inc    DL            ;adjust for...
  859.     int21    1CH            ;get FAT information, any drive
  860.     mov    AL,[BX]            ;FAT ID byte (blows away DS!)
  861.  
  862. ; Should be returning
  863. ;    CX = bytes per sector
  864. ;    AL = sectors per allocation unit
  865. ;    DX = nr of allocated units
  866. ;    Norton's wrong!  A typo! should be
  867. ;    DS:BX = pointer to FAT ID byte
  868. ;    So DX has nr of allocated units
  869.  
  870.     xor    AH,AH            ;clear msb
  871.     mov    DX,CS            ;restore ...
  872.     mov    DS,DX            ;DS=CS
  873.  
  874.     mov    DI,offset FAT_ID_Table    ;lookup table
  875.     mov    CX,5            ;5 FAT file IDs
  876.     cld
  877.     repnz scasb            ;search
  878.     jz    Do_Fat_Table        ;found it
  879.  
  880.     cmp    AL,ErMsgTblSiz        ;run off the table?
  881.     jne    Bad_Drv            ;bad/unknown FAT
  882.     mov    AL,CurDrv        ;let's see if it's an AT
  883.     sub    AL,2            ;???
  884.     add    AL,80H
  885.     mov    CurDrv,AL
  886.     mov    DL,AL            ;this drive
  887.     dskint    8            ;AT Get drive parameters
  888.     jb    Bad_Drv            ;failed
  889.  
  890.     mov    NrSides,DH        ;nr of sides
  891.     mov    AL,CL            ;max nr sectors
  892.     and    AL,3FH            ;mask to insure legal?
  893.     mov    SecsPerTrk,AL        ;sectors/track
  894.     mov    AL,CH            ;max nr tracks
  895.     mov    AH,CL            ;compute
  896.     mov    CL,6            ;divide by 64
  897.     shr    AH,CL
  898.     mov    NrTraks,AL        ;save as disk tracks
  899.     jmp    Got_Drv
  900.  
  901. Bad_Drv: string    BadFatS            ;'gonna assume DS9'
  902.     int21    9            ;display it
  903.     sub    DI,2            ;that'll make it DS9
  904.                     ;... and fall thru to
  905.  
  906. ; Working with a known FAT ID byte from our table.
  907. ; DI has where we found the byte
  908.  
  909. Do_Fat_Table:
  910.     sub    DI,offset FAT_ID_Table+1 ;correct to table offset
  911.  
  912. ; First we load our report string with the FAT ID byte data and report that.
  913. ; Then we force double-sided, 9-sector (if not doing an AT 15-sector)
  914. ; because it could have been formatted differently before, and there's
  915. ; data there!
  916. ; If it fails during the write, we'll reset back to 1-sided and/or 8-sector.
  917.  
  918.     mov    AL,Side_Table[DI]
  919.     mov    NrSides,AL        ;save nr sides
  920.     mov    AL,Trak_Table[DI]    ;nr tracks
  921.     mov    NrTraks,AL        ;save as nr of tracks
  922.     mov    AL,Sec_Table[DI]    ;nr sectors/track
  923.     mov    SecsPerTrk,AL        ;save it
  924.  
  925. Got_Drv:string    FormatS            ;'looks like...'
  926.     int21    9
  927.     call    Update            ;update per disk data
  928.     string    SideCntA        ;'% sides, %% tracks...'
  929.     int21    9
  930.     mov    NrSides,1        ;force to 2-sided
  931.     mov    AL,SecsPerTrk
  932.     cmp    AL,9            ;leave 9 & above alone
  933.     jae    Got_Drv1        ; ok
  934.     mov    AL,9            ;force to 9
  935.     mov    SecsPerTrk,AL
  936. Got_Drv1:
  937.     call    Update
  938.     string    GonnaTryS
  939.     int21    9
  940.     string    SideCntA
  941.     int21    9
  942.     clc                ;clear CF to say ok
  943.     ret
  944. Drive_It    endp
  945.  
  946. GetYesP proc    near
  947. GetYes:    int21    9            ;print string in DX
  948.     mov    AX,0C01H        ;get kbd response
  949.     int    21H
  950.     or    AL,' '            ;lowercase it
  951.     cmp    AL,'y'
  952.     ret                ;with ZF set or clear
  953. GetYesP    endp
  954.  
  955. ; If a value larger than a single digit, use this to change to Ascii.
  956. ; Enters with value in AL, and DI pointing to the target address.
  957. ; Will work with up to 255, but you'd better have room reserved!
  958. ; From Dr Dobbs, Apr 86
  959.  
  960. Asciize_It proc    near
  961. Asciiz_255:
  962.     add    di,2            ;point to low digit
  963.     std                ;set for hi to lo store
  964.     aam                ;convert low order digit
  965.     or    al,'0'            ;make it ascii
  966.     stosb                ;and store low digit
  967.     mov    al,ah            ;load high part
  968.     aam                ;convert high and middle digit
  969.     or    ax,'00'            ;make them ascii
  970.     stosb                ;store middle digit
  971.     mov    al,ah            ;high digit
  972.     stosb                ;store high digit
  973.     cld                ;restore direction flag
  974.     ret
  975.  
  976. Asciiz_99:
  977.     aam                ;convert to 2 digits
  978.     or    ax,'00'            ;make them ascii
  979.     xchg    ah,al            ;the low one
  980.     stosb                ;store middle digit
  981.     mov    al,ah
  982.     stosb                ;store high digit
  983.     ret
  984. Asciize_It    endp
  985.  
  986. ; Access the current disk parameter block, set settle-time
  987. ; and motor wait.  (Again, from Weissman's article in Dr. Dobbs.)
  988. ; Uses variable Times for initial values, and to store original values.
  989. ; (Each time you call this, you in effect toggle the times.)
  990. ; Sets disk delay times to shorter values if starting,
  991. ; else resets back to original values saved in Times.
  992.  
  993. Set_Disk proc    near
  994. Set_Parms:
  995.     push    DS
  996.     xor    BX,BX
  997.     mov    DS,BX
  998.     assume    DS:Sys0
  999.  
  1000.     lds    bx,Disk_Ptr
  1001.     assume    DS:Cseg
  1002.     mov    AX,Times        ;new settle (AL) and wait (AH) times
  1003.     xchg    [bx].DiskTimes,AX    ;move the whole word
  1004.     mov    Times,AX        ;save old times
  1005.     pop    DS
  1006.     ret
  1007. Set_Disk    endp
  1008.  
  1009. BigBuff    LABEL    BYTE
  1010.  
  1011. ; Buffer from here to end of segment.
  1012. ; No, I have NOT protected the stack...
  1013. ; This thing works now, and there IS room for everything.
  1014. ; I stayed with just reading in 10 tracks at a time,
  1015. ; assuming the 512-byte sectors, 9-sectors/track.
  1016. ; It MAY blow up with 15-sector stuff.
  1017.  
  1018. ; There are ways to handle really big buffers, other segments, etc.,
  1019. ;  but I wanted to keep this simple and not mess with ESs and DSs
  1020. ;  too much.  Also didn't want an EXE file.
  1021.  
  1022. ; We put this stuff here since it's only used at startup.
  1023. ; It gets overwritten when the BigBuff fires up.
  1024.  
  1025. CopyRite db    Lf,Tab,Tab,'TOADPURG Diskette Purge Utility V1.2',Cr,Lf
  1026.     db    Tab,Tab,'Copyright (C) 1986 David P Kirschbaum',Cr,Lf
  1027.     db    Tab,Tab,'Toad Hall  All Rights Reserved',Cr,Lf
  1028.     db    Tab,Tab,'Copy permitted for non-sale purposes.'
  1029.     db    Cr,Lf,'$'
  1030.  
  1031. TellMeTwice db    Cr,Lf,Tab,Tab,Tab,'Tell-Me-Twice...',Bell,Cr,Lf
  1032.     db    Tab,Tab,'This TOTALLY destroys ALL data on the disk!',Cr,Lf
  1033.     db    Tab,Tab,Tab,'Continue?  (Y/N):  $'
  1034.  
  1035. WhatItIs db Tab,'Purges floppy diskettes of potential sensitive data',Cr,Lf
  1036.  db    Tab,'by overwriting ALL disk tracks as specified by the',Cr,Lf
  1037.  db    Tab,'National Security Agency (NSA).',Cr,Lf,Lf
  1038.  db    Tab,'To use, enter at the command line:',Cr,Lf
  1039.  db    Tab,"TOADPURG A:  (or B:)    (no C's, D's, etc.!)",Cr,Lf,Lf
  1040.  db    Tab,'This action is irrevocable!',Cr,Lf
  1041.  db    Tab,"You can break the program with a Ctrl C, but it won't help",Cr,Lf
  1042.  db    Tab,'since the FAT and disk catalog are the first to go!',Cr,Lf,Lf
  1043.  db    Tab,'Redirect the screen display to a printer or file with:',Cr,Lf
  1044.  db    Tab,'TOADPURG A: >LPT1    or',Cr,Lf
  1045.  db    Tab,'TOADPURG B: >PURGE.LOG',Cr,Lf,Lf
  1046.  db    Tab,'Written for the US Navy (and the SEALs) as a public service.'
  1047.  db    Cr,Lf,'$'
  1048.  
  1049. Purg    Endp
  1050. Cseg    Ends
  1051.     End    Purg
  1052.