; U3 - CP/M PLUS UTILITY FOR RECOVERING ERASED FILES AND FOR CHANGING ; THE USER AREA OF A FILE - VER 1.02 12/24/84. ; ; 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 recovers all erased files on drive D: ; (or the default if D: is not specified) matching afn.aft and places ; them in 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 re- ; visions may be passed to the author by leaving a message on either: ; ; Mesilla Valley RCP/M | (505)-524-6920 ; or ; 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 MODIFICA- ; Ver. 1.00 TIONS 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, IT 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 ; CP/M 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 CP/M 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 for CP/M 3.0 - 12/24/84' ; ORG 200H START: LXI SP,START CALL SETUP CALL RDDIR CALL FINISH JMP WRMBOOT SETUP: CALL CKVERS ; Check for CP/M 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 directory 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 CP/M version # CALL BDOS CPI 30H ; Is it 3.0 or greater JC ONLY30 ; This version for CP/M 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 destination 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 ; Destination 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 ; 0E5H = 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 parameters 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 DIRSEC 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 CP/M Plus only CALL PRTMSG JMP WRMBOOT GETDIR: LDA DIRSEC ; Get # of directory sectors ORA A RZ ; Return if none CALL PHSECTRK ; Compute cur. 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 dir. 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 cur. 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 DIRSEC ; Decrement directory sectors DCR A STA DIRSEC 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 dir. entries per sector STA DVD JMP DIV32 PSH512: MVI A,16 ; Set number of dir. entries per sector STA DVD JMP DIV16 PSH256: MVI A,8 ; Set number of dir. entries per sector STA DVD JMP DIV8 PSH128: MVI A,4 ; Set number of dir. 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 ; ;----------------------------------------------------------------------- ; ; Data area ; FUN50: FUNC: DB 0 AREG: DB 0 BCREG: DB 0,0 DEREG: DB 0,0 HLREG: DB 0,0 ; DPHADD: DB 0,0 DIRSEC: DB 0 RESTKS: DB 0,0 SECTOR: DB 0 NOREC: DB 0 RECFLG: DB 0 PSH: DB 0 DVD: DB 0 CURUSR: DB 0 SPT: DB 0,0 PHSPT: DB 0 PHSECT: DB 0 PHTRK: DB 0 SRCUSR: DB 0 DSTUSR: DB 0 ; ; Messages ; 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,'++ U3 ONLY WORKS FOR CP/M V3.0 OR LATER ++' DB CR,LF,'$' ; ; Buffer area ; LOCDMA: DS 1024 ; Local DMA buffer ; ; END