; U3 - CP/M PLUS UTILITY FOR RECOVERING ERASED FILES AND FOR ; CHANGING THE USER AREA OF A FILE. This program is based on ; UNERASE.COM in the public domain. It has been modified to ; work with CP/M 3.0 and later, to change the user area of a ; file, and to accept ambiguous files names. It currently ; works with sector sizes of 128, 256 512 and 1024 bytes. It ; is placed in the public domain by Advanced Logic Systems for ; public use. Advanced Logic Systems makes no warrantee on the ; operation or use of this program or its applicability for any ; given application. ; U3 DOES NOT DOES NOT WORK WITH VERSIONS OF CP/M PRIOR TO CP/M 3.0 ; The U3 command line takes the form: ; ; U3 [d:]afn.aft [user area] ; ; If no user area is specified U3 recoverers all erased files on ; drive d: (or the default if d: is not specified) matching ; afn.aft and places them is the current user area. If a user area ; is specified U3 moves all files in the current user area on ; drive d: (or the default if d: is not specified) matching ; afn.aft to the specified user area. ; Versions 1.01 and 1.02 of U3 were prepared by R. Saeks from ; Gale Wolfenbarger's code for version 1.00. Comments regarding ; these revisions may be passed to the author by leaving a ; message on either the ; ; Mesilla Valley RCP/M | (505)-524-6920 ; or the ; Lost Dutchman's Gold Mine RCP/M | (602)-848-6708 ; HISTORY ;12/24/84 REVISED CODE TO PERMIT U3 TO BE USED TO EITHER ;Ver. 1.02 RECOVER AN ERASED FILE OR CHANGE THE USER AREA ; OF A FILE. ADDED AMBIGUOUS FILE SPECIFICATIONS ; REVISED CODE TO WORK ON DISKS WITH ; MORE THAN ONE DIRECTORY TRACK. MOVED CPM 3.0 ; VERSION CHECK CODE TO BEGINNING OF CODE AND ; CORRECTED ERROR TEST IN NODEF. - R. SAEKS ;06/16/84 REVISED TO WORK WITH 1024 BYTE SECTORS AND TO PLACE ;Ver. 1.01 RECOVERED FILES IN CURRENTLY LOGGED USER AREA. ; - R. SAEKS ;12/29/83 FIRST PASS AT DISASSEMBLY OF UNERASE.COM AND MODIFICATIONS ;Ver. 1.00 TO MAKE IT COMPATIBLE WITH CP/M PLUS ; I ALSO DID A SELECT DISK IN DSKPRM SO THAT IF YOU TRY TO ; RECOVER A FILE FROM A DISK THAT IS A DIFFERENT FORMAT FROM ; THE ONE THAT YOU ARE CURRENTLY LOGGED ON TO, ITS GETS THE ; RIGHT DPH. AT THE SAME TIME I CHANGED THE SIZE OF THE AREA ; WHERE THE NUMBER OF RESERVED SYSTEM TRACKS IS KEPT. THIS ; ALLOWS THE NUMBER OF RESERVED TRACKS TO BE GREATER THAN 256. ; - GALE WOLFENBARGER ; ADDRESSES BDOS: EQU 05H ;BDOS VECTOR ADDRESS WRMBOOT:EQU 00H ;WARM BOOT VECTOR ADDRESS DEFFCB: EQU 5CH ;ADDRESS OF THE DEFAULT FCB DEFFCB2:EQU 6CH ;ADDRESS OF THE SECOND DEFAULT FCB ; BDOS FUNCTIONS DIRCALL:EQU 32H ;CPM 3.0 DIRECT BIOS CALL DRESET: EQU 0DH ;RESET DISK SYSTEM GETCUR: EQU 19H ;GET CURRENTLY LOGGED IN DISK GETUSR: EQU 20H ;GET CURRENT USER CONIN: EQU 01H ;GET A CHARACTER FROM THE CONSOLE CONOUT: EQU 02H ;PRINT A CHARACTER TO THE CONSOLE SETDMA: EQU 1AH ;SET DMA FUNCTION GETVER: EQU 0CH ;GET CPM VERSION # GETPARM:EQU 1FH ;GET ADDRESS OF DISK PARAMETERS PRINT: EQU 09H ;PRINT STRING TO CONSOLE ; ASCII CHARACTERS CR: EQU 0DH ;CARRIAGE RETURN LF: EQU 0AH ;LINE FEED ; I USED MACROS FROM Z80.LIB TO IMPLEMENT SOME Z80 COMMANDS ; BECAUSE MAC.COM AND Z80.LIB COME WITH CP/M PLUS. ; IF YOU ARE USING ASM.COM OR NOT USING A Z80 PROCESSOR ; YOU WILL HAVE TO USE EQUATES OR INSERT INLINE CODE FOR THE ; Z80 MACROS - gw MACLIB Z80 ORG 100H JMP START DB 'U3 ver 1.02' DB 'FOR CPM 3.0' DB '12/29/83 - gw' DB 'Rev 06/16/84, 12/24/84 - rs' ORG 200H START: LXI SP,START CALL SETUP CALL RDDIR CALL FINISH JMP WRMBOOT SETUP: CALL CKVERS ;CHECK FOR CPM 3.0 OR LATER CALL CKNAME ;CHECK FOR VALID FILE NAME CALL SETSRCDST ;SET SOURCE AND DESTINATION USERS CALL SETBIOS ;COPY BIOS JUMP VECTOR RET RDDIR: CALL GETDIR ;READ A DIRECTORY SECTOR RZ ;END OR READ ERROR CALL CKENT ;CHECK ENTRIES FOR FILE JMP RDDIR FINISH: MVI C,DRESET ;RESET DISK SYSTEM CALL BDOS LDA NOREC ;GET NUMBER of DIR FOUND ORA A JZ NOTFND ;NON FOUND LDA SRCUSR CPI 0E5H JZ RECOVMSG LXI D,CHGMSG JMP PRINTMSG RECOVMSG: LXI D,RECMSG PRINTMSG: CALL PRTMSG ;PRINT SUCCESS MESSAGE RET NOTFND: LXI D,FILERR CALL PRTMSG RET CKVERS: MVI C,GETVER ;GET CPM VERSION # CALL BDOS CPI 30H ;IS IT 3.0 OR GREATER JC ONLY30 ;THIS VERSION FOR CPM 3.0 AND LATER RET CKNAME: LDA DEFFCB ;GET DRIVE FOR FILE ORA A ;USE DEFAULT? JNZ NODEF ;NO CONVERT TO FCB FORMAT MVI C,GETCUR ;GET CURRENT DISK CALL BDOS INR A ;SET UP FOR DECREMENT NODEF: DCR A ;CONVERT TO FCB FORMAT STA DEFFCB ;SAVE IT LDA DEFFCB+1 ;CHECK FOR NO FILE CPI ' '+1 RNC ;RETURN IF OK LXI D,NAMERR ;PRINT FILE ERROR CALL PRTMSG JMP WRMBOOT ;GIVE UP SETSRCDST: MVI C,GETUSR ;GET USER # MVI E,0FFH ;GET USER FLAG CALL BDOS STA CURUSR ;SAVE USER # LXI H,DEFFCB2+1 ;POINT TO 1ST DESTINATION CHAR. MVI A,' ' CMP M JZ UNERASE ;UNERASE IF NO DEST. USER # MVI B,'0' MOV C,M INX H CMP M JZ CHGUSR ;SINGLE DIGIT ASCII USER # IN BC MOV B,C MOV C,M INX H CMP M JNZ USRERROR ;TWO DIGIT ASCII USER # IN BC IF ZERO CHGUSR: MVI D,0 ;INITIAL VALUE FOR HIGH DIGIT MVI A,'0' CMP B JZ CHGUSR1 ;OK IF HIGH DIGIT IS '0' INR A ;A = '1' CMP B JNZ USRERROR ;ERROR IF HIGH DIGIT NOT '0' OR '1' MVI D,10 ;VALUE FOR HIGH DIGIT IF '1' CHGUSR1: MVI A,'0'-1 CMP C JNC USRERROR ;ERROR IF LOW DIGIT < '0' MVI A,'9' CMP C JC USRERROR ;ERROR IF LOW DIGIT > 9 MOV A,C SBI '0' ;MAKE LOW DIGIT HEX ADD D CPI 16 JNC USRERROR ;ERROR IF USER # > 15 STA DSTUSR ;DEST. USER # IN HEX LDA CURUSR STA SRCUSR ;SOURCE USER # IS CURRENT USER # RET UNERASE: LDA CURUSR ;PLACE FILE IN CURRENT USER STA DSTUSR MVI A,0E5H ;E5H = ERASED FILE "USER #" STA SRCUSR RET USRERROR: LXI D,USRERR CALL PRTMSG JMP WRMBOOT SETBIOS: ;SET UP LOCAL BIOS JUMP VECTOR LXI B,LOCDMA ;GET DEFAULT DMA ADDRESS SBCD BCREG MVI A,12 ;SET DMA TO DEFAULT STA FUNC CALL CALLBIOS CALL DSKPRM ;GET DISK PARAMETERS FROM BDOS LDA DEFFCB ;SELECT DRIVE MOV C,A MVI B,0 MOV E,B SBCD BCREG ;BIOS SELDSK FUNCTION SDED DEREG MVI A,9 ;SELDSK STA FUNC CALL CALLBIOS MOV A,H ORA L ;CHECK FOR DRIVE ERROR JZ NODRV ;GO REPORT IT MOV E,M ;GET DPH ADDRESS INX H MOV D,M XCHG SHLD DPHADD ;SAVE ADDRESS TO DPH RET DSKPRM: LDA DEFFCB MOV E,A MVI C,14 ;SELECT DISK CALL BDOS MVI C,GETPARM ;GET ADDRESS OF DISK PARMS CALL BDOS MOV E,M ;GET LOGICAL SPT INX H MOV D,M SDED SPT LXI D,6 ;OFFSET TO DRM (# OF DIRECTORY ENTRIES) DAD D ;ADD OFFSET MOV E,M ;GET DRM INX H MOV D,M PUSH D PUSH H LXI D,7 ;POINT TO PSH DAD D MOV A,M ;GET IT STA PSH ;SAVE IT POP H POP D XCHG INX H ;ADD 1 FOR TOTAL DIRECTORY ENTRIES LDA PSH CPI 3 ;1024 BYTE SECTORS CZ PSH1024 LDA PSH CPI 2 ;512 BYTE SECTORS CZ PSH512 LDA PSH CPI 1 ;256 BYTE SECTORS CZ PSH256 LDA PSH ORA A ;128 BYTE SECTORS CZ PSH128 MOV A,L ;SAVE # OF DIRECTORY SECTORS STA DIRSECTS LHLD SPT ;LOGICAL SECTORS PER TRACK LDA PSH CPI 3 ;1024 BYTE SECTORS CZ DIV8 LDA PSH CPI 2 ;512 BYTE SECTORS CZ DIV4 LDA PSH CPI 1 ;256 BYTE SECTORS CZ DIV2 MOV A,L ;PHSPT = SPT FOR 128 BYTE SECTORS STA PHSPT ;PHYSICAL SECTORS PER TRACK LXI H,5 ;ADD OFFSET TO NUMBER OF RESERVED TRACKS DAD D MOV E,M ;GET NUMBER OF RESERVED TRACKS INX H MOV D,M SDED RESTKS ;SAVE RESERVED TRACKS FOR LATER RET ONLY30: LXI D,ERR30 ;PRINT MESSAGE FOR CPM PLUS ONLY CALL PRTMSG JMP WRMBOOT GETDIR: LDA DIRSECTS ;GET # OF DIRECTORY SECTORS ORA A RZ ;RETURN IF NONE CALL PHSECTRK ;COMPUTE CURRENT PHYSICAL SECTOR AND TRACK LBCD RESTKS ;GET NUMBER OF RESERVED TRACKS IN BC MVI H,0 ;GET PHYSICAL TRACK NUMBER IN HL LDA PHTRK MOV L,A DAD B ;DIRECTORY TRACK IN HL SHLD BCREG ;SET TRACK TO DIRECTORY MVI A,10 ;SETTRK STA FUNC CALL CALLBIOS LDA PHSECT ;GET CURRENT PHYSICAL SECTOR MOV C,A CALL TRNSEC ;TRANSLATE SECTOR IF NECESSARY MVI B,0 SBCD BCREG ;POINT TO CURRENT SECTOR MVI A,11 ;SETSEC STA FUNC CALL CALLBIOS MVI A,13 ;READ SECTOR OF DIRECTORY STA FUNC CALL CALLBIOS ANI 1 XRI 1 RET CKENT: XRA A ;ZERO FLAG STA RECFLG LDA DVD ;GET NUMBER OF DIRECTORY ENTRIES PER SECTOR MOV B,A LXI H,LOCDMA CKNXT: LDA SRCUSR MOV D,A ;SOURCE USER # IN D MOV A,M ;GET BYTE CMP D ;CHECK FOR SOURCE USER # JNZ GETNXT ;NOPE SKIP PUSH H CALL CKFIL ;YES CHECK FOR RECOVERY POP H RECENT: JNZ GETNXT LDA DSTUSR MOV M,A ;RECOVER/CHANGE THE ENTRY MVI A,0FH ;SET RECOVERED FLAG STA RECFLG LDA NOREC ;INCREMENT NUMBER OF RECORDS FOUND INR A STA NOREC GETNXT: LXI D,32 ;ADVANCE TO NEXT ENTRY DAD D DCR B ;DECREMENT COUNTER JNZ CKNXT LDA RECFLG ;CHECK FOR RECOVERED/CHANGED ENTRY ORA A JZ LOOP ;NOPE CONTINUE LOOP ;WRITE SECTOR BACK TO DISK IF FILE REC/CHG CALL PHSECTRK ;COMPUTE CURRENT PHYSICAL SECTOR AND TRACK LBCD RESTKS ;GET NUMBER OF RESERVED TRACKS IN BC MVI H,0 ;GET PHYSICAL TRACK NUMBER IN HL LDA PHTRK MOV L,A DAD B ;DIRECTORY TRACK IN HL SHLD BCREG ;SET TRACK TO DIRECTORY MVI A,10 ;SETTRK STA FUNC CALL CALLBIOS LDA PHSECT ;GET CURRENT PHYSICAL SECTOR MOV C,A CALL TRNSEC ;TRANSLATE SECTOR MVI B,0 SBCD BCREG ;POINT TO CURRENT SECTOR MVI A,11 ;SETSEC STA FUNC CALL CALLBIOS LXI B,1 ;NONE DEFERRED WRITE SBCD BCREG MVI A,14 ;WRITE STA FUNC CALL CALLBIOS ;UPDATE DIRECTORY ORA A ;CHECK FOR WRITE ERROR JNZ WRTERROR ;REPORT ERROR LOOP: LDA DIRSECTS ;DECREMENT DIRECTORY SECTORS DCR A STA DIRSECTS LDA SECTOR ;INCREMENT SECTOR INR A STA SECTOR RET CKFIL: INX H ;CHECK DELETED ENTRY FOR RECOVERY LXI D,DEFFCB+1 ;POINT AT FILE TO BE RECOVERED XCHG MVI C,11 ;LOOK AT 11 CHARACTERS CMPFIL: MVI A,'?' ;CHECK TO SEE IF SPEC IS AMBIGUOUS CMP M JZ NXTCHR LDAX D ;GET CHARACTER IN NAME ANI 7FH ;TURN OFF HIGH BIT CMP M ;ARE THEY EQUAL? RNZ ;NOPE GO GET ANOTHER ENTRY NXTCHR: INX D ;YES, CONTINUE INX H DCR C JNZ CMPFIL RET PSH1024: MVI A,32 ;SET NUMBER OF DIRECTORY ENTRIES PER SECTOR STA DVD JMP DIV32 PSH512: MVI A,16 ;SET NUMBER OF DIRECTORY ENTRIES PER SECTOR STA DVD JMP DIV16 PSH256: MVI A,8 ;SET NUMBER OF DIRECTORY ENTRIES PER SECTOR STA DVD JMP DIV8 PSH128: MVI A,4 ;SET NUMBER OF DIRECTORY ENTRIES PER SECTOR STA DVD JMP DIV4 DIV32: ;DIVIDE HL BY 32 CALL DIV2 DIV16: ;DIVIDE HL BY 32 CALL DIV2 DIV8: ;DIVIDE HL BY 32 CALL DIV2 DIV4: ;DIVIDE HL BY 32 CALL DIV2 DIV2: ;DIVIDE HL BY 2 ROUTINE XRA A ;CLEAR CARRY MOV A,H ;GET HIGH ORDER BYTE RAR ;SHIFT RIGHT FOR A DIVIDE BY 2 MOV H,A MOV A,L ;GET LOW ORDER BYTE RAR ;SHIFT RIGHT FOR A DIVIDE BY 2 MOV L,A RET PHSECTRK: MVI C,0 ;SET PHTRK TO 0 LDA PHSPT MOV B,A ;PHYS SPT IN B LDA SECTOR ;LOGICAL DIRECTORY SECTOR CKPHTRK: CMP B ;COMPUTE PHSECT = SECTORS MOD PHSPT JC STSECTRK ;AND PHTRK = (SECTORS - PHSECT)/PHSPT SUB B INR C JMP CKPHTRK STSECTRK: STA PHSECT MOV A,C STA PHTRK RET PRTMSG: MVI C,PRINT ;PRINT STRING TO CONSOLE JMP BDOS NODRV: ;PRINT DRIVE ERROR MESSAGE LXI D,DRVERR CALL PRTMSG JMP WRMBOOT WRTERROR: ;PRINT WRITE ERROR MESSAGE LXI D,WRTERR CALL PRTMSG JMP WRMBOOT TRNSEC: LHLD DPHADD ;GET TRANSLATE TABLE ADDRESS XCHG SBCD BCREG SDED DEREG MVI A,16 ;SECTRAN STA FUNC CALL CALLBIOS MOV C,L RET CALLBIOS: ;CPM 3.0 DIRECT BIOS CALL MVI C,DIRCALL LXI D,FUN50 CALL BDOS RET FUN50: FUNC: DS 1 AREG: DS 1 BCREG: DS 2 DEREG: DS 2 HLREG: DS 2 DPHADD: DS 2 DIRSECTS: DS 1 RESTKS: DS 2 SECTOR: DB 0 NOREC: DB 0 RECFLG: DB 0 PSH: DS 1 DVD: DB 0 CURUSR: DS 1 SPT: DS 2 PHSPT: DS 1 PHSECT: DS 1 PHTRK: DS 1 SRCUSR: DS 1 DSTUSR: DS 1 WRTERR: DB CR,LF,'Error occurred during disk Write - ABORT$' DRVERR: DB CR,LF,'Specified an illegal disk drive - ABORT$' RECMSG: DB CR,LF,'File recovered.$' CHGMSG: DB CR,LF,'File user area changed.$' NAMERR: DB CR,LF,'No File Name specified - ABORT$' FILERR: DB CR,LF,'File NOT found$' USRERR: DB CR,LF,'Invalid User Number - ABORT$' ERR30: DB CR,LF,'THIS VERSION OF U3 ONLY WORKS' DB CR,LF,'FOR CPM 3.0 OR LATER',CR,LF,'$' LOCDMA: DS 1024 ;LOCAL DMA BUFFER END