; UNERA33.ASM - Erased-File-Recovery Program November 20, 1988 ; ; NOTE: Be very careful when recovering an erased file! ; Files that have been edited several times on the same ; disk often use different tracks on the next edit. Un- ; less they have been removed via SAP.COM (or similar), ; recovering an erased file can easily add some of those ; prior sectors, making the recovered program a lot big- ; ger than anticipated. Or, if a file is not recovered ; immediately after erasing, part of it might be renamed ; by a subsequent file and impossible to retrieve. ; ;----------------------------------------------------------------------- ; recent updates ; ; 11/20/88 Removed v1.4 option. Formatted for other assembers than ; v33 just ASM.COM or MAC.COM. - Irv Hoff ; ; 11/15/87 Revised sign-on message to reflect current version number ; v32 and revision date. Changed "no filename" message to "help" ; message displaying proper usage; edited other screen mes- ; sages for conciseness. Original 8080 code left unchanged for ; assembly with DRI's ASM.COM. ; - George Havach ; ; 07/14/85 Added disk system reset at the head of exit routine, so (at ; v31 least when running CP/M 3.x) did not have to run DIR twice ; to have it display all entries in the directory. ; ; Runs as-is under CP/M 2.2, but under CP/M Plus (3.x) re- ; quires attachment of an .RSX capable of CP/M 2.2 emulation ; with capability BOTH to emulate certain CP/M 2.2 BIOS func- ; tions that don't behave the same under CP/M 3.x as they do ; under CP/M 2.2, AND ability to trap the version call and ; return 2.2. If you are running CP/M 3.0 and have only the ; Griswold emulation .RSX (Dr. Dobb's Journal #93, July 1984), ; you will need to edit UNERA31.ASM to undo the version check. ; RSX's that do both are available on RCP/M's. ; ; This description of UNERA31 is changed (but not the code) ; from what accompanied the UNERA31.ASM that I distributed on ; 5/20/85. - Jerry Levy ; (215) 657-0898 (voice) ; ; 09/01/84 Added routine that checks directory listing on mixed v3.0 ; v30 density disks. Other modest changes. Standardized format, ; simplified some messages. - Irv Hoff ; ; 07/16/84 Corrected problem when UNERAsing a directory entry in the ; v20 last physical block. Before, the write back to disk set re- ; gister C to 1 (write to directory). ; - Bill Duerr ; ; 11/13/83 Inserted missing '$' at end of some error messages. Added ; v19 conditional assembly to omit code for CP/M 1.4 to allow ; shorter object file. - Bob Clyne ; ; 10/20/83 Corrected bug that caused program to try to read one more ; v18 record from a track than was actually on the track. Deleted ; unused data space from program. Modified CP/M version code. ; - Bob Clyne ; ; 10/02/83 Made record-related items two bytes. Changed code so that ; DPB info is not obtained until after the specified drive is ; selected - the DPB returned is for the default drive and it ; could make a difference. Changed a compare instruction so ; program correctly identifies absence of a filename. Added ; traps for MP/M and CP/M 3.0 - if anyone confirms that the ; program will run properly under these systems, remove the ; traps. Added syntax message if file name is not specified. ; Added read error message and CTRL-C abort. The changes were ; rather extensive and obviously have not been tested with all ; combinations of variables. - Bob Clyne ; ==============: ; UNERA32.ASM - Erased-File-Recovery Program ; ; NOTE: Be very careful when recovering an erased file! ; Files that have been edited several times on the same ; disk often use different tracks on the next edit. Un- ; less they have been removed via SAP.COM (or similar), ; recovering an erased file can easily add some of those ; prior sectors, making the recovered program a lot big- ; ger than anticipated. Or, if a file is not recovered ; immediately after erasing, part of it might be renamed ; by a subsequent file and impossible to retrieve. ; ; 11/20/88 Removed v1.4 option. Formatted for other assembers than ; v3.3 just ASM.COM or MAC.COM. - Irv Hoff ; ; 11/15/87 Revised sign-on message to reflect current version ; v3.2 number and revision date. Changed "no filename" ; message to "help" message displaying proper usage; ; edited other screen messages for conciseness. ; Original 8080 code left unchanged for assembly ; with DRI's ASM.COM. - George Havach ; ; 07/14/85 Added disk system reset at the head of exit routine, ; v3.1 so (at least when running CP/M 3.x) did not have to ; run DIR twice to have it display all entries in the ; directory. ; ; Runs as-is under CP/M 2.2, but under CP/M Plus (3.x) ; requires attachment of an .RSX capable of CP/M 2.2 ; emulation with capability BOTH to emulate certain ; CP/M 2.2 BIOS functions that don't behave the same ; under CP/M 3.x as they do under CP/M 2.2, AND ability ; to trap the version call and return 2.2. If you are ; running CP/M 3.0 and have only the Griswold emulation ; .RSX (Dr. Dobb's Journal #93, July 1984), you will ; need to edit UNERA31.ASM to undo the version check. ; RSX's that do both are available on RCP/M's. ; ; This description of UNERA31 is changed (but not the ; code) from what accompanied the UNERA31.ASM that I ; distributed on 5/20/85. ; - Jerry Levy ; (215) 657-0898 (voice) ; ; 09/01/84 Added routine that checks directory listing on mixed ; v3.0 density disks. Other modest changes. Standardized ; format, simplified some messages. ; - Irv Hoff ; ; 07/16/84 Corrected problem when UNERAsing a directory entry in the ; v2.0 last physical block. Before, the write back to disk set ; register C to 1 (write to directory). ; - Bill Duerr ; ; 11/13/83 Inserted missing '$' at end of some error messages. Added ; v1.9 conditional assembly to omit code for CP/M 1.4 to allow ; shorter object file. ; - Bob Clyne ; ; 10/20/83 Corrected bug that caused program to try to read one more ; v1.8 record from a track than was actually on the track. Deleted ; unused data space from program. Modified CP/M version code. ; - Bob Clyne ; ; 10/02/83 Made record-related items two bytes. Changed code so that ; v1.7 DPB info is not obtained until after the specified drive is ; selected - the DPB returned is for the default drive and it ; could make a difference. Changed a compare instruction so ; program correctly identifies absence of a filename. Added ; traps for MP/M and CP/M 3.0 - if anyone confirms that the ; program will run properly under these systems, remove the ; traps. Added syntax message if file name is not specified. ; Added read error message and CTRL-C abort. The changes ; were rather extensive and obviously have not been tested ; with all combinations of variables. ; with all combinations of variables. ; - Bob Clyne ; ;----------------------------------------------------------------------- ; ; To use: ; ; A>UNERA HELLO.ASM (previously erased file on default drive) ; A>UNERA B:ABC.TXT (file on selected drive) ; A>UNERA XYZ.* (all files with primary filename XYZ) ; ;----------------------------------------------------------------------- ; ; Conditional-assembly constants ; YES EQU 0FFH NO EQU 0 ; ; System configuration equates: ; ; ORG 0100H ; JMP BEGIN ; Bypass the storage area ; ; Equates ; BDOS EQU 0005H ; CP/M BDOS call jump vector BOOT EQU 0000H ; CP/M warm boot jump vector CR EQU 'M'-40H ; CTRL-M for carriage return CTRLC EQU 'C'-40H ; CTRL-C to abort FCB EQU BOOT+5CH ; Default file-control block FCB2 EQU BOOT+6CH ; Secondary file-control block HT EQU 09H ; Horizontal tab LF EQU 'J'-40H ; CTL-J for line feed TBUFF EQU 0080H ;..... ; ; Messages ; BMSG: DB CR,LF,'--> File recovered, check before using',CR,LF,'$' ; CPM1MSG:DB CR,LF,'++ ABORT - This copy not assembled for ' DB 'CP/M 1.4 ++',CR,LF,'$' ; CPM3MSG:DB CR,LF,'++ ABORT - Program not verified for ' DB 'CP/M 3.0 ++',CR,LF,'$' ; SONMSG: DB CR,LF,'UNERAse v33 - 11/20/88 for CP/M$' ; HLPMSG: DB CR,LF,CR,LF,'Usage:',CR,LF,CR,LF DB HT,'UNERA [d:]filename.typ [p]' DB CR,LF,CR,LF DB 'Items in brackets are optional',CR,LF DB 'Filename and type may be ambiguous',CR,LF DB '''P'' requests a pause for disk change ' DB 'before execution',CR,LF,'$' ; ILMSG: DB CR,LF,'++ ABORT - Illegal drive selected ++',CR,LF,'$' ; MSG14: DB '1.4',CR,LF,CR,LF,'$' ; MSG22: DB '2.2',CR,LF,CR,LF,'$' ; MPMMSG: DB CR,LF,'++ ABORT - Program not verified for ' DB 'MP/M ++',CR,LF,'$' ; NFMSG: DB 'File not found',CR,LF,'$' PUSMSG: DB CR,LF,'--> Change disk and hit ',CR,LF,'$' RMSG: DB CR,LF,'++ ABORT - Error during disk read ++',CR,LF,'$' WMSG: DB CR,LF,'++ ABORT - Error during disk write ++',CR,LF,'$' ;..... ; ; Data area ; BLN: DB 48 ; 'CPMCHK' stores CP/M version CURREC: DB 0,0 ; Current record number DIRCUR: DB 1,0 ; Current directory record DIRMAX: DB 16,0 ; Number of records in directory = ; ; Maximum number of directory entries ; ; Divided by 4 (entries per record) FIXED: DB 0 ; Flag to show if any entries recovered ORGDRV: DB 0 ; Default drive when program was entered SPT: DB 26,0 ; Maximum number of records/track REWRT: DB 0 ; Rewrite flag 0=no, FF=yes TEMP: DB 0,0 ; Temp storage for FCB print TRACK: DB 2,0 ; Track number of directory ; ;----------------------------------------------------------------------- ; ; PROGRAM STARTS HERE ; ;----------------------------------------------------------------------- ; BEGIN: LXI H,0 DAD SP SHLD STACK LXI SP,STACK ; Set stack pointer CALL HELLO ; Sign-on message CALL PCHECK ; Check parameters CALL TRYFIX ; Do the recovery CALL BYE ; Signoff message JMP BOOT ; Return to CP/M ;..... ; ;----------------------------------------------------------------------- ; ; MAIN ROUTINES ; ;----------------------------------------------------------------------- ; ; Sign off ; BYE: MVI C,13 ; Reset disk system CALL BDOS LDA ORGDRV ; Get original drive MOV E,A MVI C,14 ; BDOS select drive function CALL BDOS LDA FIXED ; Check for activity ORA A JZ NOFIND ; Say none found LXI D,BMSG ; Warn found CALL PRINT RET ;..... ; ; Checks the current four directory entries against argument. If match, ; rewrites record with reactivated 1st bytes ; CHKENT: XRA A ; Assume no rewrite STA REWRT MVI B,4 ; Number of entries per record LXI H,80H ; Beginning of buffer ; CKLUP: PUSH B MOV A,M CPI 0E5H ; Check for unused JNZ CKINC PUSH H ; Save beginning address CALL COMPAR ; Compare with argument POP H JNZ CKINC ; No match PUSH H ; Save HL MVI E,0FFH ; Get user value MVI C,32 ; Get user area function CALL BDOS ; BDOS returns current area in A reg POP H ; Restore HL MOV M,A ; Poke in current user area MVI A,0FH ; Say need rewrite STA REWRT STA FIXED ; CKINC: POP B LXI D,32 ; Length of entry DAD D DCR B JNZ CKLUP LDA REWRT ; See if need rewrite ORA A JZ CKDONE ; No - done ; ; Write the directory record back to the disk ; LHLD TRACK ; Set track MOV B,H MOV C,L CALL SETTRK LHLD CURREC MOV B,H MOV C,L CALL TRNSLT CALL SETREC MVI C,1 ; Set for CP/M 2.2 deblocking BIOS CALL WRITE ; Write the record back ORA A JNZ ERRWRT ; Abort if error ; CKDONE: LHLD DIRCUR ; Get current directory record number INX H SHLD DIRCUR LHLD CURREC INX H XCHG LHLD SPT CALL SUBDE ; Take current record from records/track JNC CKDONE1 ; Skip next if more records on track LXI D,0 ; CKDONE1:XCHG SHLD CURREC ; Save new record number RNC ; Return if more records on this track LHLD TRACK INX H SHLD TRACK RET ;..... ; ; Compare 11 bytes of directory entry against argument ; COMPAR: SHLD TEMP ; Hold pointer in case of match INX H LXI D,FCB+1 XCHG MVI C,11 ; CMPR1: LDAX D ; Get directory entry character ANI 7FH ; Strip any flags CMP M JNZ CMPCKAM ; CMPR2: INX D INX H ; Bump to next character DCR C JNZ CMPR1 ; Loop for 11 characters LHLD TEMP CALL PNTFCB XRA A RET ; Returns zero flag set for match ;..... ; CMPCKAM:LDAX D CPI 0E5H ; Non-allocated entry? JZ SKIP MOV A,M CPI '?' RNZ JMP CMPR2 ; SKIP: ORA A RET ;..... ; ; Check for CP/M version and set things ; CPMCHK: LXI D,80H ; Set DMA to TBUFF MVI C,26 CALL BDOS MVI C,12 ; Version number request CALL BDOS MOV A,H ORA A JNZ ERRMPM MOV A,L CPI 30H JNC ERRCPM3 CPI 20H ; Earlier than 2.2? PUSH PSW LXI D,MSG22 ; Point to CP/M 2.2 message JC ERRCPM1 ; CALL PRINT CALL GTBIOS ; Make duplicate BIOS jump table CALL PUSCHK MVI C,13 ; BDOS reset drives function CALL BDOS ; ; Select disk and set up disk-parameter header ; LDA FCB MOV E,A MVI C,14 ; BDOS select drive function CALL BDOS LDA FCB MOV C,A MVI E,0 CALL SELDSK ; CBIOS select drive call MOV A,H ORA L POP PSW ; Illegal ; ; Get address of skew translation table and number of directory entries ; MOV E,M ; Get the address INX H ; Of XLTO MOV D,M XCHG SHLD DPH ; Save the address MVI C,31 ; Get the Disk Parameter Block address CALL BDOS ; DPB address in HL on return MOV E,M ; Number of records/track INX H MOV D,M XCHG DCX H ; Account for first record being 0 SHLD SPT ; Save number of records per track XCHG LXI D,6 ; Offset to DRM DAD D MOV E,M ; Get number of INX H ; Directory entries MOV D,M XCHG INX H ; Account for - 1 CALL SHFHL2 ; Shift HL right two places SHLD DIRMAX ; Save number of directory records LXI H,5 ; Now point to system DAD D ; Track offset MOV A,M ; Pick up number of reserved tracks INX H MOV H,M MOV L,A SHLD TRACK RET ;..... ; ERRCPM3:LXI D,CPM3MSG ; System is CP/M 3.0 CALL PRINT JMP BOOT ; ERRMPM: LXI D,MPMMSG ; System is MP/M CALL PRINT JMP BOOT ; ERRCPM1:LXI D,CPM1MSG CALL PRINT JMP BOOT ; ERRRD: LXI D,RMSG ; Error occurred on disk read - abort CALL PRINT JMP BOOT ; ERRWRT: LXI D,WMSG ; Error occurred on disk write - abort CALL PRINT JMP BOOT ;..... ; ; Make sure a legal filename is specified ; FCBCHK: LDA FCB ; Get drive specification ORA A ; See if default JNZ FCBCK1 ; No, go check filename MVI C,25 ; Ask for current drive CALL BDOS STA ORGDRV ; Save the original drive number INR A ; Offset for next instruction ; FCBCK1: DCR A ; Current drive number STA FCB ; Save it LDA FCB+1 ; Get 1st byte of filename CPI ' '+1 ; Make sure it's non-blank RNC ; OK - keep going ; ; Set string pointers and end delimiter ; LXI D,TBUFF LDAX D MOV C,A MVI B,0 INX D MOV L,A MVI H,0 DAD D MVI M,0 XCHG ; ; If no filename is specified, give help with program usage ; LXI D,HLPMSG CALL PRINT JMP BOOT ; Abort ;..... ; ; Get BIOS jumps vectors for easy reference ; GTBIOS: LHLD BOOT+1 ; Points to BIOS jump table+3 LXI D,WBOOT ; Where we will keep a copy LDA BLN ; Number of bytes to move MOV B,A ; Move likes it in B reg CALL MOVE ; Move the table RET ;..... ; ; Say who we are ; HELLO:LXI D,SONMSG ; Point to hello message CALL PRINT RET ;..... ; ; Specified an illegal disk drive - abort ; ILDISK: LXI D,ILMSG CALL PRINT JMP BOOT ; Abort ;..... ; ; General-purpose move routine from HL to DE for count in B ; MOVE: MOV A,M ; Get a byte STAX D ; Put a byte INX D ; Increment to next INX H DCR B ; Count down JNZ MOVE RET ;..... ; NOFIND: LXI D,NFMSG CALL PRINT RET ;..... ; ; Reads next record (group of four directory entries), returns with ; carry flag set if no more ; NXTREC: LHLD DIRCUR XCHG LHLD DIRMAX ; See if more records CALL SUBDE ; Subtract current directory record RC ; From maximum directory record LHLD TRACK ; Set track MOV B,H MOV C,L CALL SETTRK LHLD CURREC MOV B,H MOV C,L CALL TRNSLT CALL SETREC CALL READ ; Read a record ORA A ; Reverse sense of error flag JNZ ERRRD ; Read error has occurred - abort RET ; If bad read ;..... ; ; Check for valid parameters and say which CP/M version ; PCHECK: CALL FCBCHK ; Make sure file specified CALL CPMCHK ; Establish CP/M version RET ;..... ; ; General-purpose print routine - enter with DE pointing to message, ; then returns to caller from BDOS ; PRINT: MVI C,9 ; BDOS print string command JMP BDOS ; Go do the print ;..... ; ; FCB printing routine ; PNTFCB: PUSH H LXI D,1+8+3 DAD D MOV A,M ORA A POP H RNZ ; Print only first extent INX H MVI B,8 CALL PR1 MVI E,'.' MVI C,2 PUSH H CALL BDOS POP H MVI B,3 CALL PR1 MVI E,13 ; Send CR/LF MVI C,2 CALL BDOS MVI E,10 MVI C,2 CALL BDOS RET ;..... ; PR1: MOV A,M ANI 127 CPI ' ' ; Check for blanks JZ PR2 MOV E,A PUSH H PUSH B MVI C,2 CALL BDOS POP B POP H ; PR2: INX H DCR B JNZ PR1 RET ;..... ; ; Does user want to pause to change disks? ; PUSCHK: LDA FCB2+1 ; Get option ANI 5FH ; Make it uppercase CPI 'P' ; Is it a 'P'? RNZ ; Nope, so return LXI D,PUSMSG CALL PRINT ; Print pause message MVI C,01 CALL BDOS ; Input a char CPI 'C'-40H ; Was a CTRL-C typed? JZ BOOT ; If so, abort MVI C,02 MVI E,0AH CALL BDOS ; Output a LF RET ; And return ;..... ; ; Shift regs 'HL' right 2 bits logical ; SHFHL2: CALL SHFHL ; Clear carry ; SHFHL: XRA A MOV A,H RAR ; Shifted bit in carry MOV H,A MOV A,L RAR MOV L,A RET ; ; HL-DE ==> HL ; SUBDE: MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A RET ;..... ; ; Translate C register from logical to physical record number ; TRNSLT: LHLD DPH ; Get address of XLTO XCHG CALL RECTRAN MOV B,H MOV C,L LDA SPT+1 ORA A RNZ MOV B,A ; Make high byte 0 if SPT < 256 RET ;..... ; ; Look through directory ; TRYFIX: CALL NXTREC ; Get a directory record RC ; Returns carry flag if no more CALL CHKENT ; Check it out and maybe fix MVI C,11 ; BDOS console status check CALL BDOS ORA A ; Set flags JZ TRYFIX ; If nothing there, keep it up till done MVI C,1 ; BDOS console input function CALL BDOS CPI 'C'-40H ; Was CTRL-C typed? RZ ; If so, abort via normal exit JMP TRYFIX ; Else keep it up till done ;..... ; ; This is the working copy of the BIOS jump table ; WBOOT: DS 3 CONST: DS 3 CONIN: DS 3 CONOUT: DS 3 LIST: DS 3 PUNCH: DS 3 READER: DS 3 HOME: DS 3 SELDSK: DS 3 SETTRK: DS 3 SETREC: DS 3 SETDMA: DS 3 READ: DS 3 WRITE: DS 3 LISTST: DS 3 ; RECTRAN:DS 3 DPH: DS 2 ; DS 100 ; Stack depth ; STACK: DS 0 ; Location of stack ; END