; ERADIR.Z80 Vers 1.0 26Aug86 ; (c) 1986 by W. Brimhall - Znode #52 (602)996-8739 ; This program can be distributed free for non-commerical use. ; ERADIR erases the directory of any CP/M 2.2 compatible disk by ; writing E5h to each byte of all sectors in the directory. This ; program must be used with caution since any data that was on the ; drive will be next to impossible to recover. One of the primary ; uses for ERADIR is to initalize a RAM disk after power up. ;Enter this command line to display the builtin help info: ; ; ERADIR / ;Enter this command line to Assemble & link: ; ; ZAS ERADIR ; ZLINK ERADIR,SYSLIB/ ;Syslib routines: ext cin ; input console to reg a ext bist ; bdos console input status ext bout ; bdos output reg a to console ext bbline ; bdos line input ext crlf ; output crlf to console ext print ; print inline message ext phldc ; print hl in dec on con bdos equ 5 cr set 0dh lf set 0ah ; + + + + ilprn + + + + ;This macro displays an inline string terminated with 0h ;on the console without loosing control when tw tracing ;with a debugger. It is used with the ilprn@ subroutine ;located at the end of the program. ilprn macro call ilprn@ ret endm ;----------------------------- ; COM file entry point @ 100h ;----------------------------- jp main ;-------------- ; Help routine ;-------------- help: ilprn db cr,lf db 'ERADIR Vers 1.0 26Aug86',cr,lf db ' CP/M directory erase program.',cr,lf db ' (c) 1986 by W.Brimhall Znode 52 (602) 996-8739',cr,lf db ' (This program can be distributed free for ' db 'non-commerical use.)',cr,lf,lf db ' ERADIR erases the directory of any ' db 'CP/M 2.2 compatible disk by',cr,lf db ' writing E5h to each byte of all sectors ' db 'in the directory. This',cr,lf db ' program must be used with caution since ' db 'any data that was on the',cr,lf db ' drive will be next to impossible to recover. ' db 'ERADIR is especially',cr,lf db ' suited for initalizing RAM disks ' db 'after power up.',cr,lf,lf db ' Command Line Syntax:',cr,lf,lf db ' ERADIR / ;Display this help info.',cr,lf db ' ERADIR d ;Erase directory on drive "d" ' db '(A thru P).',cr,lf db ' ERADIR ;Prompt user for drive.',cr,lf,lf db ' Information about the selected ' db 'drive is displayed and you are given',cr,lf db ' a chance to abort before the actual ' db 'directory erase is performed.',cr,lf,lf,0 jp exit ;------------------ ; dbug entry point ;------------------ dbug: call main ;ddt entry point rst 38h ;return to ddt ;-------------- ; main program ;-------------- main: ld (stack),sp ;save ccp sp ld sp,stack ;set up new stack ld c,0ch call bdos ;check cp/m version ld a,l cp 22h jp nc,verok ilprn db cr,lf,'++Must have CP/M vers 2.2 or later...',0 jp abort verok: call bjmps ;set up local bios jumps ;Check for command line parameters. ld a,(80h) ;a= command line length or a jr z,enter ;prompt user if nul command line tail ld a,(82h) cp '/' jp z,help ;display help info if / call valid ;check for valid drive jr nc,logdrv ;jmp if A thru P ;Prompt user for drive. enter: ilprn db cr,lf,'Logical drive (A: thru P:) ? ',0 or -1 ;select upper case input call bbline ;input line (syslib) or a jp z,abort ;exit if cr only call crlf ld a,(hl) ;a= selected logical drive call valid ;check for valid drive jp c,error ;jmp if bad ;Log in the selected drive. logdrv: sub 'A' ;convert to drive number ld (drive),a ;save drive number call getsp ;login & get trk & sec parms jr nz,drvok ;jmp if valid drive error: ilprn db '++Illegal drive name specified...',cr,lf,0 jp enter ;Display disk parameters and make double shure we want ;to continue... drvok: call dirgps ;calculate number of directory groups call stats ;display drive statistics ilprn db cr,lf,lf,'OK to erase directory on drive ',0 ld a,(drive) add a,'A' call bout ;display drive letter ilprn db ': (y/n, CR=n) ? ',0 or -1 ;select upper case input call bbline ;input line from console or a jp z,abort ;abort if cr only ld a,(hl) ;get 1st char cp 'Y' jp nz,abort ;abort if not Y ;Now that a drive has been successfully selected & verified ;we can go ahead and erase its directory. ilprn db cr,lf,lf,'Erasing directory',lf,0 call filbfr ;fill default dma buffer with e5h call group0 ;set track & sector for group 0 nxtrec: ld bc,dskbfr ;bc--> disk write buffer call lsdma ;set dma address ilprn db cr,'Track ',0 ld hl,(curtrk) call phldc ilprn db ' Sector ',0 ld hl,(physec) call phldc call wrtsec ;write 1 sector call nxtsec ;advance to next sector within group jr nc,nxtrec ;loop til end of group ld hl,group inc (hl) ;group=group+1 ld a,(dgrps) ;a=number of dir groups cp (hl) jp nz,nxtrec ;loop til end of directory call crlf ;output crlf to console (syslib) ld hl,(count) ld a,h or l jr z,dirok ;jmp if no write errors call phldc ;display error count ilprn db ' Media errors in directory, Reformat and try again...' jr exit ;exit dirok: ilprn db cr,lf,'Successful Directory erase',cr,lf,0 jr exit ; + + + + abort & exit + + + + ;Exit back to the CCP. abort: ilprn db cr,lf,'++ERADIR aborted...',cr,lf,0 exit: ld bc,80h ;bc--> default dma buffer call lsdma ;restore dma address to default buffer ld a,(4) ;get current drive ld c,a ;c= drive ld e,1 ;reset "new select" flag call lseldk ;restore original drive selection ld sp,(stack) ;restore CCP sp ret ; + + + valid + + + ;Return cf reset if reg A contains a drive name from 'a' to 'p' valid: and 5fh ;convert drive to upper case cp 'A' ret c ;cf=set if drive < 'a' cp 'P'+1 ccf ret ;cf=set if drive > 'p' ; + + + getsp + + + ;Get track and sector parameters for drive. ;Return zf set if drive is not in system. getsp: ld a,(drive) ld c,a ;c=current drive ld e,0 ;set "new select" flag call lseldk ;select disk through bios to get dph ld a,h or l ret z ;ret if select error ld e,(hl) ;get the sector table pntr inc hl ld d,(hl) inc hl ex de,hl ld (sectbl),hl ld hl,8 ;offset to dpbptr add hl,de ld a,(hl) inc hl ld h,(hl) ld l,a ;hl --> dpb start (source) ld de,dpb ;de --> local area for dpb (dest) ld bc,14 ;bc= bytes to move ;fill in disk params ldir ;move dpb into local area ld hl,grpdsp ld a,(blm) ;blm=sectors per group ld (hl),a ;grpdsp=max ld hl,(dsm) ;dsm=total groups ld (group),hl ;group=max call gtksec ;compute last track & sector on drive ld hl,(cursec) ld (maxsec),hl ld hl,(curtrk) ld (maxtrk),hl or -1 ret ;a= -1, zf=reset ; + + + filbfr + + + ;Fill dskbfr with E5h. filbfr: ld hl,dskbfr ;hl--> dma buffer ld c,80h ;c= 80h= byte count filbf1: ld (hl),0e5h ;load a byte inc hl dec c jr nz,filbf1 ;loop til c=0 ret ; + + + group0 + + + ;Set track & sector for group 0. group0: ld hl,0 ld (group),hl ;group=0 ld a,0 ld (grpdsp),a ;displacement=0 call gtksec ;set track & sector ret ; + + + dirgps + + + ;Calculate the number of directory groups ;and store at dgrps. dirgps: ld hl,(al0) ;hl=dir group allocation bits ld c,16 ;c=loop count ld b,0 ;b=group count dirg1: call rotrhl jp nc,notset ;jmp if bit is not set inc b notset: dec c jp nz,dirg1 ld a,b ld (dgrps),a ;store directory groups ret ; + + + stats + + + ;Print disk statistics stats: ilprn db cr,lf,'Disk information for drive ',0 ld a,(drive) add 'A' call bout ilprn db ':',cr,lf,' tracks:',9,0 ld hl,(maxtrk) inc hl call phldc ilprn db cr,lf,' sys tracks:',9,0 ld hl,(systrk) call phldc ilprn db cr,lf,' recs/trk:',9,0 ld hl,(spt) call phldc ilprn db cr,lf,' recs/group:',9,0 ld a,(blm) inc a ld l,a ld h,0 call phldc ilprn db cr,lf,' tot grps:',9,0 ld hl,(dsm) call phldc ilprn db cr,lf,' dir entries:',9,0 ld hl,(drm) inc hl call phldc ilprn db cr,lf,' dir groups:',9,0 ld hl,(dgrps) ld h,0 call phldc ret ; + + + report + + + ;Report error information on consol report: ilprn db cr,lf,'media error : track ',0 ld hl,(curtrk) call phldc ilprn db ' physical sector ',0 ld hl,(physec) call phldc call crlf ret ; + + + nxtsec + + + ;advance to next sector. ;ret cf set when next group is reached nxtsec: ld a,(grpdsp) ;a=sector within group inc a ld (grpdsp),a ;bump to next sector ld d,a ;save in d ld a,(blm) ;a=sectors per group cp d ret nc ;ret cf=0 if same group xor a ld (grpdsp),a ;else make displacement=0 scf ret ;ret cf set ; + + + settrk + + + ;Set track # to value stored at curtrk. settrk: ld hl,(curtrk) ld b,h ld c,l call lstrk ;local bios set track call ret ; + + + setsec + + + ;Set the physical sector number through bios by taking the ;logical sector value stored at cursec and using the sector ;translation table to convert it to its physical value. setsec: push hl ld hl,(cursec) ex de,hl push de ld hl,(systrk) ex de,hl ld hl,(curtrk) call subde pop bc ld h,b ld l,c jp nc,notsys ld a,(first0) ;see if first sec 0 or a jp nz,sets1 ;no, jump away dec hl ;yes, so decrement jp sets1 ;requested, then go notsys: ld hl,(sectbl) ex de,hl dec bc call lsectn ld a,(spt+1) ;if spt<256 (hi-ord = 0) or a ;then force 8-bit translation jp nz,sets1 ;else keep all 16 bits ld h,a sets1: ld (physec),hl ld b,h ld c,l call lssec pop hl ret ; + + + gtksec + + + ;Convert group & displacement to track & sector. gtksec: ld hl,(group) ;hl = group # ld a,(bsh) gloop: add hl,hl dec a jp nz,gloop ld a,(grpdsp) add a,l ;can't carry ld l,a ;Divide by # of sectors, quotient=track, remainder=sector ex de,hl ld hl,(spt) call neg ex de,hl ld bc,0 divlp: inc bc add hl,de jp c,divlp dec bc ex de,hl ld hl,(spt) add hl,de push hl ld hl,(systrk) add hl,bc ld (curtrk),hl pop hl inc hl ld (cursec),hl ret ; + + + wrtsec + + + ;Write sector to disk. wrtsec: xor a call gtksec ;convert group to track & sector call setsec ;set sector call settrk ;set track call lwrite ;local bios disk write or a ret z ;ret if good write ld hl,(count) inc hl ld (count),hl ;inc error count rdsec1: call report ;report error on consol ret ; + + + prndrv + + + ;print current drive name on consol prndrv: ld a,(drive) ;get current drive number add 'a' ;convert to ascii name call bout ld a,':' call bout ret ; + + + bjmps + + + ;This subroutine sets up local jumps to the bios bjmps: ld hl,(1) ld de,3 add hl,de ld (lconst+1),hl add hl,de ld (lconin+1),hl add hl,de ld (lbout+1),hl add hl,de ld (llist+1),hl add hl,de ;punch add hl,de ;reader add hl,de ld (lhome+1),hl add hl,de ld (lseldk+1),hl add hl,de ld (lstrk+1),hl add hl,de ld (lssec+1),hl add hl,de ld (lsdma+1),hl add hl,de ld (lread+1),hl add hl,de ld (lwrite+1),hl add hl,de add hl,de ld (lsectn+1),hl ret lconst: jp $-$ ;filled in by bjmps routine lconin: jp $-$ lbout: jp $-$ llist: jp $-$ lhome: jp $-$ lseldk: jp $-$ lstrk: jp $-$ lssec: jp $-$ lsdma: jp $-$ lread: jp $-$ lwrite: jp $-$ lsectn: jp $-$ ; + + + neg + + + ;2's complement hl ==> hl neg: ld a,l cpl ld l,a ld a,h cpl ld h,a inc hl ret ; + + + rotrhl + + + ;hl/2 ==> hl rotrhl: or a ld a,h rra ld h,a ld a,l rra ld l,a ret ; + + + subde + + + ;hl-de ==> hl subde: ld a,l sub e ld l,a ld a,h sbc a,d ld h,a ret ; + + + mult + + + ;quick kludge multiply ;hl=de ==> hl mult: push bc push de ex de,hl ld b,d ld c,e ld a,b or c jp nz,mulcon ld hl,0 ;filter special case jp mldone ; of multiply by 0 mulcon: dec bc ld d,h ld e,l multlp: ld a,b or c jp z,mldone add hl,de dec bc jp multlp mldone: pop de pop bc ret ; * * * * ilprn@ * * * * ;Special inline string print subroutine that will ;not lose control when tw tracing with Zsid. this ;routine returns to the instruction following the ;ilprn@ which must be a ret. Zsid places a ;break point at that address. the address of the ;instruction following the inline string is calculated ;and returned on the stack. ;destroys: a,d,e & flags ilprn@: ld (hlsave),hl ;save hl pop hl ;hl= return address push hl ;put back on stack inc hl ;hl --> inline string ilp1: ld a,(hl) or a jr z,ilp2 ;jmp if end of string call bout inc hl jp ilp1 ilp2: inc hl ;hl= next instruction address ex (sp),hl ;(sp)= next instruction address, ;hl= return address push hl ;(sp)= return address ld hl,(hlsave) ;restore hl ret ;return to "ret" instruction ;following "ilprn@" ; * * * data area * * * hlsave: ds 2 ;temp storage for hl drive: ds 1 ;current drive dgrps: ds 1 ;directory groups dmaptr: ds 2 ;dma buffer pointer group dw 0 ;current cp/m group number grpdsp db 0 ;sector displacement within group count dw 0 ;write error counter saveflg db 0 curtrk dw 0 cursec dw 1 physec dw 1 filect dw 0 qflag db 0 ;quiet? (0=no) first0 db 0 ;sets to 0 if first sec # is 0 maxtrk dw 0 maxsec dw 0 ver2fl db 0 sectbl dw 0 ;pointer to sector skew table ;The disk parameter block is moved here from cp/m dpb: equ $ ;disk parameter block (copy) spt: ds 2 ;sectors per track bsh: ds 1 blm: ds 1 ;group size -1 exm: ds 1 dsm: ds 2 ;total groups drm: ds 2 ;directory entries -1 al0: ds 1 ;dir group allocation bits al1: ds 1 cks: ds 2 systrk: ds 2 ;system tracks dskbfr: ds 128 ;disk sector write buffer ds 128 stack: ds 2 end