;FIXDEL.M68  Recover a file that has been accidently erased
;Completely rewritten 03-86
;No longer is DSKANA required to retrieve the lost disk blocks
;11-27-86  Change FNDUFD to use Alpha Micro's Call $FNPPN
;          Allow File Spec on command line when calling program
;          (FIXDEL DSK0:[75,1])
; written by Steve Farnum

AUTOEXTERN


       SEARCH  SYS
       SEARCH  SYSSYM
       SEARCH  TRM


BUFF    =       A0
MFD     =       A1
UFD     =       A1
BADD    =       A2
BDDB    =       A3
HADD    =       A4
BMP     =       A5
COUNTR  =       D0
DSPLY   =       D1
OFFSET  =       D2
RECNO   =       D3
FLAGS   =       D4
HASH    =       D5
BMPSIZ  =       D6
UFDLNK  =       2
WIDTH   =       4.
BLOCKS  =       6.
MFDSIZ  =       8.
CONTIG  =       8.
BLOKNO  =       10.
UFDSIZ  =       12.
UFDTOT  =       42.
MFDTOT  =       63.
MFDLNK  =       506.
BLKSIZ  =       ^H0F
DELREC  =       ^H0FFFF                 ; -1 = deleted record
CLRHI   =       ^H0FF

VMAJOR  =       1
VMINOR  =       4
VSUB    =       1
VEDIT   =       100.

PHDR    -1,PH$REE,PH$REU

       OBJNAM  FIXDEL.LIT
       GETIMP  D.DDB,MFD               ; DDB for MFD & UFD directories
       GETIMP  D.DDB,BDDB              ; DDB for bitmap area
       CMPB    (A2),#15                ; anything entered
       BNE     CHKINP                  ; yep, start processing
START:
       TYPESP <Enter Erased File Device & PPN  (DEVN:[P,PN]) >
       KBD     ENDIT
       CMPB    (A2),#15                ; anything entered ?
       JEQ     ENDIT                   ; no, tell user
CHKINP:
       CALL    OPNFIL                  ; open ddb buffers
       CLR     FLAGS                   ; clear flag counter
       CALL    FNDUFD                  ; find desired UFD
       TST     FLAGS                   ; UFD found ?
       JNE     NOPPN                   ; no exit
       CALL    DSPDEL                  ; display deleted records in ppn
       TST     FLAGS                   ; any record found
       JEQ     NODR                    ; no exit
NEWNAM:
       PUSH    DSPLY                   ; save buffer index offset
       PUSH    D.REC(UFD)              ; save ufd record
       CALL    GETBMP                  ; index bitmap area and hash total
       CLR     FLAGS
       CRLF
       TYPESP  <Resetting Bitmap >
       CMPW    CONTIG(BUFF),#DELREC    ; is the file contiguous
       BNE     SETBLK                  ; no, find all blocks
       MOVW    BLOCKS(BUFF),COUNTR     ; no of block to reallocate
CNTFIL:
       CALL    SETBIT                  ; set block in use in bitmap
       TST     FLAGS                   ; block already in use bad news
       JNE     BADFIL                  ; tell user
       INC     RECNO                   ; set next block
       SOB     COUNTR,CNTFIL           ; set all blocks
       JMP     GODFIL                  ; release bitmap
SETBLK:
       MOV     RECNO,D.REC(UFD)        ; set up to read next linked block
       READ    (UFD)                   ; read block
       CALL    SETBIT                  ; set block in use in bitmap
       TST     FLAGS                   ; block already in use bad news
       JNE     BADFIL                  ; tell user
       MOV     D.BUF(UFD),BUFF         ; check if there is another block linked
       MOVW    (BUFF),RECNO            ; next link
       TST     RECNO
       BNE     SETBLK                  ; yes, set bit
       JMP     GODFIL
SETBIT:
       CLR     DSPLY                   ; clear work reg
       PUSH    RECNO                   ; save record number
       DIV     RECNO,#^H10             ; divide by 16 (a word)
       MOVW    RECNO,OFFSET            ; compute bitmap word
       ASL     OFFSET                  ; mul * 2 to find correct offset
       MOVW    0(BADD)[~OFFSET],DSPLY  ; get bitmap word
       SWAP    RECNO                   ; get remainder = bit to set
       BTST    RECNO,DSPLY             ; is block free
       BEQ     SETOK                   ; yes, ok to set block
       SET     FLAGS                   ; no output error message
       BR      ENDBIT
SETOK:
       SUB     DSPLY,HASH              ; calculate new hash total
       BSET    RECNO,DSPLY             ; set block in bitmap
       MOVW    DSPLY,0(BADD)[~OFFSET]  ; write new bitmap block word
       ADD     DSPLY,HASH              ; update hash total
       TYPE    <.>
ENDBIT:
       POP     RECNO                   ; restore record number
       RTN
GETBMP:
       DSKBMR  (BDDB)                  ; read & lock bitmap
       MOV     D.ARG(BDDB),BMP         ; get address of bitmap area
       MOV     BMP,BADD                ; save bitmap address
       SUB     #BM.SIZ,BMP             ; index bitmap ddb
       MOV     D.SIZ(BMP),BMPSIZ       ; no of bytes in bitmap + hash total
       SUB     #4,BMPSIZ               ; compute bitmap size in bytes
       MOV     D.BUF(BMP),HADD         ; compute hash total address
       ADD     BMPSIZ,HADD             ;
       MOV     (HADD),HASH             ; get bitmap hash total
       SWAP    HASH                    ;
       RTN

; Allocate DDB For reading and writing
OPNFIL:
       PUSH    A2                      ; save file spec pointer
       CLR     D.DEV(BDDB)
       CLR     D.FIL(BDDB)
       CLR     D.EXT(BDDB)
       CLR     D.DEV(MFD)
       CLR     D.FIL(MFD)
       CLR     D.EXT(MFD)
       FSPEC   (BDDB)                  ; setup for bitmap read
       INIT    (BDDB)
       POP     A2                      ; restore ascii file spec
       FSPEC   (MFD)
       INIT    (MFD)                   ; get buffers for input ddb
       RTN

; Read the MFD and find desired UFD on specified device
FNDUFD:
       CLR     D1
       MOVW    D.PPN(MFD),D1           ; octal ppn now in d1
       MOV     MFD,A2                  ; set A2 fo ppn find call
       CALL    $FNPPN
       MOV     D0,FLAGS                ; D0 = completions codes
       CLR     RECNO
       CLR     OFFSET
       MOVW    UFDLNK(MFD),RECNO       ; store ufd link address
       MOV     A2,MFD
       RTN

;Target PPN found check for deleted records
DSPDEL:
REDPPN:
       CLR     FLAGS                   ; no deleted records yet
       TST     RECNO                   ; at end of ufd link
       JEQ     ENDDSP                  ; yes exit program
       MOV     RECNO,D.REC(UFD)        ; set up for block read
       READ    (UFD)                   ; read ufd
       MOV     D.BUF(UFD),BUFF         ; save buffer address
       MOVW    #^H0FF00,DSPLY          ; clear screen
       TCRT
       CRLF
       MOV     #UFDTOT,COUNTR          ; max ufd's per block
       MOVW    (BUFF)+,RECNO           ; next ufd block link 1st word
       CLR     DSPLY
PPNLOP:
       CMPW    (BUFF),#DELREC          ; deleted record ?
       BNE     NXTFIL                  ; no, look at nxt file
       TYPE    <(>                     ; output file name to screen
       DCVT    2,OT$TRM
       TYPE    <)>
       PRNAM   (BUFF)
       INC     OFFSET
       CMP     OFFSET,#WIDTH
       BLT     SPACE
       CRLF
       CLR     OFFSET
       BR      NOSPAC
SPACE:
       TAB
NOSPAC:
       SET     FLAGS                   ; deleted records found
NXTFIL:
       INC     DSPLY                   ; index next filname
       ADDW    #UFDSIZ,BUFF            ; and display it
       SOB     COUNTR,PPNLOP
;Check for another UFD for this PPN
;Or for the file to save
NXTUFD:
       TST     FLAGS                   ; any deleted records found
       JEQ     REDPPN                  ; no read next ufd link
       CRLF
       CRLF
       TYPESP  <Enter Number Of Erase File or CR For Next Block >
       KBD     ENDIT
       CMPB    (A2),#15                ; anything entered ?
       JEQ     REDPPN                  ; no, check for another ufd
       CLR     DSPLY                   ; conver ascii to a number
       GTDEC
       TYPESP  <Enter New Filename (FILNAM.EXT) >
       KBD     ENDIT
       MOV     D.BUF(UFD),BUFF         ; index buffer start
       ADD     #2,BUFF                 ; skip link word
       MUL     DSPLY,#UFDSIZ           ; calulate offset size * ufdsiz
       ADD     DSPLY,BUFF              ; index offset
       FILNAM  D.FIL(UFD)              ; write new file name to ufd
       MOVW    BLOKNO(BUFF),RECNO      ; set up to read the block
ENDDSP:
       RTN

GODFIL:
       POP     D.REC(UFD)              ; restore ufd record number
       READ    (UFD)                   ; re-read ufd record
       POP     DSPLY
       MOV     D.BUF(UFD),BUFF         ; index buffer start
       ADD     #2,BUFF                 ; skip link word
       ADD     DSPLY,BUFF              ; index offset
       MOVW    D.FIL(UFD),(BUFF)       ; write new file name to ufd
       MOV     <D.FIL+2>(UFD),2(BUFF)
       WRITE   (UFD)                   ; rewrite UFD block
       SWAP    HASH                    ; write out new bitmap
       MOV     HASH,(HADD)             ; hash total
       ORW     #BM$REW,<BM.SIZ-2>(BMP) ; set bitmap rewrite flag
       XORW    #BM$LOK,<BM.SIZ-2>(BMP) ; clear bitmap lock flag
       DSKBMW  (BDDB)                  ; rewite bitmap
       CRLF
       CRLF
       TYPECR  <Your Erased File Has Now Been Restored !!!!!! >
       JMP     ENDIT
BADFIL:
       POP                             ; clear stack
       POP                             ; clear stack
       XORW    #BM$LOK,<BM.SIZ-2>(BMP) ; clear bitmap lock flag
       CRLF
       TYPECR  <I am sorry your file cannot be fully recovered.>
       TYPECR  <Some blocks have already been reassigned to another file.>
       TYPECR  <Run DSKANA to recover any blocks that may have been set.>
       TYPECR  <You should really be more carefull in the future>
       BR      ENDIT
NOPPN:
       TYPECR  <That PPN does not exist on specified device>
       BR      ENDIT
NODR:
       TYPECR  <No deleted records found in specified PPN>
       BR      ENDIT
ENDIT:
       CRLF
       TYPESP  <Want to unerase another File ??? >
       KBD     THEEND
       CMPB    (A2),#15                ; anything entered ?
       BEQ     THEEND                  ; no, tell user
       CMPB    (A2),#'N
       BEQ     THEEND
       CMPB    (A2),#'n
       BEQ     THEEND
       JMP     START
THEEND:
       CRLF
       EXIT
       END