.TITLE PUP ... Patch Utility Program .IDENT /110682/ .ENABL LC ; ; ; ; Written by Ray Di Marco ; 11-Jun-82. ; ; ; Version 110682/01. ; ; ;---------------------------------------------------------------------------- ; ; This module, when assembled and linked produces the PUP program, that ; allows OBJECTS in a file to be patched; PUP treats all files as a series of ; bytes, and as such, can be used to patch any series of bytes within the ; file. ; ; Upon activation, PUP requests the name of the file that is to be patched. ; The file name must be in standard RT-11 format; the default extension is ; SAV. Once the file is openned, PUP prompts the user for TARGET and PATCH ; strings; entering a NULL TARGET string causes the current file to be closed ; out, after which PUP will prompt for a new one. ; ; PUP requires that the user enter TARGET and PATCH strings; each of these ; consist of a series of OCTAL bytes seperated by comma. Two mask strings, also ; consisting of octal bytes must also be entered; one is associated with the ; TARGET and one with the PATCH strings. These are used as follows ; ; PUP locates an OBJECT by searching for a series of bytes ; that match the TARGET exactly, except for those BITs that ; correspond the TARGET MASK bits that are set; in other words ; the TARGET MASK is used to specify DON'T cares. ; ; PUP then replaces all OBJECT bytes with the PATCH bytes ; specified except for those corresponding to none zero ; PATCH MASK bytes, which are not changed. In other words ; a non-zero PATCH MASK byte means that the corresponding ; byte in the OBJECT is not to be changed. The PATCH bytes ; are truncated or padded with NULLs to make the PATCH the ; same size as the OBJECT. ; ; NOTE that if the last character in a OCTAL string is a '-', it is assumed ; that the string is continued on the next line! ; ; .SBTTL DECLARATIONS ; ; .MCALL .READW,.WRITW,.CLOSE ; File I/O .MCALL .GTLIN,.PRINT ; Terminal I/O .MCALL .CSIGEN,.WAIT,.SRESET ; Special file support macros ; ; CR = 15 ; ASCII for LF = 12 ; ASCII for TSIZE = 100 ; maximum target size ERRBYT = 52 ; RT-11 error byte ; .SBTTL Macro Definitions ; ; .MACRO ERROR MESSAGE ?A,?B,?C .PRINT #'B JMP NEWFIL B': .ASCIZ / Error: MESSAGE!/ .EVEN .ENDM ERROR ; .MACRO .PUSH ARG .IRP X, MOV X,-(SP) .ENDR .ENDM .PUSH ; .MACRO .POP ARG .IRP X, MOV (SP)+,X .ENDR .ENDM .POP ; .SBTTL Data Structures involved with USR SWAPPING ; ; .ASECT ; open absolute section ; ------ ; . = 46 ; open USR SWAP pointer .WORD START ; swap USR over code ; .PSECT ; open relocatable section ; ------ ; ; The followind data structures are sensitive to USR swapping and as ; such are placed below the USR swap area. ; EXTBLK: .RAD50 /SAVSAVSAVSAV/ ; default extensions STKHLD: .BLKW 1 ; holds STACK address LINBUF: .BLKB 200 ; used to hold line input ; ; .SBTTL Initialization and Restart Code ; ; ; Save stack address so that can reset SP at start of each pass. ; START: MOV SP,STKHLD ; save STACK address ; ; ; This is module restart point; reset everything ready for new file. ; NEWFIL: MOV STKHLD,SP ; reset STACK (error entry) .SRESET ; reset all channels ; ; Get name of file to be processed; when got one open file and invoke ; process to patch it. ; .GTLIN #LINBUF,#1000$ ; request name of FILE TSTB LINBUF ; get any input? BEQ 700$ ; no -> identify self .CSIGEN #FREESP,#EXTBLK,#LINBUF ; attempt to open file BCS 710$ ; failed -> abort MOV STKHLD,SP ; reset STACK .WAIT #3 ; have a file on channel 3? BCS 710$ ; no -> abort CALL PROCESS ; process file BR NEWFIL ; loop ; 700$: ERROR 710$: ERROR ; .NLIST BIN 1000$: .ASCII /PUP: File > /<200> .EVEN .LIST BIN ; ; .SBTTL Routine - "PROCESS" ... Process file ; ; We are here to process the file; to do so we request that the user specify ; ; * a series of bytes (TARGET) that identify the object to patch ; * a series of bytes (MASK) that indicates DON'T cares in the TARGET ; * a series of bytes (PATCH) that will replace the object ; * a series of bytes (MASK) that indicates unchangable object bits ; ; ; Start by clearing the TARGET, PATCH and two MASK buffers. ; PROCES: MOV #TVALU,R0 ; R0 -> buffers MOV #TSIZE*4,R1 ; R1 = number bytes in bufs 10$: CLRB (R0)+ ; clear all ... SOB R1,10$ ; ... buffers ; ; ; Until user specifies a NULL target; setup TARGET, PATCH and MASKs and use ; PATCH routine to patch file. ; 100$: MOV #1000$,R5 ; R5 -> table CALL 600$ ; load target BEQ 170$ ; no target -> abort .PUSH R2 ; save SIZE CALL 600$ ; load mask CALL 600$ ; load edit CALL 600$ ; load mask .POP R1 ; R1 = SIZE MOV #EMASK,R2 ; R2 = Edit Mask MOV #EVALU,R3 ; R3 = Edit Value MOV #TMASK,R4 ; R4 = Target Mask MOV #TVALU,R5 ; R5 = Target Value CALL PATCH ; patch file BR PROCES ; loop 170$: RETURN ; all done ; ; ; ; This routine is used to process the next entry in the 1000$ table. The ; table consists of two word entries; the first is the address of a prompt ; message and the second the buffer in which the bytes are to be returned. ; R5 is used as the table pointer and R2 to return the number of bytes. ; 600$: .GTLIN #LINBUF,(R5)+ ; get input MOV (R5)+,R2 ; R2 -> storage buffer 607$: MOV #LINBUF,R1 ; R1 -> input 610$: CALL ASCNUM ; R0 = value BCS 620$ ; no number -> skip MOVB R0,(R2)+ ; store value BR 610$ ; loop 620$: CMPB -1(R1),#'- ; contuation? BNE 660$ ; no -> exit .GTLIN #LINBUF,#1006$ ; as for rest BR 607$ ; pocess rest 660$: SUB -2(R5),R2 ; R2 = number bytes 670$: RETURN ; all done ; ; .NLIST BIN 1000$: .WORD 1005$,TVALU,1004$,TMASK,1003$,EVALU,1002$,EMASK 1002$: .ASCII / mask= /<200> 1003$: .ASCII / Patch= /<200> 1004$: .ASCII / mask= /<200> 1005$: .ASCII / Target= /<200> 1006$: .ASCII / /<200> .EVEN .LIST BIN ; .SBTTL Routine - "PATCH" ... carry out a patch ; ; This routine is called to actually patch the file; at entry it requires ; the following information to be passed via the registers ; ; R5 address of target ; R4 address of target mask ; R3 address of patch ; R2 address of patch mask ; R1 size of target (in bytes) ; ; ; Start by ensuring that all bits in TARGET that are masked are set to ; 0; this speeds up the serach loop considerably. Also clear number of ; patchs made (NUMPAT) counter. ; PATCH: .PUSH ; save 10$: BICB (R4)+,(R5)+ ; clear unwanted bits ... SOB R1,10$ ; ... in TARGET string .POP ; restore CLR NUMPAT ; no patchs as yet ; ; ; In the following loop all characters are transfered from the file into ; register R0 and that back out again; when hit EOF return to caller. Special ; action is initiated when find a byte in file that matchs first byte of ; TARGET; in this case we SAVE file parameters (so can UNSAVE back to that ; point), and see if rest of TARGET is matched. After checking too see if ; target found, the file is UNSAVEd, and we continue transfering bytes, ; patching them as we go if they matched the target. ; BR 120$ ; enter loop 100$: CALL FILUNS ; restore SAVEd position 110$: CALL FILOUT ; output byte 120$: CALL FILINP ; input a byte BCS 170$ ; none left -> skip CMPB R0,(R5) ; matchs first byte of target? BNE 110$ ; no -> ignore it CALL FILSAV ; save position in file CALL 1000$ ; match all of target? BCS 100$ ; no -> skip CALL FILUNS ; restore SAVEd position CALL 2000$ ; patch file BR 120$ ; loop ; 170$: CALL FILRWD ; rewind stream MOV #710$,R1 ; R1 -> buffer MOV NUMPAT,R2 ; R2 = number of patchs CALL NUMASC ; convert to ascii .PRINT #700$ ; tell number of patchs RETURN ; exit ; .NLIST BIN 700$: .ASCII / Patch implemented / 710$: .ASCIZ /DDDDD. times!/ .EVEN .LIST BIN ; ; ; ; ; This routine returns with the C flag clear iff all bytes in the target ; match the following bytes in the file; bits corresponding to those set ; in the target mask are treated as DON'T cares and are ignored in the ; compasison. ; 1000$: .PUSH ; save registers CMPB (R5)+,(R4)+ ; point next target/mask bytes DEC R1 ; one less byte to check 1100$: CALL FILINP ; get next char BCS 1700$ ; eof -> failed BICB (R4)+,R0 ; clear 'masked' bits CMPB (R5)+,R0 ; match? BNE 1700$ ; no -> abort SOB R1,1100$ ; loop till all tested TST (PC)+ ; suceeded, therefore clear C flag 1700$: SEC ; failed, there set C flag .POP ; restore registers RETURN ; exit ; ; ; ; This routine replaces all bytes in the file with the PATCH bytes, except ; for those that correspond to PATCH MASK bytes that are non-zero; these ; bytes are not patched. ; 2000$: .PUSH ; save resgisters BR 2110$ ; enter loop 2100$: CALL FILINP ; get byte 2110$: TSTB (R2) ; edit byte? BNE 2200$ ; no -> skip MOVB (R3),R0 ; replace with new 2200$: CALL FILOUT ; output byte CMPB (R2)+,(R3)+ ; bump pointers SOB R1,2100$ ; loop till all done .POP ; restore INC NUMPAT ; one more patch done RETURN ; all done ; ; ; .SBTTL Primitive - "FILINT" ... initialize file variables ; ; This routine is called to initialize the variables needed by FILINP ; and FILOUT. ; ; FILINT: MOV #O.BUF,O.PNT ; setup pointer MOV #1000,O.CNT ; setup counter MOV #-1,O.BLK ; setup block number CLR I.CNT ; reset character count MOV #I.BUF,I.PNT ; reset pointer CLR I.BLK ; reset block number CLR EOFFLG ; reset EOF flag RETURN ; home ; .SBTTL Primitive - "FILRWD" ... rewind stream ; ; This routine rewinds the stream; it must be called at the end of each ; pass to reset the pointers/variables and ensure that everything looks ; ok. ; FILRWD: TST I.CNT ; buffer must be empty BNE 1000$ ; not -> abort MOV I.BLK,R0 ; R0 = next block to read SUB O.BLK,R0 ; R0 = next block - last written CMP R0,#1 ; difference should be 1 BNE 1000$ ; not same -> skip CMP O.CNT,#1000 ; any chars in buffer? BEQ FILINT ; no -> all ok 1000$: ERROR ; .SBTTL Primitive - "FILSAV" ... save current position ; ; ; When called this routine SAVEs all file paramters so that the FILUNS ; routine can return the input file to exactly the same point when ; called. As R0 holds the last byte input from the stream, it also ; is saved so that it may be restored. ; FILSAV: MOV I.CNT,SAVBLK ; save I.CNT MOV I.PNT,SAVBLK+2 ; save I.PNT MOV I.BLK,SAVBLK+4 ; save I.BLK MOV R0,SAVBLK+6 ; save last byte input MOV EOFFLG,SAVBLK+10; save EOF flag RETURN ; exit ; ; .SBTTL Primitive - "FILUNS" ... unsave input stream ; ; ; This routine restores the input stream (and R0) to exactly the same point ; when FILSAV was last invoked. ; FILUNS: MOV SAVBLK,I.CNT ; unsave I.CNT MOV SAVBLK+2,I.PNT ; unsave I.PNT MOV SAVBLK+6,R0 ; unsave last byte input MOV SAVBLK+10,EOFFLG; unsave EOF flag CMP SAVBLK+4,I.BLK ; still on same block? BEQ 1000$ ; yes -> skip ; MOV SAVBLK+4,I.BLK ; unsave I.BLK DEC I.BLK ; point to last block read .READW #EMTBLK,#3,#I.BUF,#400,I.BLK MOV SAVBLK+4,I.BLK ; unsave I.BLK MOV SAVBLK+6,R0 ; unsave last byte input BCC 1000$ ; read ok -> skip ERROR 1000$: RETURN ; exit ; ; .SBTTL Primitive - "FILINP" ... input a byte ; ; This routine returns the next byte from the input stream in R0. The "C" ; flag is set iff a EOF error occures. ; FILINP: TST EOFFLG ; eof hit? BNE 1000$ ; yes -> problem TST I.CNT ; any chars in buffer? BNE 100$ ; yes -> skip .READW #EMTBLK,#3,#I.BUF,#400,I.BLK BCS 700$ ; failed -> abort CMP R0,#400 ; read in right amount? BNE 1200$ ; no -> skip MOV #1000,I.CNT ; reset character count MOV #I.BUF,I.PNT ; reset pointer INC I.BLK ; bump block number 100$: MOVB @I.PNT,R0 ; fetch character INC I.PNT ; bump pointer DEC I.CNT ; one less character in buffer RETURN ; all ok; exit ; 700$: TSTB @#ERRBYT ; hit EOF? BNE 1170$ ; no -> hard error INC EOFFLG ; set hit EOF 1000$: SEC ; indicate EOF RETURN ; all done 1170$: ERROR 1200$: ERROR ; .SBTTL Primitive - "FILOUT" ... output 1 byte to file ; ; This primitive will output the byte passed in R0 to the output stream. ; FILOUT: MOVB R0,@O.PNT ; store byte in buffer INC O.PNT ; bump pointer DEC O.CNT ; one less space in buffer BNE 1000$ ; all ok -> skip MOV #O.BUF,O.PNT ; reset pointer MOV #1000,O.CNT ; reset counter INC O.BLK ; bump block counter .PUSH R0 ; save R0 .WRITW #EMTBLK,#3,#O.BUF,#400,O.BLK .POP R0 ; restore R0 BCC 1000$ ; ok -> skip ERROR 1000$: RETURN ; all done ; .SBTTL Primitive - "ASCNUM" ... return next number @R1 in R0 ; ; This routine returns the next OCTAL number in the string @R1 in R0. The ; string must be in ASCIZ format. ; ; ASCNUM: .PUSH R2 ; save work register CLR R0 ; clear accumulator ; 100$: TSTB (R1) ; hit EOS delimiter? BEQ 1100$ ; yes -> abort CMPB (R1),#'! ; hit COMMENT delimiter? BEQ 1100$ ; yes -> abort MOVB (R1)+,R2 ; R2 = character SUB #'0,R2 ; R2 = value of digit CMP R2,#10 ; was valid OCTAL digit? BHIS 100$ ; no -> loop ; 200$: ASL R0 ; R0 = TOTAL*2 ASL R0 ; R0 = TOTAL*4 ASL R0 ; R0 = TOTAL*8 ADD R2,R0 ; R0 = TOTAL*8 + new digit TSTB (R1) ; hit EOS delimiter? BEQ 1000$ ; yes -> abort CMPB (R1),#'! ; hit COMMENT delimiter? BEQ 1000$ ; yes -> abort MOVB (R1)+,R2 ; R2 = character SUB #'0,R2 ; R2 = value of digit CMP R2,#7. ; was valid OCTAL digit? BLOS 200$ ; yes -> add to total DEC R1 ; rewind over delimiter ; 1000$: TST (PC)+ ; have a number, clear FAIL flag 1100$: SEC ; no number, set FAIL flag .POP R2 ; restore RETURN ; done ; .SBTTL Primitive - "NUMASC" ... convert R2 to decimal string @R1 ; ; This routine returns a 5 digit decimal string equivalent to R2 in the ; string pointed to by R1. ; NUMASC: .PUSH ; save registers .IRP X,<10000.,1000.,100.,10.,1> ; ---- for all powers of 10 --- MOV #'X,R3 ; setup base CALL 1000$ ; produce digit .ENDR ; ----------------------------- .POP ; restore RETURN ; all done ; 1000$: MOVB #'0,(R1)+ ; initialize counter 1100$: CMP R2,R3 ; is R2 less than BASE? BLO 1200$ ; yes -> done SUB R3,R2 ; decrease R2 by one base value INCB -1(R1) ; up count BR 1100$ ; loop 1200$: RETURN ; exit ; ; .SBTTL Data Structures and Variables ; TVALU: .BLKB TSIZE ; used to hold TARGET TMASK: .BLKB TSIZE ; used to hold TARGET MASK EVALU: .BLKB TSIZE ; used to hold PATCH EMASK: .BLKB TSIZE ; used to hold PATCH MASK EMTBLK: .BLKW 10. ; used for RT-11 emt argument passing NUMPAT: .WORD 0 ; used by PATCH as a counter ; SAVBLK: .BLKW 10 ; used by FILSAV to save stream position EOFFLG: .WORD 0 ; indicates hit EOF on input I.PNT: .WORD I.BUF ; fetch pointer I.CNT: .WORD 0 ; bytes in buffer I.BLK: .WORD 0 ; next block to read O.PNT: .WORD O.BUF ; store pointer O.CNT: .WORD 1000 ; bytes left in buffer O.BLK: .WORD -1 ; last block written I.BUF: .BLKW 400 ; input buffer O.BUF: .BLKW 400 ; output buffer FREESP: ; start of free space ; .IIF GT,<10000-.+START>, .BLKB <10000-.+START> ; PADDING MEMTOP = . ; this is address of last byte used ; ; .END START