home *** CD-ROM | disk | FTP | other *** search
- TITLE TOADPURG v1.2 20 May 86
- ; Floppy disk purge program.
- ; Copyright (C) 1986 David P Kirschbaum All Rights Reserved
- ; Toad Hall
- ; 7573 Jennings Lane
- ; Fayetteville NC 28303
- ; tel (919) 868-3471
- ; ARPAnet ABN.ISCAMS@USC-ISID
-
- ; Copying permitted for all non-sales applications.
- ; Copyright information and all credits must remain with the code.
-
- ; This means if you've figured a way to sell it,
- ; I want a share!
-
- ; Credits:
- ; Portions of this code (or the idea anyway) from VERDISK.COM.
- ; BIOS disk call speedup from "Speeding MS DOS" by Gregg Weissman,
- ; Dr Dobbs, Mar 86
-
- ; Program Justification and Use:
- ;
- ; NSA and other security agencies specify the method of erasing
- ; sensitive data from floppy (and other) disks. Similar to core memory
- ; purging, you must write each track with 1's, then with 0's, then with
- ; random data. (They really meant binary 1's.)
-
- ; Each track write must be confirmed with a track read to insure the actual
- ; data was written. (No, a disk or track verify is NOT sufficient since
- ; that only does a CRC of the data to insure any data there is not damaged.
- ; It does NOT compare with a buffer read and verify.)
- ;
- ; NSA recommends you use a debugger to physically look at the data
- ; on each track. I did that plenty during the debugging of this program.
- ; If you don't get any error reports, you can rest assured that disk gets
- ; written with 1's, then 0's, then random.
- ; If you want to check for yourself, break it with a Ctrl C part way through,
- ; and look at the parts that have been purged.
-
- ; The TOADPURGE Process:
- ;
- ; It uses a large memory buffer, which it fills with the required
- ; fill data (1's, 0's, or random).
- ; It then writes the full buffer to disk, one track at a time (processing
- ; 10 tracks in sequence for efficiency).
- ; After each buffer write, it purges the memory buffer by filling it with
- ; the OTHER fill data (1's and 0's only).
- ; It then physically reads the same tracks to the buffer, and scans that
- ; buffer to insure EVERY byte matches the current fill data.
-
- ; Diskette Formats:
- ;
- ; TOADPURGE uses the FAT disk format byte from the disk to obtain the
- ; floppy diskette format. If that byte is not standard, TOADPURGE will
- ; assume a 9-sector track format and try the purge that way.
-
- ; If the disk ID byte says 8-sector tracks, TOADPURGE will try to force
- ; to 9-sector. If 9-sector fails, TOADPURGE will drop back to 8-sector
- ; and try that way.
- ;
- ; Even if the disk format byte says single-sided, TOADPURGE will try to
- ; purge the other side (just in case any data from a previous format remains).
- ; The second side is also forced to 9-sector format (even if side 0 proved
- ; to be 8-sector format) just in case.
-
- ; Disk Errors:
- ;
- ; TOADPURGE will report errors as they occur:
- ; If a write error, attempt to write that track up to 5 times.
- ; If a read error, revert back to write mode and try that track again.
- ; If trying to force double-sided purging on a single-sided disk,
- ; and significant errors occur on the "back side", TOADPURGE will
- ; give up and revert to single-sided purging.
-
- ; Diskette formats supported:
- ;
- ; S8, D8, S9, D9, QD15 (untested).
-
- ; Drives supported:
- ;
- ; TOADPURGE supports A and B drives as specified from the command line.
- ; If a drive is not given, TOADPURGE explains what's happening
- ; and exits.
- ; If the user attempts to command other than A or B Drives, TOADPURGE
- ; presents a warning ('No way, Clyde') and reverts to A drive.
-
- ; System requirements:
- ;
- ; Uses PC-DOS ROM-BIOS Interrupt 13H (absolute disk read/write).
- ; Could be rewritten to DOS interrupts 25H and 26H easily enough...
- ; I just hated all the absolute disk address conversion math.
- ; Also uses disk parameters at base memory for the disk delay speedup,
- ; but this should be common to all DOS systems.
-
- ; Redirection:
- ; If you wish redirection of the screen reports to printer or file,
- ; you can do that just as always. Only problem is .. the fancy screen
- ; display uses isolated carriage returns, so your file may need some
- ; processing in a text editor. If going to a printer, make sure your
- ; printer is set to convert isolated carriage returns into the Cr/Lf
- ; combination.
-
- ; Improvements:
- ; Plans for a "silent" mode are in the workings ... doesn't do fancy
- ; screen stuff, just writes a nice neat formatted report, date/time
- ; stamped, to the standard I/O (screen, printer, file).
-
-
- int21 MACRO function ; Call the DOS interrupt
- mov ah,function ; Put function number in AH
- int 21h
- ENDM
-
- dskint MACRO function ; Call the PC disk interrupt
- mov AH,function ; put function number in AH
- int 13H ; PC interrupt
- ENDM
-
- string MACRO msg
- mov DX,offset msg
- ENDM
-
- Bell equ 07H ;beep
- Tab equ 09H
- Cr equ 0DH
- Lf equ 0AH
- AscMask equ 2710H ;constant for Asciizing numbers
- FWORD equ 0FFFFH
- RWORD equ 07FFFH
-
- FSTATE equ 'FF'
- RSTATE equ 'RR'
- ZSTATE equ '00'
-
- ; Structure of BIOS diskette parameters
- ; (from "Speeding MS DOS" by Gregg Weissman, Dr Dobbs, Mar 86)
- ; When we reset the drive delay parameters to the faster values suggested
- ; in the referenced article, the purge time for a D9 disk decreases from
- ; 2:43 to 2:26. Donno if it's worth the hassle or not, but it's here for
- ; your edification.
-
- ; Only problem is .. if you break this program with a Ctrl C, I don't
- ; exactly know WHAT happens to the disk parameter table values maintained
- ; by DOS. It appears they don't get corrected by anything short of a reboot,
- ; and many programs can have problems.
-
- ; For this reason, the drive delay speedup has NOT been engaged in this
- ; version.
-
- ; The code is still there, though. Set_Parms is what does it.
-
- DiskParms struc
- Spec1 db ?
- Spec2 db ?
- Spec3 db ?
- Spec4 db ?
- Spec5 db ?
- Spec6 db ?
- Spec7 db ?
- Spec8 db ?
- Spec9 db ?
- ;Head_Settle db ?
- ;Motor_Wait db ?
- DiskTimes dw ?
- DiskParms ends
-
- ; Define location of the pointer to the parameters
- Sys0 Segment at 0000
- org 78H
- Disk_Ptr label dword
- Sys0 ends
-
- CSeg Segment Public Para 'CODE'
- Assume CS:Cseg,DS:Cseg,ES:CSeg
-
- Org 5CH ;FCB1
- Fcb1 label BYTE
-
- Org 6CH ;FCB2
- Fcb2 label BYTE
-
- Org 100H
-
- Purg proc Far
- jmp Start
-
- FAT_ID_Table db 0FFH ;D8 FAT IDs
- db 0FEH ;S8
- db 0FDH ;D9
- db 0FCH ;S9
- db 0F9H ;QD15 !!
-
- ; Different FAT ID byte disk formats
-
- Side_Table db 1 ;D8 nr sides
- db 0 ;S8
- db 1 ;D9
- db 0 ;S9
- db 1 ;QD15
-
- Trak_Table db 27H ;D8 nr tracks
- db 27H ;S8
- db 27H ;D9
- db 27H ;S9
- db 4FH ;QD15
-
- Sec_Table db 8 ;D8 sectors per track
- db 8 ;S8
- db 9 ;D9
- db 9 ;S9
- db 0FH ;QD15
-
- ErMsgTblSiz db 0F8H
-
- Err0S db 'ToadPurg V1.2, Toad Hall, 20 May 86'
- Err1S db 'Bad command$' ;1
- Err2S db 'Address mark not found$' ;2
- Err3S db 'Write attempted on ',Cr,Lf ;3
- db 'write-protected disk. '
- db 'Correct and hit any key to continue.$'
- Err4S db 'Sector not found$' ;4
- Err5S db 'Unknown Error$' ;no 5
- Err6S db 'Diskette removed$' ;6
- Err7S db 'Unknown Error$' ;no 7
- Err8S db 'DMA overrun$' ;8
- Err9S db 'DMA across 64Kb boundary$' ;9
- Err10S db 'Bad CRC$' ;10H
- Err11S db 'NEC controller failed$' ;20H
- Err12S db 'Seek failed$' ;40H
- Err13S db 'NEC Controller time out$' ;80H
- Err14S db 'Table overrun$'
- ErAdrTbl dw Err0S,Err1S,Err2S,Err3S,Err4S,Err5S,Err6S
- dw Err7S,Err8S,Err9S,Err10S,Err11S,Err12S,Err13S
- dw Err14S
-
- BadDrMsg db Cr,Lf,'Invalid drive specified.',Cr,Lf,'$'
- BadFatS db Cr,Lf,'Error: disk not supported, or invalid FAT ID found.'
- db Cr,Lf,'Will attempt to purge as a DS9 format.',Cr,Lf,Lf,'$'
- Try8S db "9-sector tracks failed. Switching to 8...",Cr,Lf,'$'
- TrySS db "Double-sided failed. Switching to single...",Cr,Lf,'$'
- FlopOnly db Cr,Lf,'This program purges FLOPPY DISKS ONLY!'
- db Cr,Lf,'(See the author for the special hard disk version.)'
- db Cr,Lf,'Forcing to Drive A:',Cr,Lf,'$'
-
- FormatS db 'Format reported as $'
- GonnaTryS db ' Attempting $'
- PurgS db Cr,'Purging '
- DriveA db 'n Drive, '
- SideCntA db '% side(s), '
- TrkCntA db '%% tracks, '
- SecCntA db '%% sectors per track.',Cr,Lf,'$'
- WriteS db Cr,' Writing$'
- VerifyS db Cr,'Verifying$'
- ErrStat db ' track '
- CurTrkA db '%%, $'
- SecRetA db '%% sectors returned$'
-
- ErrorS db ', Error: '
- DerrA db '%%%, $'
- WerrA db '%$'
- VerrA db '%$'
-
- SideS db 'Processing side '
- CurSidA db '%, Purge Write value: '
- FillValA dw FSTATE ;start fill state is FF
- CrLfS db Cr,Lf,'$'
-
- VErrChkS db Cr,Lf,'Will attempt to verify the read...$'
- RdOkS db ', read verify ok.$'
- RdFailS db ', read verify FAILED!',Cr,Lf,'$'
- RetryMsg db ', OK on retry. ',Cr,Lf,'$'
- BadMsg db ', still bad after 5 retries.',Cr,Lf,'$'
-
- DonMsg db Cr,Lf,'Purgdisk done.$'
- PurgNxtS db Cr,Lf,'Purge another? (Y/N): $'
- NewDiskS db Cr,Lf,'Insert new disk...$'
-
- ; "Constants"
-
- BufTrkSiz db 10 ;default max of 10 tracks in buffer
- MaxTrkCnt db 10 ;target track count when using buffer
- BufferSiz dw ? ;512*9*5 worst case,
- ;256 words * 9 sectors max
-
- TrkSiz dw ? ;words in one full track
-
- DrvSidW LABEL word ;for loading DX
- CurDrv db ? ;uninitialized drive number
- CurSid db 0 ;start with side 0
- TrkSecW LABEL word ;for loading CX
- StartSec db 1 ;this is constant for CL (Sector Nr)
- CurTrk db 0 ;current track for CH, init to 0
-
- BuffPtr dw ? ;buffer pointer
-
- SeedInt dw ?
-
- ;for fast disk parameters
-
- Times label word
- HeadSettle db 1 ;millisecond, down from 15 in DOS 3.1
- MotorWait db 15 ;1/8ths of a second, down from 25
-
- FillVal dw FWORD ;first time is 1's
-
- NrSides db ?
- NrTraks db ?
- SecsPerTrk db ?
-
- Start proc near
- xchg BX,AX ;save entering AL
- string CopyRite
- int21 9
- xchg BX,AX ;get back
- cmp AL,0FFH ;donno WHAT this means!
- jnz Continue ; yep (ff means bad)
- string BadDrMsg ;tell the sad news
- int21 9
- mov AH,8 ;return error code 8
- int21 4CH ;..and terminate
- Start endp
-
- Purge_Loop proc near
-
- Continue:
- mov AL,Fcb1 ;user-specified drive?
- mov CurDrv,AL ;save it here for now
- dec AL ;adjust
- jns Cont0 ; yep, got one
- jmp Hi_There ; Go say what's happening and exit.
-
- Cont0: string TellMeTwice
- call GetYes
- je Cont1 ; ok, do it
- jmp Exeunt ; nope, exit
-
- Cont1: string CrLfS ;new line
- int21 9
- ;;;;; call Set_Parms ;set disk parms to fast values
- call Get_Drv ;try to get drive/FAT stuff
- call Compute_Size ;actual size of disk buffer
- call Update ;update data string
- string PurgS ;write the whole thing
- int21 9
-
- State_Lup:
- xor AX,AX ;zeroes
-
- Buffer_Lup:
- mov CurSid,AL ;save bumped val
- mov BL,'0'
- or AL,BL ;asciify
- mov CurSidA,AL ;stuff in string
- mov CurTrk,AH
- cmp AL,BL ;'0' = side 0?
- je Buffer1 ; yep, forget it
- cmp FillVal,FWORD ;first cycle?
- jne Buffer1 ; nope, forget it
- cmp SecsPerTrk,9 ;already 9 or above?
- jae Buffer1 ; yep, forget it
- mov SecsPerTrk,9 ;force to 9 for 2d side
- call Compute_Size
- call Update
- Buffer1:
- mov Werra,'0' ;write error
- mov Verra,'0' ;verify error
- string SideS ;'purging side %, fill value nn'
- int21 9 ;display string
-
- ; First try to write a full track.
-
- One_Side_Lup:
- call W_Track ;Write a track
-
- mov AL,MaxTrkCnt ;current count
- mov AH,BufTrkSiz ;max tracks per buff
- add AL,AH ;new target number
- mov MaxTrkCnt,AL ;save again
-
- sub AL,AH ;put back
- cmp AL,NrTraks ;done whole disk?
- jb One_Side_Lup ;nope, next cycle
-
- ; We've done side 0. Now to reset for side 1. We do one full side,
- ; and then the other for a reason: If the disk format byte says
- ; double sided, but that back side is blasted or a different format ...
- ; at least the front side purge will go cleanly enough, and you can
- ; decide whether or not to sit through all the error messages on the other!
-
- mov MaxTrkCnt,AH ;reset starting track back to 10
- mov AL,CurSid
- cmp AL,NrSides ;done all the sides?
- je NextFill ; nope, greater
- inc AL
- xor AH,AH
- jmp short Buffer_Lup ;go do side 1
-
- NextFill:
- call Change_State ;next state,please
- cmp AX,FSTATE ;this time back to F's?
- jne State_Lup
-
- string DonMsg ;'Purge complete'
- Int21 9 ;print string
- Anothr: string PurgNxtS ;'purge another?'
- call GetYes
- Int21 9
- jne Main_Exit
- string NewDiskS ;'insert new disk...'
- int21 9
- mov AH,1 ;1 = get kbd response
- int21 0CH ;get kbd response
- jmp Continue ;restart with state of 1's again
-
- Hi_There:
- string WhatItIs ;'what this is is ...'
- int21 9
- jmp Exeunt ;die
-
- Main_Exit:
- ;;;;;; call Set_Parms ;put disk parms back to orig vals
- Exeunt: xor AL,AL ;return error code 0 in any case
- Int21 4CH ;terminate
- Purge_Loop endp
-
- ; Change current fill value state in sequence 1's, 0's, random.
-
- Change_It proc near
- Change_State:
- mov BX,FillValA ;get last time's state
- ;
- mov AX,ZSTATE ;assume this time is 00's
- xor CX,CX ;0's this time
- cmp BX,FSTATE ;last time F's?
- je Change1 ; yep, do 0's
- ;
- mov AX,RSTATE ;assume this time is RR
- mov CX,RWORD ;special randomizer
- cmp BX,ZSTATE ;last time 0's?
- je Change1 ; yep, time for random
- ;
- mov AX,FSTATE ;assume this time is FF's
- mov CX,FWORD
- Change1:
- mov FillValA,AX ;save this time's state
- mov FillVal,CX ;and value
- ret
- Change_it endp
-
- ; Updates display string of drive,track,sector data.
- ; Does NOT print it.
-
- UpdateP proc near
- Update:
- mov AL,CurDrv ;active drive
- inc AL ;change 0-3 to 1-4
- or AL,'@' ;Asciify it, no confusion
- mov DriveA,AL ;stuff
-
- mov AL,NrSides ;nr sides
- inc AL ;change 0-1 to 1-2
- or AL,'0' ;make visible, no confusion
- mov SideCntA,AL ;stuff
-
- mov DI,offset TrkCntA ;disk track Ascii val addr
- mov AL,NrTraks ;disk tracks (2 digits)
- inc AL ;so the 39 doesn't confuse them
- call Asciiz_99 ;make it ASCII
-
- mov DI,offset SecCntA ;might be more than 8 or 9
- mov AL,SecsPerTrk ;nr sectors per track
- call Asciiz_99 ;stuff it
- ret
- UpdateP endp
-
- ; Write a buffer full of tracks, then go right ahead and verify them.
- ; If Verify fails, we attempt to rewrite and verify until maxed out.
- ; If failures, a track by track report is made.
- ; Real spagetti code here, but it works. YOU make it cleaner.
- ; Structured programming .. phoooey!
-
- Write_It proc near
- W_Track:
- ; clc ;NC = fill
- mov AX,FillVal
- call Fill_Buff ;fill buffer with current fill val,
- ;initialize starting regs
- W_Trk_Lup:
- string WriteS ;'Writing'
- call Post_Trk ;display, plus track counters
-
- mov AH,03H ;write sectors
- int 13H
- call Say_Secs ;say how many recs written (adjusted)
- jb WrErr_Tst ;had an error
-
- cmp Werra,'0' ;no errors to date?
- je W_Trk1 ; that's right
- string RetryMsg ;'ok after retry.'
- W_Trk0: int21 9
- W_Trk1:
- mov Werra,'0' ;insure Ascii write err val is reset
- call Add_BX ;bump buffer ptr n sectors worth
- inc CH ;bump track
- cmp CH,MaxTrkCnt ;done a buffer's worth of tracks?
- jb W_Trk_Lup ; nope, loop
- cmp FillValA,'RR' ;random? (no testing that!)
- je W_Trk2 ; yep, forget it
- call Verify ; go verify what we wrote
-
- W_Trk2: cmp VerrA,'0' ;went ok?
- jne W_Trk_Lup ; nope, try it again,
- ; maybe with reduced current track
- mov AH,MaxTrkCnt ;the end track count
- mov CurTrk,AH ;is now new starting track
- jmp short Wrt_Exit ;return to disk loop
-
- WrErr_Tst:
- call Disp_Err ;display the error in AH
- jnc W_Trk_Lup ; Write-prot or disk-removed, retry
-
- mov DL,Werra ;current Ascii write error val
- inc DL ;bump ascii error cnt
- int21 2 ;display the value
- cmp DL,'5' ;maxed out?
- jae Wr_Maxed ; yep, give it up on this track
- mov Werra,DL ;save bumped value
- jmp W_Trk_Lup ;try same track again
-
- Wr_Maxed:
- string BadMsg ;'still bad after 5 retries'
- jmp short W_Trk0 ;go back
-
- Wrt_Exit:
- string CrLfS ;force Lf for next cycle
- int21 9
- ret
- Write_It endp
-
- ; Wrote a buffer full to target disk.
- ; Now read it back to verify the write was correct.
- ; Protect CX, which has current track data.
-
- Verify_It proc near
- Verify:
- mov AX,FillVal
- not AX
- call Fill_Buff ;prime buffer for read,
- ;initialize starting regs
- Ver_Lup:
- string VerifyS ;'Verifying'
- call Post_Trk ;display, plus track/error data
-
- mov AH,02H ;read sectors
- int 13H
- call Say_Secs ;show secs verified, won't hurt CF
- jnc Ver_0 ; no disk read error, fine
-
- call Disp_Err ;display the error in AH
- string VErrChkS ;"will check buff anyway"
- int21 9 ;display it, fall thru to Chek_Buff
-
- Ver_0: call Chek_Buff ;see how the read verifies
- jnc Ver_Ok ; yep, just fine
-
- mov DL,VerrA ;snarf the Ascii verify error val
- inc DL ;inx it
- mov VerrA,DL ;save it
- int21 2 ;display that char
- cmp DL,'5' ;maxed out?
- jb Ver_Lup ; nope, retry
-
- string BadMsg ;'still bad after 5 retries'
- jmp short Ver_1 ;give it up
-
- Ver_Ok: cmp VerrA,'0' ;no errors to date?
- string RetryMsg ;'ok after retry'
- jne Ver_1 ; old errors, so skip the stroke
- string RdOkS ;'read verified ok'
- Ver_1: int21 9
- mov VerrA,'0' ;clear the flag in any case
- call Add_BX ;bump buffer pointer n sectors worth
- inc CH ;bump track
- cmp CH,MaxTrkCnt ;max for this buffer?
- jb Ver_Lup ; nope, loop
-
- ret
- Verify_It endp
-
- ; Update our progress report with Ascii side and track values.
- ; Enters with activity string address in DX ('Reading', 'Writing',
- ; or 'Verifying').
- ; Reloads DX with current drive/side information for the next read/write,
- ; BX with current buffer pointer,
- ; AL with nr of sectors to read/write.
-
- Post_It proc near
- Post_Trk:
- push CX ;save track/sector stuff
- int21 9 ;print present activity in DX
- mov DI,offset CurTrkA ;here's where the track goes
- mov AL,CH ;current track
- inc AL ;adjust
- call Asciiz_99 ;stuff in string
- string ErrStat ;'track nn'
- int21 9
-
- mov AL,SecsPerTrk ;nr of sectors/track
- mov BX,BuffPtr ;get pointer (easier than
- ;always protecting BX)
- mov DX,DrvSidW ;current drive/side
- pop CX ;restore
- ret
- Post_It endp
-
- ; Compute how big the buffer must be, using current disk format's
- ; sectors per track and the constant tracks per buffer.
- ; Update variable word BufferSiz.
-
- Compute_It proc near
- Compute_Size:
- mov AX,256 ;words/sector
- xor CX,CX ;clear msb
- mov CL,SecsPerTrk ;sectors/track
- mul CX ;= words/track
- mov TrkSiz,AX ;save that val
- mov CL,BufTrkSiz ;max tracks/buffer
- mul CX ;= bytes/this read/write
- mov BufferSiz,AX ;save as new buffer size
- ret
- Compute_It endp
-
- ; Bump our buffer pointer BX by the appropriate amount
- ; for this disk format (e.g., 512 bytes per sector for 8/9 sectors)
- ; each time we read/write a track.
-
- Add_It proc near
- Add_BX:
- mov BX,BuffPtr ;get previous value
- mov AX,TrkSiz ;words per track
- shl AX,1 ;change to bytes
- add BX,AX ;new buffer offset
- mov BuffPtr,BX ;save new value, return new val in BX
- ret
- Add_It endp
-
- ; Shows the returned sector count (after a track write or verify)
- ; as a double check of what's been accomplished.
-
- Sector_It proc near
- Say_Secs:
- pushf ;save flags a sec
- mov DI,offset SecRetA ;'%% sectors reported'
- mov DX,DI ;string offset
- push AX ;save errors
- call Asciiz_99 ;asciify it
- string SecRetA
- int21 9
- pop AX
- popf ;if this breaks your 80286,
- ;there's ways to replace it.
- ;don't break mine!
- ret
- Sector_It endp
-
- ; Fill buffer with whatever our current fill activity is.
- ; No registers to preserve.
- ; Updates CX (track/sector) in each case.
-
- Fill_It proc near
- Fill_Buff:
- mov DI,offset BigBuff
- mov CX,BufferSiz ;256 words * 9 sectors max
- cld ;insure left to right
- cmp AX,RWORD ; random?
- je Random ; yep, go generate some
-
- rep stosw ;fill with fill word
- jmp short Fill_Exit
-
- ; From Prof. Arne Thesen et al, Univ of Wisconsin-Madison
- ; (but his paper had Fortran and Pascal .. ugh ..)
- ; I don't know how great this is for a random number generator,
- ; but it certainly is fast enough!
- ;CX=buffer size,
- ;AX=7FFFH
-
- Random: mov BP,3993 ;suggested constant multiplier
- mov BX,AX ;enters with 7FFFH in AX
- mov AX,SeedInt ;get seed.int
- Rnd_Lup:
- mul BP ;seed.int = multiplier * seed.int
- add AX,1 ;..+1
- mov DL,AH ;rbyte (only use msb)
- jns Rnd1 ;if seed.int < 0 .. nope, skip
- add AX,BX ;seed.int = seed.int + maxint...
- inc AX ;...+1
- Rnd1: mul BP ;again
- add AX,1
- mov DH,AH ;the other rbyte
- jns Rnd2
- add AX,BX
- inc AX
- Rnd2: xchg DX,AX ;get the Rword
- stosw
- loop Rnd_Lup ;until done
- mov SeedInt,DX ;save the seed.int
-
- Fill_Exit: ;load starting parms
- mov CX,TrkSecW ;starting track,sector 1
- mov BX,offset BigBuff ;point to buffer
- mov BuffPtr,BX
- ret
- Fill_It endp
-
- ; Verifying each track (as I did in the first version of PURGE)
- ; doesn't hack it .. only shows disk errors or CRC errors,
- ; NOT that we successfully wrote as planned. So we do it the hard way.
-
- ; Purge the buffer, read in a buffer full, call this procedure.
- ; This scans the buffer (starting at the current track offset)
- ; for one track's worth. It scans for the current fill value,
- ; reporting success or failure as appropriate.
-
- ; This is useless for random writes, of course, but plan on doing something
- ; clever to see if the buffer has big chunks of the previous fill (0's).
-
- Check_It proc near
- Chek_Buff:
- push CX
- mov DI,Buffptr ;ES:DI current buffer psn
- mov CX,TrkSiz ;words in one track
- mov AX,FillVal ;current fill value
- ChekLup:
- scasw
- jne Bad_Chk
- loop ChekLup
- clc ;say ok
- jmp short Chek_Exit
-
- Bad_Chk:
- string RdFailS ;'failed read verify..'
- int21 9
- stc ;CF set to show failure
- Chek_Exit:
- pop CX
- ret
- Check_It endp
-
- ; Process errors from ROM-BIOS call.
- ; Error arrives in AH.
- ; 1 = bad command
- ; 2 = address mark not found
- ; 3 = write attempted on write-protected disk
- ; 4 = sector not found
- ; 6 = diskette removed
- ; 8 = DMA overrun
- ; 9 = DMA across 64Kb boundary
- ; 10H = bad CRC
- ; 20H = NEC controller failed
- ; 40H = seek failed
- ; 80H = time out
-
- Disk_Error proc near
- Disp_Err:
- push CX ;track/sector stuff
- push DX ;head/drive
-
- xchg BX,AX ;save that error val
-
- mov DI,offset DerrA ;point to where we stuff it
- mov AL,AH ;Asciize wants it in AL
- call Asciiz_255 ;stuff the error in AL
- string ErrorS ;'Error nnn'
- int21 9 ;print char
- dskint 0 ;reset drive
-
- xchg BX,AX ;get error back
-
- xor BX,BX ;clear BX
- cmp AH,80H ;max allowable error
- ja Disp2 ;bogus, treat as 0
- mov BL,AH ;here's the raw error
- cmp AH,9 ;1..9H?
- jbe Disp2 ; yes, fine
- mov BL,9 ; nope, start at 9 (+inc)
-
- Disp1: inc BL ;bump processed counter
- cmp AH,10H ;raw error 10H or above?
- jbe Disp2 ; all adjusted, done
- shr AH,1 ; divide adjusted error in half
- jmp short Disp1 ;loop and try again
-
- ; Error checking here isn't real strong .. we'd better hope a fairly legal
- ; error number comes in here, or no telling WHERE our table offset
- ; will end up!
-
- Disp2: mov SI,offset ErAdrTbl ;header of error msg address table
- shl BL,1 ;*2 for word addresses
- add SI,BX ;add in offset
- mov DX,[SI]
- push AX ;save processed error
- int21 9 ;print it
- string CrLfS ;and terminate with Cr/Lf
- int21 9
- pop AX ;restore processed error
-
- cmp AH,2 ;no address (e.g., unformatted)
- jb Disp4 ;forget it
- jne Disp2a ; maybe larger
- cmp NrSides,1 ;are we set as a single-sided disk?
- jb Disp4 ; Yep, no use trying to switch
- string TrySS ;'will try single-sided'
- int21 9
- mov NrSides,0 ;hope this works
- call Update ;post the strings
- clc ;so we retry
- jmp short Disp4 ;exit
-
- Disp2a: cmp AH,3 ;write-protected?
- jb Disp4 ; Nope
- cmp AH,4 ;sector not found?
- jne Disp2b ; nope
- string Try8S ;'will try 8-sector format'
- int21 9
- mov SecsPerTrk,8 ;try 8-sector format
- call Update
- call Compute_Size ;gotta recompute this
- clc ;to attempt retry
- jmp short Disp4 ;exit
-
- Disp2b: cmp AH,6 ;diskette removed?
- ja Disp4 ; nope
- mov AH,1 ;1 = get kbd response
- int21 0CH ;get kbd response
-
- Disp4: pop DX ;restore regs
- pop CX
- ret
- Disk_Error endp
-
- ; Get the specifics about the target disk. I can NOT test the
- ; AT portion of this code (since I ain't got one).
- ; Normal PC disk formats work just fine (8 track SS, 9 track DSDD, etc.)
-
- Drive_It proc near
- Get_Drv:
- mov DL,CurDrv ;user-specified drive
- dec DL ;A or B?
- jns Drv_Ok ; fine
- string FlopOnly ;'floppies only, Clyde'
- int21 9
- inc AH ;1 = get kbd response
- int21 0CH ;get kbd response
- xor DL,DL ;sigh... force A
-
- Drv_Ok: mov CurDrv,DL ;save drive ID
- inc DL ;adjust for...
- int21 1CH ;get FAT information, any drive
- mov AL,[BX] ;FAT ID byte (blows away DS!)
-
- ; Should be returning
- ; CX = bytes per sector
- ; AL = sectors per allocation unit
- ; DX = nr of allocated units
- ; Norton's wrong! A typo! should be
- ; DS:BX = pointer to FAT ID byte
- ; So DX has nr of allocated units
-
- xor AH,AH ;clear msb
- mov DX,CS ;restore ...
- mov DS,DX ;DS=CS
-
- mov DI,offset FAT_ID_Table ;lookup table
- mov CX,5 ;5 FAT file IDs
- cld
- repnz scasb ;search
- jz Do_Fat_Table ;found it
-
- cmp AL,ErMsgTblSiz ;run off the table?
- jne Bad_Drv ;bad/unknown FAT
- mov AL,CurDrv ;let's see if it's an AT
- sub AL,2 ;???
- add AL,80H
- mov CurDrv,AL
- mov DL,AL ;this drive
- dskint 8 ;AT Get drive parameters
- jb Bad_Drv ;failed
-
- mov NrSides,DH ;nr of sides
- mov AL,CL ;max nr sectors
- and AL,3FH ;mask to insure legal?
- mov SecsPerTrk,AL ;sectors/track
- mov AL,CH ;max nr tracks
- mov AH,CL ;compute
- mov CL,6 ;divide by 64
- shr AH,CL
- mov NrTraks,AL ;save as disk tracks
- jmp Got_Drv
-
- Bad_Drv: string BadFatS ;'gonna assume DS9'
- int21 9 ;display it
- sub DI,2 ;that'll make it DS9
- ;... and fall thru to
-
- ; Working with a known FAT ID byte from our table.
- ; DI has where we found the byte
-
- Do_Fat_Table:
- sub DI,offset FAT_ID_Table+1 ;correct to table offset
-
- ; First we load our report string with the FAT ID byte data and report that.
- ; Then we force double-sided, 9-sector (if not doing an AT 15-sector)
- ; because it could have been formatted differently before, and there's
- ; data there!
- ; If it fails during the write, we'll reset back to 1-sided and/or 8-sector.
-
- mov AL,Side_Table[DI]
- mov NrSides,AL ;save nr sides
- mov AL,Trak_Table[DI] ;nr tracks
- mov NrTraks,AL ;save as nr of tracks
- mov AL,Sec_Table[DI] ;nr sectors/track
- mov SecsPerTrk,AL ;save it
-
- Got_Drv:string FormatS ;'looks like...'
- int21 9
- call Update ;update per disk data
- string SideCntA ;'% sides, %% tracks...'
- int21 9
- mov NrSides,1 ;force to 2-sided
- mov AL,SecsPerTrk
- cmp AL,9 ;leave 9 & above alone
- jae Got_Drv1 ; ok
- mov AL,9 ;force to 9
- mov SecsPerTrk,AL
- Got_Drv1:
- call Update
- string GonnaTryS
- int21 9
- string SideCntA
- int21 9
- clc ;clear CF to say ok
- ret
- Drive_It endp
-
- GetYesP proc near
- GetYes: int21 9 ;print string in DX
- mov AX,0C01H ;get kbd response
- int 21H
- or AL,' ' ;lowercase it
- cmp AL,'y'
- ret ;with ZF set or clear
- GetYesP endp
-
- ; If a value larger than a single digit, use this to change to Ascii.
- ; Enters with value in AL, and DI pointing to the target address.
- ; Will work with up to 255, but you'd better have room reserved!
- ; From Dr Dobbs, Apr 86
-
- Asciize_It proc near
- Asciiz_255:
- add di,2 ;point to low digit
- std ;set for hi to lo store
- aam ;convert low order digit
- or al,'0' ;make it ascii
- stosb ;and store low digit
- mov al,ah ;load high part
- aam ;convert high and middle digit
- or ax,'00' ;make them ascii
- stosb ;store middle digit
- mov al,ah ;high digit
- stosb ;store high digit
- cld ;restore direction flag
- ret
-
- Asciiz_99:
- aam ;convert to 2 digits
- or ax,'00' ;make them ascii
- xchg ah,al ;the low one
- stosb ;store middle digit
- mov al,ah
- stosb ;store high digit
- ret
- Asciize_It endp
-
- ; Access the current disk parameter block, set settle-time
- ; and motor wait. (Again, from Weissman's article in Dr. Dobbs.)
- ; Uses variable Times for initial values, and to store original values.
- ; (Each time you call this, you in effect toggle the times.)
- ; Sets disk delay times to shorter values if starting,
- ; else resets back to original values saved in Times.
-
- Set_Disk proc near
- Set_Parms:
- push DS
- xor BX,BX
- mov DS,BX
- assume DS:Sys0
-
- lds bx,Disk_Ptr
- assume DS:Cseg
- mov AX,Times ;new settle (AL) and wait (AH) times
- xchg [bx].DiskTimes,AX ;move the whole word
- mov Times,AX ;save old times
- pop DS
- ret
- Set_Disk endp
-
- BigBuff LABEL BYTE
-
- ; Buffer from here to end of segment.
- ; No, I have NOT protected the stack...
- ; This thing works now, and there IS room for everything.
- ; I stayed with just reading in 10 tracks at a time,
- ; assuming the 512-byte sectors, 9-sectors/track.
- ; It MAY blow up with 15-sector stuff.
-
- ; There are ways to handle really big buffers, other segments, etc.,
- ; but I wanted to keep this simple and not mess with ESs and DSs
- ; too much. Also didn't want an EXE file.
-
- ; We put this stuff here since it's only used at startup.
- ; It gets overwritten when the BigBuff fires up.
-
- CopyRite db Lf,Tab,Tab,'TOADPURG Diskette Purge Utility V1.2',Cr,Lf
- db Tab,Tab,'Copyright (C) 1986 David P Kirschbaum',Cr,Lf
- db Tab,Tab,'Toad Hall All Rights Reserved',Cr,Lf
- db Tab,Tab,'Copy permitted for non-sale purposes.'
- db Cr,Lf,'$'
-
- TellMeTwice db Cr,Lf,Tab,Tab,Tab,'Tell-Me-Twice...',Bell,Cr,Lf
- db Tab,Tab,'This TOTALLY destroys ALL data on the disk!',Cr,Lf
- db Tab,Tab,Tab,'Continue? (Y/N): $'
-
- WhatItIs db Tab,'Purges floppy diskettes of potential sensitive data',Cr,Lf
- db Tab,'by overwriting ALL disk tracks as specified by the',Cr,Lf
- db Tab,'National Security Agency (NSA).',Cr,Lf,Lf
- db Tab,'To use, enter at the command line:',Cr,Lf
- db Tab,"TOADPURG A: (or B:) (no C's, D's, etc.!)",Cr,Lf,Lf
- db Tab,'This action is irrevocable!',Cr,Lf
- db Tab,"You can break the program with a Ctrl C, but it won't help",Cr,Lf
- db Tab,'since the FAT and disk catalog are the first to go!',Cr,Lf,Lf
- db Tab,'Redirect the screen display to a printer or file with:',Cr,Lf
- db Tab,'TOADPURG A: >LPT1 or',Cr,Lf
- db Tab,'TOADPURG B: >PURGE.LOG',Cr,Lf,Lf
- db Tab,'Written for the US Navy (and the SEALs) as a public service.'
- db Cr,Lf,'$'
-
- Purg Endp
- Cseg Ends
- End Purg