FIXOBJ: DO; /* FIXOBJ - Program to change load addresses of iAPX 86,88 object files. INSITE ORDER NO: BG77 Written by: John Hall Eastman Kodak Co. Rochester, NY Date: 17-Aug-82 Description: This is a utility program that executes on any Intellec Series II or Series II development system. It reads iAPX 86,88 absolute object file and creates a new object file. Whenever a data record is copied (PEDATA or PIDATA), if the load address is within a selected range, it is offset by a specified amount. Invocation: -FIXOBJ TO Switches: /S=nnnnn - Start of selected address range. A hexadecimal number 00000H - FFFFFH. Trailing "H" is optional. /E=nnnnn - End of selected address range. A hexadecimal number 00000H - FFFFFH. Trailing "H" is optional. /O={+|-}nnnnn - Offset to be added or subtracted from the load address of every record in the selected range. It is a hexadecimal number in the range -FFFFFH .. 00000H .. FFFFFH. Leading sign and trailing "H" are optional. Method: The load address of each PEDATA or PIDATA record is evaluated as a 20-bit unsigned value. It is compared to the selected starting and ending addresses. If it within the range, the specified offset is added or subtracted from the load address. It is then put back into the proper form in the object record. A new value is calculated for the checksum, and it is written to the output file. Reference: "iAPX 86,88 FAMILY UTILITIES USER'S GUIDE for 8086-Based Development Systems" Appendix A - "iAPX-86,88 ABSOLUTE OBJECT FILE FORMATS" (Manual Order Number 121616-001 Rev.A) Copyright (C) 1980 Intel Corporation */ $EJECT /**********************************************************/ /* */ /* DECLARATIONS */ /* */ /**********************************************************/ DECLARE TRUE LITERALLY '0FFH'; DECLARE FALSE LITERALLY '000H'; DECLARE BOOLEAN LITERALLY 'BYTE'; DECLARE WORD LITERALLY 'ADDRESS'; DECLARE FOREVER LITERALLY 'WHILE TRUE'; DECLARE CR LITERALLY '0DH'; DECLARE LF LITERALLY '0AH'; DECLARE SPACE LITERALLY ''' '''; DECLARE CHECK$SUM$MSG (*) BYTE DATA('*** BAD CHECKSUM ENCOUNTERED ***', 0DH, 0AH ); DECLARE REC$NO WORD; DECLARE REC$TYPE BYTE; DECLARE REC$LENGTH WORD; DECLARE FILE$OFFSET (3) BYTE; DECLARE ACTUAL WORD; /* File connections */ DECLARE AFTCI WORD DATA( 01H ); /* :CI: */ DECLARE AFTCO WORD DATA( 00H ); /* :CO: */ DECLARE AFTIN WORD; /* input object file */ DECLARE AFTOUT WORD; /* output listing file */ DECLARE HEADER (*) BYTE DATA(CR, LF, 'FIXOBJ V1.0', CR, LF, LF); DECLARE CLINE$ERROR (*) BYTE DATA(CR, LF, 'ERROR IN COMMAND LINE AT #', CR, LF); DECLARE I BYTE; DECLARE J WORD; DECLARE COMMANDLINE(122) BYTE; DECLARE COMMANDLINE$LEN BYTE; DECLARE STATUS WORD; DECLARE JUNK WORD; DECLARE CARRY BYTE; DECLARE ENDFILE$IN BOOLEAN; /* TRUE if end of input file */ DECLARE CHECK$SUM BYTE; DECLARE START$VALUE (3) BYTE INITIAL( 00H, 00H, 00H ); DECLARE END$VALUE (3) BYTE INITIAL( 0FFH, 0FFH, 00FH ); DECLARE OFFSET$VALUE (3) BYTE INITIAL( 00H, 00H, 00H ); DECLARE LOAD$ADR (3) BYTE; DECLARE OFFSET$SIGN BYTE; DECLARE DATA$REC STRUCTURE( FRAME WORD, OFFSET BYTE ) AT( .MEMORY ); /**********************************************************/ /* */ /* ISIS-II ROUTINE DECLARATIONS */ /* */ /**********************************************************/ OPEN: PROCEDURE( CONN$P, PATH$P, ACCESS, ECHO, STATUS$P ) EXTERNAL; DECLARE( CONN$P, PATH$P, ACCESS, ECHO, STATUS$P ) ADDRESS; END OPEN; READ: PROCEDURE( CONN, BUFF$P, COUNT, ACTUAL$P, STATUS$P ) EXTERNAL; DECLARE( CONN, BUFF$P, COUNT, ACTUAL$P, STATUS$P ) ADDRESS; END READ; WRITE: PROCEDURE( CONN, BUFF$P, COUNT, STATUS$P ) EXTERNAL; DECLARE( CONN, BUFF$P, COUNT, STATUS$P ) ADDRESS; END WRITE; CLOSE: PROCEDURE( CONN$P, STATUS$P ) EXTERNAL; DECLARE( CONN$P, STATUS$P ) ADDRESS; END CLOSE; ERROR: PROCEDURE( ERRNUM ) EXTERNAL; DECLARE( ERRNUM ) ADDRESS; END ERROR; EXIT: PROCEDURE EXTERNAL; END EXIT; $EJECT /**********************************************************/ /* */ /* ERROR HANDLERS */ /* */ /**********************************************************/ COMMAND$ERROR: PROCEDURE; DECLARE L BYTE; CALL WRITE( AFT$CO, .CLINE$ERROR, SIZE(CLINE$ERROR), .JUNK ); CALL WRITE( AFT$CO, .COMMANDLINE, COMMANDLINE$LEN, .JUNK); IF I > 0 THEN DO L = 1 TO I; CALL WRITE( AFT$CO, .(SPACE), 1, .JUNK); END; CALL WRITE( AFT$CO, .('#',CR,LF), 1, .JUNK ); CALL EXIT; END COMMAND$ERROR; $EJECT /**********************************************************/ /* */ /* OBJECT DATA INPUT ROUTINES */ /* */ /**********************************************************/ READ$DATA: PROCEDURE( DAT$ADR, REQ ); /* READ$DATA Read the REQ number of bytes from the object input file into the area at DAT$ADR. The CHECK$SUM is updated. Errors and end-of-file are checked */ DECLARE DAT$ADR ADDRESS; DECLARE DAT$BUF BASED DAT$ADR (1) BYTE; DECLARE REQ WORD; DECLARE K WORD; DECLARE READ$ERR (*) BYTE DATA(CR, LF, 'ERROR READING INPUT FILE', CR, LF); /* Get the data */ CALL READ( AFT$IN, DAT$ADR, REQ, .ACTUAL, .STATUS ); /* Check for errors */ IF STATUS <> 0 THEN DO; CALL WRITE( AFT$CO, .READ$ERR, SIZE(READ$ERR), .JUNK ); CALL ERROR( STATUS ); CALL EXIT; END; /* Check for end of file */ ENDFILE$IN = ACTUAL <> REQ; /* Update the CHECK$SUM */ IF ACTUAL <> 0 THEN DO K = 0 TO ACTUAL-1; CHECK$SUM = CHECK$SUM + DAT$BUF(K); END; RETURN; END READ$DATA; READ$BYTE: PROCEDURE BYTE; /* READ$BYTE Read an 8-bit byte value from the object input file. */ DECLARE DAT$BYTE BYTE; CALL READ$DATA( .DAT$BYTE, 1 ); RETURN( DAT$BYTE ); END READ$BYTE; READ$WORD: PROCEDURE WORD; /* READ$WORD Read a 16-bit word value from the object input file */ DECLARE DAT$WORD WORD; CALL READ$DATA( .DAT$WORD, 2 ); RETURN( DAT$WORD ); END READ$WORD; $EJECT /**********************************************************/ /* */ /* OBJECT DATA OUTPUT ROUTINES */ /* */ /**********************************************************/ WRITE$DATA: PROCEDURE( DATA$ADR, REQUESTED ); /* WRITE$DATA Write the REQUESTED number of bytes to the object input file from the area at DATA$ADR. Errors are checked */ DECLARE DATA$ADR ADDRESS; DECLARE DATA$BUF BASED DATA$ADR (1) BYTE; DECLARE REQUESTED WORD; DECLARE WRITE$ERR (*) BYTE DATA(CR, LF, 'ERROR READING INPUT FILE', CR, LF); /* Write the data */ CALL WRITE( AFT$OUT, DATA$ADR, REQUESTED, .STATUS ); /* Check for errors */ IF STATUS <> 0 THEN DO; CALL WRITE( AFT$CO, .WRITE$ERR, SIZE(WRITE$ERR), .JUNK ); CALL ERROR( STATUS ); CALL EXIT; END; RETURN; END WRITE$DATA; WRITE$BYTE: PROCEDURE( DATA$BYTE ); /* WRITE$BYTE Write an 8-bit byte value to the object input file. */ DECLARE DATA$BYTE BYTE; CALL WRITE$DATA( .DATA$BYTE, 1 ); RETURN; END WRITE$BYTE; WRITE$WORD: PROCEDURE( DATA$WORD ); /* WRITE$WORD Read a 16-bit word value from the object input file */ DECLARE DATA$WORD WORD; CALL WRITE$DATA( .DATA$WORD, 2 ); RETURN; END WRITE$WORD; $EJECT /**********************************************************/ /* */ /* BYTE ADDITON PROCEDURE FOR MULTI-PRECISION ARITHMETIC */ /* */ /**********************************************************/ ADD$BYTES: PROCEDURE( A, B, C, SUM$ADR, CARRY$ADR ); DECLARE (A, B, C) BYTE; DECLARE SUM$ADR ADDRESS; DECLARE SUM BASED SUM$ADR BYTE; DECLARE CARRY$ADR ADDRESS; DECLARE CARRY BASED CARRY$ADR BYTE; DECLARE SUMW WORD; SUMW = DOUBLE(A) + DOUBLE(B) + DOUBLE(C); SUM = LOW(SUMW); CARRY = HIGH(SUMW); RETURN; END ADD$BYTES; $EJECT /**********************************************************/ /* */ /* 20-BIT COMPARISON ROUTINES */ /* */ /**********************************************************/ COMPARE20: PROCEDURE( L$ADR, R$ADR ) BYTE; /* Perform a 20-bit comparison on L:R. if LR then return +1 */ DECLARE L$ADR ADDRESS, L BASED L$ADR (3) BYTE; DECLARE R$ADR ADDRESS, R BASED R$ADR (3) BYTE; DECLARE RESULT BYTE; /* Compare high bytes */ IF L(2) > R(2) THEN RESULT = 1; ELSE IF L(2) < R(2) THEN RESULT = -1; ELSE DO; /* Compare middle bytes */ IF L(1) > R(1) THEN RESULT = 1; ELSE IF L(1) < R(1) THEN RESULT = -1; ELSE DO; /* Compare low bytes */ IF L(0) > R(0) THEN RESULT = 1; ELSE IF L(0) < R(0) THEN RESULT = -1; ELSE RESULT = 0; END; END; RETURN( RESULT ); END COMPARE20; GE20: PROCEDURE( L$ADDR, R$ADDR ) BOOLEAN; /* Returns true if 20 bit L >= R */ DECLARE (L$ADDR, R$ADDR) ADDRESS; RETURN( COMPARE20( L$ADDR, R$ADDR ) <> -1 ); END GE20; LE20: PROCEDURE( L$AD, R$AD ) BOOLEAN; /* Returns true if 20 bit L <= R */ DECLARE (L$AD, R$AD) ADDRESS; RETURN( COMPARE20( L$AD, R$AD ) <> 1 ); END LE20; $EJECT /**********************************************************/ /* */ /* CONSOLE INPUT ROUTINES */ /* */ /**********************************************************/ UC: PROCEDURE(CHR) BYTE; /* Convert to upper case */ DECLARE CHR BYTE; IF (CHR>='a') AND (CHR<='z') THEN RETURN( CHR -'a' +'A' ); ELSE RETURN( CHR ); END UC; GET$VALUE: PROCEDURE( VALUE$ADR ); /* GET$VALUE Get a Hexadecimal number of up to five digits from the commandline. */ DECLARE VALUE$ADR ADDRESS, VALUE BASED VALUE$ADR (3) BYTE; DECLARE FIRST$DIGIT BYTE; DECLARE LAST$DIGIT BYTE; DECLARE DIGIT BYTE; DECLARE DIGIT$VALUE BYTE; DECLARE CHAR BYTE; /* Skip blanks */ FIRST$DIGIT = I; DO WHILE COMMANDLINE(FIRST$DIGIT) = ' '; FIRST$DIGIT = FIRST$DIGIT + 1; END; /* Skip non-blanks until a blank or '/' is found */ LAST$DIGIT = FIRST$DIGIT; DO WHILE (COMMANDLINE(LAST$DIGIT+1) <> ' ') AND (COMMANDLINE(LAST$DIGIT+1) <> '/'); LAST$DIGIT = LAST$DIGIT + 1; END; I = LASTDIGIT + 1; /* If the "LAST$DIGIT" is an "H" or "h", then back up one space */ IF ( UC(COMMANDLINE(LASTDIGIT)) = 'H') THEN LASTDIGIT = LASTDIGIT - 1; /* Make sure the number isn't too long */ IF (LAST$DIGIT - FIRSTDIGIT) > 4 THEN CALL COMMAND$ERROR; /* Evaluate the number */ VALUE(0) = 0; VALUE(1) = 0; VALUE(2) = 0; DIGIT = FIRST$DIGIT; DO WHILE DIGIT <= LAST$DIGIT; CHAR = UC( COMMANDLINE(DIGIT) ); IF (CHAR >= '0') AND (CHAR <= '9') THEN DIGIT$VALUE = CHAR - '0'; ELSE IF ( CHAR >= 'A') AND ( CHAR <= 'F') THEN DIGIT$VALUE = CHAR + 10 - 'A'; ELSE /* Bad digit */ CALL COMMAND$ERROR; /* Shift VALUE left 4 bits */ VALUE(2) = SHR( VALUE(1), 4); VALUE(1) = SHR( VALUE(0), 4) OR SHL( VALUE(1), 4); VALUE(0) = SHL( VALUE(0), 4); /* Add in the new digit */ CALL ADD$BYTES( DIGIT$VALUE, VALUE(0), 0, .VALUE(0), .CARRY ); CALL ADD$BYTES( CARRY, VALUE(1), 0, .VALUE(1), .CARRY ); CALL ADD$BYTES( CARRY, VALUE(2), 0, .VALUE(2), .CARRY ); DIGIT = DIGIT + 1; END; RETURN; END GET$VALUE; GETSWITCHES: PROCEDURE; /* Skip over blanks in the keyboard buffer */ DO WHILE COMMANDLINE(I) = SPACE; I = I + 1; END; /* Handle switches */ DO WHILE COMMANDLINE(I) = '/'; I = I + 1; /* Check for /S= switch, starting address value */ IF ( UC( COMMANDLINE(I)) = 'S' ) AND ( COMMANDLINE(I+1) = '=' ) THEN DO; I = I + 2; CALL GET$VALUE( .START$VALUE ); END; /* Check for /E= switch, ending address value */ ELSE IF ( UC(COMMANDLINE(I)) = 'E' ) AND ( COMMANDLINE(I+1) = '=' ) THEN DO; I = I + 2; CALL GET$VALUE( .END$VALUE ); END; /* Check for /O= switch, offset value */ ELSE IF ( UC(COMMANDLINE(I)) = 'O' ) AND ( COMMANDLINE(I+1) = '=' ) THEN DO; I = I + 2; /* Check for offset sign */ OFFSET$SIGN = 1; IF COMMANDLINE(I) = '+' THEN I = I + 1; ELSE IF COMMANDLINE(I) = '-' THEN DO; I = I + 1; OFFSET$SIGN = -1; END; /* Get value */ CALL GET$VALUE( .OFFSET$VALUE ); /* Complement the value if the sign was '-' */ IF OFFSET$SIGN = -1 THEN DO; /* Take 2's complement (1's complement, plus 1) */ OFFSET$VALUE(0) = NOT OFFSET$VALUE(0); OFFSET$VALUE(1) = NOT OFFSET$VALUE(1); OFFSET$VALUE(2) = NOT OFFSET$VALUE(2); CALL ADD$BYTES( OFFSET$VALUE(0), 1, 0, .OFFSET$VALUE(0), .CARRY ); CALL ADD$BYTES( OFFSET$VALUE(1), 0, CARRY, .OFFSET$VALUE(1), .CARRY ); CALL ADD$BYTES( OFFSET$VALUE(2), 0, CARRY, .OFFSET$VALUE(2), .CARRY ); END; END; ELSE CALL COMMAND$ERROR; /* Jump to next switch or blank */ DO WHILE (COMMANDLINE(I) <> '/') AND (COMMANDLINE(I) <> ' '); I = I + 1; END; END; /* Jump over blanks */ DO WHILE (COMMANDLINE(I) = ' '); I = I + 1; END; RETURN; END GETSWITCHES; $EJECT /**********************************************************/ /* */ /* FILE HANDLING ROUTINES */ /* */ /**********************************************************/ OPEN$FILES: PROCEDURE; /* OPEN$FILES Open the file specified in the command tail for reading. */ /* Open the input file */ CALL OPEN(.AFTIN, .COMMANDLINE(I), 1, 0, .STATUS); IF STATUS <> 0 THEN DO; CALL ERROR(STATUS); CALL COMMAND$ERROR; END; /* Find next blank */ DO WHILE COMMANDLINE(I) <> SPACE; I = I + 1; IF I >= COMMANDLINE$LEN THEN CALL COMMAND$ERROR; END; /* Find next non-blank */ DO WHILE COMMANDLINE(I) = SPACE; I = I + 1; IF I >= COMMANDLINE$LEN THEN CALL COMMAND$ERROR; END; /* Did we find "TO" or "to" ? */ IF ( UC(COMMANDLINE(I)) = 'T') AND ( UC(COMMANDLINE(I+1)) = 'O' ) THEN I = I + 2; ELSE CALL COMMAND$ERROR; IF I >= COMMANDLINE$LEN THEN CALL COMMAND$ERROR; /* Open the output file */ CALL OPEN(.AFTOUT, .COMMANDLINE(I), 2, 0, .STATUS); IF STATUS <> 0 THEN DO; CALL ERROR(STATUS); CALL COMMAND$ERROR; END; RETURN; END; CLOSE$FILES: PROCEDURE; /* CLOSE$FILES Close all files */ CALL CLOSE( AFT$IN, .STATUS ); CALL CLOSE( AFT$OUT, .STATUS ); CALL CLOSE( AFT$CI, .STATUS ); CALL CLOSE( AFT$CO, .STATUS ); RETURN; END CLOSE$FILES; /*************************/ /* */ /* START OF MAIN BODY */ /* */ /*************************/ CALL WRITE( AFT$CO, .HEADER, SIZE(HEADER), .JUNK ); ENDFILE$IN = FALSE; /* Read the rest of the command line */ CALL READ (AFTCI, .COMMANDLINE, 122, .COMMANDLINE$LEN, .STATUS); IF STATUS <> 0 THEN CALL COMMAND$ERROR; /* Put a blank at the end of the command line */ COMMANDLINE(COMMANDLINE$LEN) = ' '; /* Point to the first character in the command line */ I = 0; /* Read the switches from the command line */ CALL GETSWITCHES; CALL OPEN$FILES; DO FOREVER; /* Reset the checksum */ CHECK$SUM = 0; /* Read the record */ REC$TYPE = READ$BYTE; REC$LENGTH = READ$WORD; CALL READ$DATA( .MEMORY, REC$LENGTH ); /* Report bad checksums */ IF CHECK$SUM <> 0 THEN CALL WRITE( AFTCO, .CHECK$SUM$MSG, SIZE(CHECK$SUM$MSG), .JUNK ); /* Check for end of file */ IF ENDFILE$IN THEN DO; CALL CLOSE$FILES; CALL EXIT; END; IF (REC$TYPE = 84H) OR (REC$TYPE = 86H) THEN DO; /* Convert the load address to a 20-bit value */ LOAD$ADR(0) = ( DATAREC.OFFSET AND 0000$1111B ) OR ( SHL(DATAREC.FRAME, 4) AND 1111$0000B ); LOAD$ADR(1) = LOW( SHR(DATAREC.FRAME, 4) ); LOAD$ADR(2) = LOW( SHR(DATAREC.FRAME, 12)); /* Check if the address is in range */ IF GE20(.LOAD$ADR, .START$VALUE) AND LE20(.LOAD$ADR, .END$VALUE) THEN DO; /* Add the offset to form a new load address */ CALL ADD$BYTES( LOAD$ADR(0), OFFSET$VALUE(0), 0, .LOAD$ADR(0), .CARRY ); CALL ADD$BYTES( LOAD$ADR(1), OFFSET$VALUE(1), CARRY, .LOAD$ADR(1), .CARRY ); CALL ADD$BYTES( LOAD$ADR(2), OFFSET$VALUE(2), CARRY, .LOAD$ADR(2), .CARRY ); /* Truncate it to 20 bits */ OFFSET$VALUE(2) = OFFSET$VALUE(2) AND 0000$1111B; /* Convert the 20-bit sum back to frame number and offset */ DATAREC.FRAME = ( SHR( DOUBLE(LOAD$ADR(0)), 4) AND 0000$0000$0000$1111B ) OR ( SHL( DOUBLE(LOAD$ADR(1)), 4) AND 0000$1111$1111$0000B ) OR ( SHL( DOUBLE(LOAD$ADR(2)), 12) AND 1111$0000$0000$0000B ); DATAREC.OFFSET = LOAD$ADR(0) AND 0000$1111B; /* Compute the new checksum */ CHECK$SUM = REC$TYPE + LOW(REC$LENGTH) + HIGH(REC$LENGTH); DO J = 0 TO REC$LENGTH - 2; CHECK$SUM = CHECK$SUM + MEMORY(J); END; CHECKSUM = -CHECKSUM; MEMORY(REC$LENGTH-1) = CHECKSUM; END; END; /* Write the record */ CALL WRITE$BYTE( REC$TYPE ); CALL WRITE$WORD( REC$LENGTH ); CALL WRITE$DATA( .MEMORY, REC$LENGTH ); END; END FIXOBJ;