.TITLE DCLPATCH .IDENT /02-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. ; ; Under AXP, the limit is 99 characters. The limit could be ; higher, but we're restricted to 2 digits for the RECALL/ALL ; display. ; ; Modified by: ; ; 02-000 Hunter Goatley 13-MAR-1993 20:52 ; Merge the two into one program. ; ; 01-002 Ehud Gavron 15-Jan-1993 All day! ; Patch DCL.EXE so RECALL/ALL numbers correctly. ; Remove conditional IFDEFs on .JSB_ENTRYs. ; ; 01-001 Hunter Goatley November 1992 ; Converted to work on AXP. All but RECALL/ALL numbers. ; ; 01-000 Hunter Goatley 21-FEB-1991 09:12 ; Original version. ; ;-- .SBTTL Symbols and macros ; ; To build DCLPATCH on the VAX but allow it to handle AXP DCL.EXE images, ; uncomment the following line. ; ;DO_ALPHA = 1 ; ; To build DCLPATCH on AXP but allow it to handle VAX DCL.EXE images, ; uncomment the following line. ; ;DO_ALPHA = 0 ; ; This line says, "If DO_ALPHA is not defined and we're compiling under ; OpenVMS AXP, then define the DO_ALPHA symbol." ; .IF NOT_DEFINED DO_ALPHA ; If user has not set a preference, DO_ALPHA = 0 ; ... then assume VAX unless .IIF DF,EVAX, DO_ALPHA = 1 ; ... compiling under AXP .ENDC .IF NOT_DEFINED EVAX ; .PRINT is ugly under AXP compiler .IF EQ DO_ALPHA .PRINT ;Producing VAX version of DCLPATCH .IFF .PRINT ;Producing AXP version of DCLPATCH .ENDC .ENDC .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: ; .IF NOT_DEFINED EVAX .EXTRN WRK_B_RECALLCNT ; Symbol from DCLDEF.STB .EXTRN WRK_C_RECALLMAX ; Symbol from DCLDEF.STB .ENDC $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 DNM=,- ; Default 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 ;+ ; ; The sections below include the AXP instruction sequences that are to ; be modified, followed by the actual new values. The AXP listings came ; from the DCL source listings CD for OpenVMS AXP V1.0. ; ;- .IF NE DO_ALPHA ; The AXP sections.... ;A30AFFC0 0FB0 LDL R24, -64(R10) ; 003517 ;4B0070D8 0FB4 EXTBL R24, 3, R24 ;4302B530 0FB8 SUBQ R24, 21, R16 ;E61FFF06 0FBC BEQ R16, 22_30$ ; 003519 ;43C1153E 0FC0 SUBQ SP, 8, SP ; 003521 OINST1: .LONG ^X4302B530 OINST1_L = .-OINST1 NINST1: .LONG ^X430C9530 ; 21 -> 100 NINST1_L = .-NINST1 ;010000 ;11000 24 ;00010101 21 => ; ;01100100 100 ;11111111 255 ;1 Bit 12 indicates that it is a literal ; 0101 0011 0000 ; ;A2EAFFC0 13E0 LDL R23, -64(R10) ; 003439 ;4AE070D7 13E4 EXTBL R23, 3, R23 ;42E2B530 13E8 SUBQ R23, 21, R16 ;E61FFDEC 13EC BEQ R16, END_OF_LIST ; 003441 OINST2: .LONG ^X42E2B530 OINST2_L = .-OINST2 NINST2: .LONG ^X42EC9530 ; 21 -> 100 NINST2_L = .-NINST2 ;A34AFFC0 1450 LDL R26, -64(R10) ;4B4070DA 1454 EXTBL R26, 3, R26 ;4342B530 1458 SUBQ R26, 21, R16 ;E61FFDCE 145C BEQ R16, 19_20$ ; 003454 OINST3: .LONG ^X4342B530 OINST3_L = .-OINST3 NINST3: .LONG ^X434C9530 ; 21 -> 100 NINST3_L = .-NINST3 ;47E0341A 1FD4 BIS R31, 1, R26 ; 004209 ;40229530 1FD8 SUBQ R1, 20, R16 ; 004208 ;402293B1 1FDC CMPULT R1, 20, R17 ;461F04DA 1FE0 CMOVNE R16, R31, R26 ; 004209 OINST4: .LONG ^X40229530 .LONG ^X402293B1 OINST4_L = .-OINST4 NINST4: .LONG ^X402C7530 .LONG ^X402C73B1 NINST4_L = .-NINST4 ;47E29406 2078 BIS R31, 20, R6 ;F120005E 207C BLBS R9, 32_50$ ; 004236 ;45205017 2080 AND R9, 2, R23 ; 004237 OINST5: .LONG ^X47E29406 OINST5_L = .-OINST5 NINST5: .LONG ^X47EC7406 NINST5_L = .-NINST5 ;43C1153E 20C0 SUBQ SP, 8, SP ; 004248 ;47E29409 20C4 BIS R31, 20, R9 ; 004247 ;B7FE0000 20C8 STQ R31, (SP) ; 004248 OINST6: .LONG ^X47E29409 OINST6_L = .-OINST6 NINST6: .LONG ^X47EC7409 NINST6_L = .-NINST6 OINST8: .LONG ^X43063530 OINST8_L = .-OINST8 NINST8: .LONG ^X43041530 NISTN8_L = .-NINST8 OINST10: .LONG ^X239F2F32 OINST10_L =.-OINST10 NINST10: .LONG ^X239F2F31 NINST10_L =.-NINST10 ; ; The RECALL display code. Same basic modification as for the VAX. ; OINST9: .LONG ^X2F820000 ; LDQ_U R28,(R2) .LONG ^X22DF2F31 ; LDA R22, 12081(R31) .LONG ^X4AC2037A ; INSWL R22, R2, R26 .LONG ^X4B82025C ; MSKWL R28, R2, R28 .LONG ^X479A041C ; BIS R28, R26, R28 .LONG ^XF0400036 ; BLBS R2, $L51 .LONG ^X3F820000 ; $L52: STQ_U R28, (R2) .LONG ^X47FF041F ; NOP OINST9_L = .-OINST9 NINST9: .LONG ^XA3420000 ; LDL R26, (R2) .LONG ^X22DF09FF ; LDA R22, 2559(R31) .LONG ^X4356013A ; SUBL R26, R22, R26 .LONG ^XB3420000 ; STL R26, (R2) .LONG ^X2FFE0000 ; LNOP .LONG ^X47FF041F ; NOP .LONG ^X47FF041F ; NOP .LONG ^X47FF041F ; NOP NINST9_L = .-NINST9 ;+ ; ; The sections below contain the VAX instructions that are to be replaced. ; The values are stored as hexadecimal values for the MACRO-32 compiler, ; which would produce AXP object code, not VAX object code. To enhance ; readability, the original VAX instructions are still included for ; assembling on the VAX. ; ;- .IFF ;Producing VAX version..... .IF DEFINED EVAX ;Compiling for AXP.... OINST1: .LONG ^X15C3AA91 OINST1_L = 4 NINST1: .LONG ^X3FC3AA91 NINST1_L = 4 .IFF ;Assembling for VAX.... 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 ;... .ENDC ASSUME OINST1_L EQ NINST1_L ;Ensure equal lengths .IF DEFINED EVAX ;Compiling for AXP.... OINST2: .BYTE ^XD1,^X51,^X14 OINST2_L = 3 NINST2: .BYTE ^XD1,^X51,^X3E NINST2_L = 3 .IFF ;Assembling for VAX.... OINST2: CMPL R1,S^#WRK_C_RECALLMAX ;Old instruction 2 OINST2_L = .-OINST2 ;... NINST2: CMPL R1,S^#NEW_C_RECALLMAX ;... NINST2_L = .-NINST2 ;... .ENDC ASSUME OINST2_L EQ NINST2_L ;... .IF DEFINED EVAX ;Compiling for AXP.... OINST3: .BYTE ^XD0,^X14,^X56 OINST3_L = 3 NINST3: .BYTE ^XD0,^X3E,^X56 NINST3_L = 3 .IFF ;Assembling for VAX.... OINST3: MOVL S^#WRK_C_RECALLMAX,R6 ;Old instruction 3 OINST3_L = .-OINST3 ;... NINST3: MOVL S^#NEW_C_RECALLMAX,R6 ;... NINST3_L = .-NINST3 ;... .ENDC ASSUME OINST3_L EQ NINST3_L ;... .IF DEFINED EVAX ;Compiling for AXP.... OINST4: .BYTE ^XD0,^X14,^X59 OINST4_L = 3 NINST4: .BYTE ^XD0,^X3E,^X59 NINST4_L = 3 .IFF ;Assembling for VAX.... OINST4: MOVL S^#WRK_C_RECALLMAX,R9 ;Old instruction 4 OINST4_L = .-OINST4 ;... NINST4: MOVL S^#NEW_C_RECALLMAX,R9 ;... NINST4_L = .-NINST4 ;... .ENDC ASSUME OINST4_L EQ NINST4_L ;... ; ; DEC's RECALL code that is replaced ; .IF DEFINED EVAX ;Compiling for AXP.... OINST5: .LONG ^X12316291 .LONG ^X328FB007 .LONG ^X0511622F .LONG ^X2F318FB0 .BYTE ^X62 OINST5_L = .-OINST5 NINST5: .LONG ^X12206291 .LONG ^X62309003 .LONG ^X2F906296 .LONG ^X010101A2 .BYTE ^X01 NINST5_L = .-NINST5 .IFF ;Assembling for VAX.... 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 .ENDC ASSUME OINST5_L EQ NINST5_L .ENDC ; End of .IF NE DO_ALPHA .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 .IF NE DO_ALPHA PRINT .ENDC BSBW READ_OLD_IMAGE ; Go open the image ; ; Now do a search and replace for each of the instructions. ; PRINT - .IF NE DO_ALPHA REPLACE #OINST1_L,OINST1,NINST1,#1,10$ ; Go replace instruction REPLACE #OINST2_L,OINST2,NINST2,#1,10$ ; Go replace instruction REPLACE #OINST3_L,OINST3,NINST3,#1,10$ ; Go replace instruction .IFF REPLACE #OINST1_L,OINST1,NINST1,#3,10$ ; Go replace instruction .ENDC PRINT .IF NE DO_ALPHA REPLACE #OINST4_L,OINST4,NINST4,#1,10$ ; Go replace instruction .IFF REPLACE #OINST2_L,OINST2,NINST2,#1,10$ ; Go replace instruction .ENDC PRINT .IF NE DO_ALPHA REPLACE #OINST5_L,OINST5,NINST5,#1,10$ ; Go replace instruction .IFF REPLACE #OINST3_L,OINST3,NINST3,#1,10$ ; Go replace instruction .ENDC PRINT .IF NE DO_ALPHA REPLACE #OINST6_L,OINST6,NINST6,#1,10$ ; Go replace instruction .IFF REPLACE #OINST4_L,OINST4,NINST4,#1,10$ ; Go replace instruction .ENDC .IF NE DO_ALPHA PRINT REPLACE #OINST8_L,OINST8,NINST8,#1,10$ PRINT REPLACE #OINST9_L,OINST9,NINST9,#1,10$ PRINT REPLACE #OINST10_L,OINST10,NINST10,#1,10$ .IFF PRINT REPLACE #OINST5_L,OINST5,NINST5,#1,10$ ; Go replace instructions .ENDC 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: .IIF DF,EVAX, .JSB_ENTRY 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: .IIF DF,EVAX, .JSB_ENTRY $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: .IIF DF,EVAX, .JSB_ENTRY $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