.TITLE DCLPATCH .IDENT /01-000/ ;++ ; ; Facility: DCLPATCH ; ; Author: Hunter Goatley ; Western Kentucky University ; Academic Computing, STH 226 ; Bowling Green, KY 42101 ; E-mail: goathunter@wkuvx1.bitnet ; Voice: 502-745-5251 ; ; Date: February 21, 1991 ; ; Functional Description: ; ; Applies the "extended RECALL" patch to DCL.EXE. ; ; SYS$SYSTEM:DCL.EXE is read into memory and the instructions to ; to patch are located and replaced. When all patches have been ; made, a new image file, DCL_RECALL.EXE, is created. ; ; Once applied, DCL_RECALL.EXE can recall as many as 62 commands ; (instead of the DCL limit of 20). The limit is imposed by the ; instructions being replaced: the maximum number is stored as a ; short literal, which has a maximum value of 63. Since the code ; actually checks against max+1, 63 became max+1. ; ; Modified by: ; ; 01-000 Hunter Goatley 21-FEB-1991 09:12 ; Original version. ; ;-- .SBTTL Symbols and macros .LINK "SYS$SYSTEM:DCLDEF.STB"/selective_search .DSABL GLOBAL ; Declare external references .ENABL SUPPRESSION ; Don't list unreference symbols .NOSHOW BINARY ; Skip binary until data ; ; External routines: ; .EXTRN LIB$GET_VM ; Allocate memory .EXTRN LIB$PUT_OUTPUT ; Write to SYS$OUTPUT ; ; Global variables used here: ; .EXTRN WRK_B_RECALLCNT ; Symbol from DCLDEF.STB .EXTRN WRK_C_RECALLMAX ; Symbol from DCLDEF.STB $DSCDEF ; Descriptor symbols $FABDEF ; File Access Block symbols $RABDEF ; Record Access Block symbols $RMSDEF ; RMS definitions $SSDEF ; System service status symbols $XABDEF ; Extended attribute block .MACRO ON_ERR LAB,?TMPLAB ;* BRW on error condition BLBS R0,TMPLAB ; Branch if R0 indicates success BRW LAB ; Branch to error address TMPLAB: .ENDM ON_ERR ;* End of ON_ERR macro .MACRO PRINT STRING,?TEXT ;* Macro to print text .SAVE_PSECT LOCAL_BLOCK ;* Save this PSECT .PSECT _DCLPATCH_DATA,NOEXE,WRT,LONG,SHR ;* Change to data PSECT .ALIGN LONG ;* Align on longword TEXT: .ASCID ~STRING~ ;* Create .ASCID string PRINT_TEXT = TEXT ;* Save the address .RESTORE_PSECT ;* Go back to code PUSHAQ PRINT_TEXT ; Write the string CALLS #1,G^LIB$PUT_OUTPUT ; ... to SYS$OUTPUT .ENDM PRINT ;* End of PRINT macro .MACRO REPLACE LEN,OLD,NEW,CNT,ERR,?CONT MOVL LEN,R5 ; R5 = Size MOVAL OLD,R6 ; R6 -> old instruction MOVAL NEW,R7 ; R7 -> new instruction BSBW REPLACE_STREAM ; Go replace it CMPL CNT,R0 ; Right number found? BEQL CONT ; Branch if OK BRW ERR ; Branch to print error message CONT: .ENDM REPLACE ;* End of REPLACE macro .SHOW BINARY ; Include binary in listings .SBTTL Data area .PSECT _DCLPATCH_DATA,NOEXE,WRT,LONG,SHR ; ;*** File Access Block for input ; INFAB: $FAB FNM=, - ; File name FAC=, - ; File Access (GET only) SHR=, - ; Allow others to read also XAB=INXAB ; eXtended attribute block ; ;*** Record Access Block for input ; INRAB: $RAB FAB=INFAB, - ; The File Access Block RAC=SEQ, - ; Record Access is sequential USZ=512 ; The max size of input record INXAB: $XABFHC ; XAB - File Header Chars ; ; ;*** File Access Block for output ; OUTFAB: $FAB FNM=,- FAC=, - ; File Access (GET only) FOP=MXV, - ; Maximize Version number RFM=FIX, - ; VARiable length records MRS=512, - ; Maximum record size ORG=SEQ ; SEQuential organization ; ;*** Record Access Block for output ; OUTRAB: $RAB FAB=OUTFAB, - ; The File Access Block RAC=SEQ, - ; Record Access is sequential RSZ=512 ; Record size is 512 bytes DCL_IMAGE: .LONG 0 ; Holds address of GET_VM mem. IMAGE_SIZE: .LONG 0 ; Size of the image in bytes ; ; The DCL instructions to replace. ; NEW_C_RECALLMAX = 62 ; New limit is 62 commands OINST1: CMPB B^WRK_B_RECALLCNT(R10),- ;Old instruction S^#WRK_C_RECALLMAX+1 ;... (occurs 3 times) OINST1_L = .-OINST1 ;... (calc inst. length) NINST1: CMPB B^WRK_B_RECALLCNT(R10),- ;New instruction S^#NEW_C_RECALLMAX+1 ;... NINST1_L = .-NINST1 ;... ASSUME OINST1_L EQ NINST1_L ;Ensure equal lengths OINST2: CMPL R1,S^#WRK_C_RECALLMAX ;Old instruction 2 OINST2_L = .-OINST2 ;... NINST2: CMPL R1,S^#NEW_C_RECALLMAX ;... NINST2_L = .-NINST2 ;... ASSUME OINST2_L EQ NINST2_L ;... OINST3: MOVL S^#WRK_C_RECALLMAX,R6 ;Old instruction 3 OINST3_L = .-OINST3 ;... NINST3: MOVL S^#NEW_C_RECALLMAX,R6 ;... NINST3_L = .-NINST3 ;... ASSUME OINST3_L EQ NINST3_L ;... OINST4: MOVL S^#WRK_C_RECALLMAX,R9 ;Old instruction 4 OINST4_L = .-OINST4 ;... NINST4: MOVL S^#NEW_C_RECALLMAX,R9 ;... NINST4_L = .-NINST4 ;... ASSUME OINST4_L EQ NINST4_L ;... ; ; DEC's RECALL code that is replaced ; OINST5: CMPB (R2),#^A/1/ ; Is tens digit a "1"? BNEQ 10$ ; Branch if not MOVW #^A"2/",(R2) ; Move "2/" into command buffer BRB 20$ 10$: MOVW #^A"1/",(R2) ; Move "1/" into command buffer 20$: OINST5_L = .-OINST5 NINST5: CMPB (R2),#^A/ / ; Is the tens digit " "? BNEQU 10$ ; Branch if not MOVB #^A/0/,(R2) ; Move a "0" in 10$: INCB (R2) ; Bump tens digit MOVB #^A"/",1(R2) ; Move "/" in after tens digit NOP ; Use NOPs to blank out old NOP ; ... code NOP NINST5_L = .-NINST5 ASSUME OINST5_L EQ NINST5_L .ALIGN LONG PATCH_ADDR_MSG: .ASCID /Patching at image address !XL/ .ALIGN LONG MSGBUF_L = 256 MSGBUF: .WORD MSGBUF_L ; Buffer for messages .BYTE DSC$K_DTYPE_T ; ... Text string .BYTE DSC$K_CLASS_S ; ... Static string .ADDRESS .+4 ; ... Buffer follows .BLKB MSGBUF_L ; The actual output buffer .SBTTL DCLPATCH main routine .PSECT _DCLPATCH_CODE,EXE,NOWRT,LONG,PIC,SHR .ENTRY DCLPATCH,^M PRINT BSBW READ_OLD_IMAGE ; Go open the image ; ; Now do a search and replace for each of the instructions. ; PRINT - REPLACE #OINST1_L,OINST1,NINST1,#3,10$ ; Go replace instruction PRINT REPLACE #OINST2_L,OINST2,NINST2,#1,10$ ; Go replace instruction PRINT REPLACE #OINST3_L,OINST3,NINST3,#1,10$ ; Go replace instruction PRINT REPLACE #OINST4_L,OINST4,NINST4,#1,10$ ; Go replace instruction PRINT REPLACE #OINST5_L,OINST5,NINST5,#1,10$ ; Go replace instructions PRINT ; Print info message BSBW WRITE_NEW_IMAGE ; Go create the "patched" image BLBC R0,20$ ; Branch if not successful PRINT BRB 20$ ; Branch to return to DCL 10$: PRINT PRINT 20$: PUSHL R0 ; Save the status $CLOSE FAB=INFAB ; Close the input file POPL R0 ; Restore the status 30$: RET ; Return to caller ;+ ; ; Function: REPLACE_STREAM ; ; Functional description: ; ; This internal subroutine searches for all occurrences of a stream ; of bytes and replaces the stream with a new stream of bytes. ; ; It is assumed that the streams are the same length. ; ; Inputs: ; ; R5 - Length of stream to find/replace ; R6 - Address of stream of bytes to find ; R7 - Address of stream of replacement bytes ; ; Outputs: ; ; R0 - Number of replacements made ; ;- REPLACE_STREAM: PUSHR #^M MOVL DCL_IMAGE,R4 ; Point to DCL image MOVL IMAGE_SIZE,R3 ; R3 = # of blocks to check MULL2 #512,R3 ; R3 = # of bytes to check CLRL R11 ; Clear # of matches 10$: MOVL R6,R2 ; Move instruction to R2 ; ; Look for the stream of bytes. ; 20$: CMPB (R2),(R4)+ ; Found first byte? BEQL 40$ ; Branch if so 30$: SOBGTR R3,20$ ; Decrement # of bytes to search BRW 70$ ; Branch to return ; ; The first byte has been matched. Check to see if all the others match. ; 40$: DECL R3 ; Decrement # of bytes BEQL 70$ ; Branch if no more image bytes MOVL R5,R0 ; Get length of instruction DECL R0 ; Don't count byte just found MOVL #1,R1 ; Init index value 50$: CMPB (R2)[R1],(R4)+ ; Check each additional byte BNEQ 30$ ; Branch if not the same DECL R3 ; Decrement this byte BEQL 70$ ; Branch if no more image bytes INCL R1 ; Bump index value SOBGTR R0,50$ ; Loop until no more bytes ; ; Here we found an occurrence of the string (all bytes matched). ; MOVW #MSGBUF_L,MSGBUF ; Reset length of output buffer SUBL3 DCL_IMAGE,R4,R0 ; R0 = virtual address SUBL2 R5,R0 ; R0 -- offset in DCL_IMAGE SUBL2 #512,R0 ; Account for header $FAO_S CTRSTR=PATCH_ADDR_MSG,- ; Format the output string OUTBUF=MSGBUF,- ; ... OUTLEN=MSGBUF,- ; ... P1=R0 ; ... PUSHAQ MSGBUF ; Print the string out CALLS #1,G^LIB$PUT_OUTPUT ; ... ; ; Now replace the old stream with the new stream of bytes. ; SUBL2 R5,R4 ; R4 -> beginning of string MOVL R5,R0 ; Copy length to counter MOVL R7,R2 ; R2 -> replacement bytes 60$: MOVB (R2)+,(R4)+ ; Copy the new byte SOBGTR R0,60$ ; Loop until no more bytes INCL R11 ; Increment # of matches BRW 10$ ; Branch to continue search 70$: MOVL R11,R0 ; Return # of matches as status POPR #^M RSB ; Return to caller ;+ ; ; Function: READ_OLD_IMAGE ; ; Functional description: ; ; Opens image file described by INFAB, allocates enough virtual memory ; to contain the whole file, and reads the whole file into memory. ; ; Inputs: ; ; INFAB, INRAB, INXAB, DCL_IMAGE ; ; Outputs: ; ; R0 - RMS Status ; DCL_IMAGE - Address of allocated memory ; IMAGE_SIZE - The size of the file (in blocks) ; ;- READ_OLD_IMAGE: $OPEN FAB=INFAB ; Open the image file ON_ERR 50$ ; Branch on error $CONNECT RAB=INRAB ; Connect the RAB BLBC R0,40$ ; Branch on error MOVL INXAB+XAB$L_EBK,R0 ; Get size of file in blocks MULL2 #512,R0 ; Get size in bytes PUSHL R0 ; Push onto the stack PUSHAL DCL_IMAGE ; Allocate some P0 space PUSHAL 4(SP) ; ... to hold entire file CALLS #2,G^LIB$GET_VM ; Go allocate the memory POPL R1 ; Pop size off stack BLBC R0,40$ ; Branch on error ; ; Read the whole file into one big buffer. ; MOVAL INRAB,R6 ; R6 -> input file RAB MOVL DCL_IMAGE,RAB$L_UBF(R6) ; Start reading into DCL_IMAGE CLRL R7 ; Clear # of blocks 10$: $GET RAB=(R6) ; Read a record BLBS R0,20$ ; Branch if successful CMPL #RMS$_EOF,R0 ; Was it EOF? BEQL 30$ ; Branch if so BRW 40$ ; Branch to report other error 20$: ADDL2 #512,RAB$L_UBF(R6) ; Bump buffer address to next INCL R7 ; ... block (and counter) BRB 10$ ; Branch to read next block 30$: TSTL R7 ; Was anything read? BEQL 40$ ; ... MOVL R7,IMAGE_SIZE ; # of blocks actually read MOVL #SS$_NORMAL,R0 ; Set success status 40$: PUSHL R0 ; Save status $CLOSE FAB=INFAB ; Close the input file POPL R0 ; Restore the status 50$: RSB ; Return to caller ;+ ; ; Function: WRITE_NEW_IMAGE ; ; Functional description: ; ; Creates image file described by OUTFAB and writes data in allocated ; memory to the file (in 512-byte (block) chunks). ; ; Inputs: ; ; OUTFAB, OUTFAB, DCL_IMAGE, IMAGE_SIZE ; ; Outputs: ; ; R0 - RMS Status ; ;- WRITE_NEW_IMAGE: $CREATE FAB=OUTFAB ; Create the output file BLBC R0,30$ ; Branch on error $CONNECT RAB=OUTRAB ; Connect the RAB BLBC R0,20$ ; Branch on error MOVAL OUTRAB,R6 ; R6 -> output file RAB MOVL DCL_IMAGE,RAB$L_RBF(R6) ; Start reading into DCL_IMAGE MOVL IMAGE_SIZE,R7 ; Initialize # of blocks 10$: $PUT RAB=(R6) ; Write a block out BLBC R0,20$ ; Branch on error ADDL2 #512,RAB$L_RBF(R6) ; ... SOBGTR R7,10$ ; Loop until all finished 20$: PUSHL R0 ; Save status $CLOSE FAB=INFAB ; Close the input file POPL R0 ; Restore the status 30$: RSB ; Return to caller .END DCLPATCH