PROGRAM ASSEM(INPUT,OUTPUT,SRCFILE,OBJFILE,LISFILE,INTFILE); (* THIS IS A SIMPLE ASSEMBLER FOR SIC (STANDARD VERSION). THE ASSEMBLER IS DIVIDED INTO PROCEDURES AS DESCRIBED IN CHAPTER 8 OF THE SECOND EDITION OF "SYSTEM SOFTWARE." THE SOURCE PROGRAM INPUT IS READ FROM FILE SRCFILE. THE OBJECT PROGRAM IS WRITTEN ON FILE OBJFILE, AND THE ASSEMBLY LISTING IS WRITTEN ON FILE LISFILE. THIS ASSEMBLER HANDLES ALL STANDARD SIC INSTRUCTIONS AS DESCRIBED IN APPENDIX A. INSTRUCTION OPERANDS MUST BE OF THE FORM 'ADDRESS' OR 'ADDRESS,X' WHERE 'ADDRESS' IS EITHER A SYMBOL THAT IS USED AS A LABEL IN THE SOURCE PROGRAM OR AN ACTUAL HEXADECIMAL ADDRESS. HEXADECIMAL ADDRESSES THAT WOULD BEGIN WITH 'A' THROUGH 'F' MUST START WITH A LEADING '0' TO DISTINGUISH THEM FROM LABELS. THE ASSEMBLER ALSO SUPPORTS FOLLOWING ASSEMBLER DIRECTIVES (SEE CHAPTER 2 FOR FURTHER INFORMATION): START, END, BYTE, WORD, RESB, RESW. THE SOURCE PROGRAM TO BE ASSEMBLED MUST BE IN FIXED FORMAT AS FOLLOWS: BYTES 1-8 LABEL 9 BLANK 10-15 OPERATION CODE 16-17 BLANK 18-35 OPERAND 36-66 COMMENT IF A SOURCE LINE CONTAINS "." IN THE FIRST BYTE, THE ENTIRE LINE IS TREATED AS A COMMENT. *) CONST QUOTE = ''''; MAXERRORS = 25; (* SIZE OF ARRAY OF ERROR FLAGS *) MAXOPS = 25; (* SIZE OF OPCODE TABLE *) SYMTABLIMIT = 100; (* SIZE OF SYMBOL TABLE *) BLANK6 = ' '; BLANK8 = ' '; BLANK15 = ' '; BLANK18 = ' '; BLANK30 = ' '; BLANK31 = ' '; TYPE CHAR4 = PACKED ARRAY [1..4] OF CHAR; CHAR6 = PACKED ARRAY [1..6] OF CHAR; CHAR8 = PACKED ARRAY [1..8] OF CHAR; CHAR15 = PACKED ARRAY [1..15] OF CHAR; CHAR18 = PACKED ARRAY [1..18] OF CHAR; CHAR30 = PACKED ARRAY [1..30] OF CHAR; CHAR31 = PACKED ARRAY [1..31] OF CHAR; CHAR50 = PACKED ARRAY [1..50] OF CHAR; CHAR66 = PACKED ARRAY [1..66] OF CHAR; SOURCETYPE = RECORD (* SOURCE LINE AND SUBFIELDS *) LINE : CHAR66; COMLINE : BOOLEAN; LABL : CHAR8; OPERATION : CHAR6; OPERAND : CHAR18; COMMENT : CHAR31; END; OBJTYPE = RECORD (* OBJECT CODE, LENGTH, AND TYPE *) RECTYPE : (HEADREC, TEXTREC, ENDREC, NONE); OBJLENGTH : INTEGER; OBJCODE : CHAR30; END; SYMTABTYPE = ARRAY [0..SYMTABLIMIT] OF RECORD SYMBOL : CHAR8; ADDRESS : INTEGER; END; OPTABTYPE = ARRAY [1..MAXOPS] OF RECORD MNEMONIC : CHAR6; OPCODE : INTEGER; END; ERRTYPE = ARRAY [1..MAXERRORS] OF BOOLEAN; SYMREQTYPE = (SEARCH, STORE); SYMRETTYPE = (FOUND, NOTFOUND, ADDED, DUPLICATE, TABLEFULL); INTREQTYPE = (READLINE, WRITELINE); INTRETTYPE = (NORMAL, ENDFILE); OPRETTYPE = (VALIDOP, INVALIDOP); VAR SRCFILE,OBJFILE,LISFILE,INTFILE : TEXT; SOURCE : SOURCETYPE; (* SOURCE LINE AND SUBFIELDS *) OBJCT : OBJTYPE; (* OBJECT CODE FOR CURRENT STMT *) SYMTAB : SYMTABTYPE; (* SYMBOL TABLE *) OPTAB : OPTABTYPE; (* OPCODE TABLE *) ASCII : ARRAY[0..255] OF INTEGER; (* ASCII CONVERSION TABLE *) ERRORFLAGS : ERRTYPE; (* ERROR FLAGS FOR CURRENT STMT *) ERRORSFOUND : BOOLEAN; (* TRUE IF ANY ERRORS IN CURRENT STMT *) ERRMSG : ARRAY [1..MAXERRORS] OF CHAR50; I,LOCCTR : INTEGER; PROGNAME : CHAR6; PROGSTART : INTEGER; TEMPM : CHAR6; TEMPO : INTEGER; SWITCHOP : BOOLEAN; (* GLOBALS USED ONLY BY P2_ASSEMBLE_INST *) FIRSTSTMT,ENDFOUND : BOOLEAN; (* GLOBALS USED ONLY BY P2_WRITE_OBJ *) TEXTSTART,TEXTADDR,TEXTLENGTH : INTEGER; TEXTARRAY : ARRAY[1..60] OF CHAR; (***************************************************************) FUNCTION HEXTONUM (STR : CHAR18; FIRST : INTEGER; VAR LAST : INTEGER; VAR CONVERROR : BOOLEAN) : INTEGER; (* THIS FUNCTION EXAMINES THE STRING STR, BEGINNING WITH THE BYTE POSITION INDICATED BY FIRST, FOR A HEXADECIMAL VALUE. THIS VALUE, CONVERTED TO AN INTEGER, IS RETURNED AS THE VALUE OF THE FUNCTION AND LAST IS SET TO INDICATE THE NEXT CHARACTER IN STR AFTER THE HEX VALUE THAT WAS FOUND. IF ANY SCANNING OR CONVERSION ERRORS ARE DETECTED, CONVERROR IS SET TO TRUE. THE MAXIMUM LENGTH OF THE HEX VALUE TO BE SCANNED IS 4 HEX DIGITS. *) VAR N,I : INTEGER; SCANNING : BOOLEAN; BEGIN N := 0; I := 0; SCANNING := TRUE; CONVERROR := FALSE; WHILE SCANNING DO BEGIN IF (STR[FIRST+I] >= '0') AND (STR[FIRST+I] <= '9') THEN N := 16 * N + ORD(STR[FIRST+I]) - ORD('0') ELSE IF (STR[FIRST+I] >= 'A') AND (STR[FIRST+I] <= 'F') THEN N := 16 * N + ORD(STR[FIRST+I]) - ORD('A') + 10 ELSE IF (STR[FIRST+I] = ' ') OR (STR[FIRST+I] = ',') THEN SCANNING := FALSE ELSE BEGIN CONVERROR := TRUE; SCANNING := FALSE; END; I := I + 1; IF I > (FIRST + 3) THEN SCANNING := FALSE; END; LAST := FIRST + I - 1; IF CONVERROR THEN HEXTONUM := 0 ELSE HEXTONUM := N; END; (* HEXTONUM *) (***************************************************************) FUNCTION DECTONUM (STR : CHAR18; FIRST : INTEGER; VAR LAST : INTEGER; VAR CONVERROR : BOOLEAN) : INTEGER; (* THIS FUNCTIONS SCANS THE STRING STR, BEGINNING AT THE BYTE POSITION GIVEN BY FIRST, FOR THE CHARACTER REPRESENTATION OF A DECIMAL VALUE. THIS VALUE, CONVERTED TO NUMERIC, IS RETURNED AS THE VALUE OF THE FUNCTION, AND LAST IS SET TO INDICATE THE NEXT CHARACTER IN STR AFTER THE VALUE THAT WAS FOUND. IF ANY SCANNING OR CONVERSION ERRORS ARE FOUND, CONVERROR IS SET TO TRUE. THE MAXIMUM LENGTH VALUE TO BE SCANNED IS 4 DECIMAL DIGITS. *) VAR N,I : INTEGER; SCANNING : BOOLEAN; BEGIN N := 0; I := 0; SCANNING := TRUE; CONVERROR := FALSE; WHILE SCANNING DO BEGIN IF (STR[FIRST+I] >= '0') AND (STR[FIRST+I] <= '9') THEN N := 10 * N + ORD(STR[FIRST+I]) - ORD('0') ELSE IF STR[FIRST+I] = ' ' THEN SCANNING := FALSE ELSE BEGIN CONVERROR := TRUE; SCANNING := FALSE; END; I := I + 1; IF I > (FIRST + 3) THEN SCANNING := FALSE; END; LAST := FIRST + I - 1; IF CONVERROR THEN DECTONUM := 0 ELSE DECTONUM := N; END; (* DECTONUM *) (***************************************************************) PROCEDURE NUMTOHEX (NUM : INTEGER; VAR HEXSTR : CHAR4); (* THIS PROCEDURE CONVERTS THE NUMERIC VALUE NUM INTO A HEXADECIMAL CHARACTER STRING REPRESENTATION HEXSTR. *) VAR I : INTEGER; WORK4 : ARRAY[1..4] OF INTEGER; BEGIN FOR I := 4 DOWNTO 1 DO BEGIN WORK4[I] := NUM MOD 16; NUM := NUM DIV 16 END; FOR I := 1 TO 4 DO IF WORK4[I] < 10 THEN HEXSTR[I] := CHR(ORD('0') + WORK4[I]) ELSE HEXSTR[I] := CHR(ORD('A') + WORK4[I] - 10); END; (* NUMTOHEX *) (***************************************************************) PROCEDURE ACCESS_SYMTAB (REQUESTCODE : SYMREQTYPE; VAR RETURNCODE : SYMRETTYPE; SYMBOL : CHAR8; VAR ADDRESS : INTEGER); (* THIS PROCEDURE IS USED TO ACCESS THE SYMBOL TABLE FOR THE ASSEMBLY. IF REQUESTCODE = SEARCH, THE SYMBOL PASSED AS A PARAMETER IS SEARCHED FOR IN THE TABLE. IF THIS SYMBOL IS FOUND, RETURNCODE IS SET TO FOUND, AND ADDRESS IS SET TO THE VALUE OF THE SYMBOL (FROM THE SYMBOL TABLE). IF THE SYMBOL IS NOT FOUND IN THE TABLE, RETURNCODE IS SET TO NOTFOUND. IF REQUESTCODE = STORE, THE SYMBOL IS ADDED TO THE TABLE, WITH VALUE GIVEN BY ADDRESS. IF THE SYMBOL IS ADDED NORMALLY, RETURNCODE IS SET TO ADDED. IF THE SYMBOL ALREADY EXISTS IN THE TABLE, RETURNCODE IS SET TO DUPLICATE (AND THE SYMBOL IS NOT ADDED). IF THE TABLE IS ALREADY FULL, RETURNCODE IS SET TO TABLEFULL. THE SYMBOL TABLE IS ORGANIZED AS A HASH TABLE. THE HASHING FUNCTION SIMPLY SUMS THE ORDINAL VALUES OF ALL OF THE CHARACTERS IN THE SYMBOL. COLLISIONS ARE HANDLED BY LINEAR PROBING. *) VAR SEARCHING : BOOLEAN; I,HASH,PTR : INTEGER; BEGIN HASH := 0; FOR I := 1 TO 8 DO HASH := HASH + ORD(SYMBOL[I]); HASH := HASH MOD (SYMTABLIMIT + 1); IF REQUESTCODE = SEARCH THEN BEGIN SEARCHING := TRUE; PTR := HASH; WHILE SEARCHING DO BEGIN IF SYMTAB[PTR].SYMBOL = SYMBOL THEN BEGIN RETURNCODE := FOUND; ADDRESS := SYMTAB[PTR].ADDRESS; SEARCHING := FALSE; END ELSE IF SYMTAB[PTR].SYMBOL = BLANK8 THEN BEGIN RETURNCODE := NOTFOUND; ADDRESS := 0; SEARCHING := FALSE; END ELSE BEGIN PTR := (PTR + 1) MOD (SYMTABLIMIT + 1); IF PTR = HASH THEN BEGIN RETURNCODE := NOTFOUND; ADDRESS := 0; SEARCHING := FALSE; END; END; END; END ELSE BEGIN SEARCHING := TRUE; PTR := HASH; WHILE SEARCHING DO BEGIN IF SYMTAB[PTR].SYMBOL = SYMBOL THEN BEGIN RETURNCODE := DUPLICATE; SEARCHING := FALSE; END ELSE IF SYMTAB[PTR].SYMBOL = BLANK8 THEN BEGIN RETURNCODE := ADDED; SYMTAB[PTR].SYMBOL := SYMBOL; SYMTAB[PTR].ADDRESS := ADDRESS; SEARCHING := FALSE; END ELSE BEGIN PTR := (PTR + 1) MOD (SYMTABLIMIT + 1); IF PTR = HASH THEN BEGIN RETURNCODE := TABLEFULL; SEARCHING := FALSE; END; END; END; END; END; (* ACCESS_SYMTAB *) (***************************************************************) PROCEDURE ACCESS_INT_FILE (REQUESTCODE : INTREQTYPE; VAR RETURNCODE : INTRETTYPE; VAR SOURCE : SOURCETYPE; VAR ERRORSFOUND : BOOLEAN; VAR ERRORFLAGS : ERRTYPE); (* THIS PROCEDURE IS USED TO ACCESS THE INTERMEDIATE FILE INTFILE. IF REQUESTCODE = WRITELINE, THE CURRENT SOURCE PROGRAM LINE IS WRITTEN, FOLLOWED BY A BOOLEAN VALUE (T OR F) THAT INDICATES WHETHER THIS IS A COMMENT LINE, AND THE CURRENT LOCATION COUNTER VALUE. FOR NON-COMMENT LINES, THE SUBFIELDS ARE ALSO WRITTEN OUT, FOLLOWED BY THE VALUE OF ERRORSFOUND (T OR F). IF ERRORSFOUND WAS TRUE, THIS IS FOLLOWED BY THE VALUES IN ERRORFLAGS. IF REQUESTCODE = READLINE, THE VARIABLES DESCRIBED ABOVE ARE READ FROM THE INTERMEDIATE FILE. VARIABLES THAT ARE NOT REPRESENTED IN THE FILE (FOR EXAMPLE, SOURCE.LABL AND ERRORSFOUND FOR A COMMENT LINE) ARE SET TO BLANK (FOR CHARACTER FIELDS) OR TO FALSE (FOR BOOLEAN VARIABLES). IF THE END OF FILE HAS BEEN REACHED, RETURNCODE IS SET TO ENDFILE; OTHERWISE, IT IS SET TO NORMAL. *) VAR I : INTEGER; CH : CHAR; BEGIN IF REQUESTCODE = READLINE THEN BEGIN IF EOF(INTFILE) THEN RETURNCODE := ENDFILE ELSE BEGIN RETURNCODE := NORMAL; FOR I := 1 TO 66 DO READ (INTFILE,SOURCE.LINE[I]); READ (INTFILE,CH); IF CH = 'T' THEN SOURCE.COMLINE := TRUE ELSE SOURCE.COMLINE := FALSE; READLN (INTFILE,LOCCTR); IF SOURCE.COMLINE THEN BEGIN SOURCE.LABL := BLANK8; SOURCE.OPERATION := BLANK6; SOURCE.OPERAND := BLANK18; SOURCE.COMMENT := BLANK31; ERRORSFOUND := FALSE; FOR I := 1 TO MAXERRORS DO ERRORFLAGS[I] := FALSE; END ELSE BEGIN FOR I := 1 TO 8 DO READ (INTFILE,SOURCE.LABL[I]); FOR I := 1 TO 6 DO READ (INTFILE,SOURCE.OPERATION[I]); FOR I := 1 TO 18 DO READ (INTFILE,SOURCE.OPERAND[I]); FOR I := 1 TO 31 DO READ (INTFILE,SOURCE.COMMENT[I]); READLN (INTFILE,CH); IF CH = 'T' THEN ERRORSFOUND := TRUE ELSE ERRORSFOUND := FALSE; IF ERRORSFOUND THEN BEGIN FOR I := 1 TO MAXERRORS DO BEGIN READ (INTFILE,CH); IF CH = 'T' THEN ERRORFLAGS[I] := TRUE ELSE ERRORFLAGS[I] := FALSE; END; READLN (INTFILE); END ELSE FOR I := 1 TO MAXERRORS DO ERRORFLAGS[I] := FALSE; END; END; END ELSE BEGIN FOR I := 1 TO 66 DO WRITE (INTFILE,SOURCE.LINE[I]); IF SOURCE.COMLINE THEN WRITE (INTFILE,'T') ELSE WRITE (INTFILE,'F'); WRITELN (INTFILE,LOCCTR); IF NOT SOURCE.COMLINE THEN BEGIN FOR I := 1 TO 8 DO WRITE (INTFILE,SOURCE.LABL[I]); FOR I := 1 TO 6 DO WRITE (INTFILE,SOURCE.OPERATION[I]); FOR I := 1 TO 18 DO WRITE (INTFILE,SOURCE.OPERAND[I]); FOR I := 1 TO 31 DO WRITE (INTFILE,SOURCE.COMMENT[I]); IF ERRORSFOUND THEN WRITELN (INTFILE,'T') ELSE WRITELN (INTFILE,'F'); IF ERRORSFOUND THEN BEGIN FOR I := 1 TO MAXERRORS DO IF ERRORFLAGS[I] THEN WRITE (INTFILE,'T') ELSE WRITE (INTFILE,'F'); WRITELN (INTFILE); END; END; END; END; (* ACCESS_INT_FILE *) (***************************************************************) PROCEDURE P1_READ_SOURCE (VAR SOURCE : SOURCETYPE; VAR ENDOFINPUT : BOOLEAN; VAR ERRORSFOUND : BOOLEAN; VAR ERRORFLAGS : ERRTYPE); (* THIS PROCEDURE READS THE NEXT LINE FROM SRCFILE. IF THERE ARE NO MORE LINES ON SRCFILE, ENDOFINPUT IS SET TO TRUE. IF THE SOURCE STATEMENT CONTAINS A "." IN COLUMN 1, THEN SOURCE.COMLINE IS SET TO TRUE. OTHERWISE, THE SUBFIELDS OF THE STATEMENT (LABL, OPERATION, OPERAND, COMMENT) ARE SCANNED. ERRORS THAT MAY BE DETECTED: 1, 2, 3 (SEE THE INITIALIZATION OF THE ARRAY ERRMSG IN THE MAIN PROCEDURE FOR ERROR DESCRIPTIONS) *) VAR I,J : INTEGER; BEGIN IF EOF(SRCFILE) THEN ENDOFINPUT := TRUE ELSE BEGIN FOR I := 1 TO 66 DO SOURCE.LINE[I] := ' '; FOR I := 1 TO MAXERRORS DO ERRORFLAGS[I] := FALSE; ERRORSFOUND := FALSE; I := 1; WHILE (I <= 66) AND (NOT EOLN(SRCFILE)) DO BEGIN READ(SRCFILE,SOURCE.LINE[I]); I := I + 1; END; READLN(SRCFILE); IF SOURCE.LINE[1] = '.' THEN SOURCE.COMLINE := TRUE ELSE SOURCE.COMLINE := FALSE; SOURCE.LABL := BLANK8; SOURCE.OPERATION := BLANK6; SOURCE.OPERAND := BLANK18; SOURCE.COMMENT := BLANK31; IF NOT SOURCE.COMLINE THEN BEGIN I := 1; IF (SOURCE.LINE[1] >= 'A') AND (SOURCE.LINE[1] <= 'Z') THEN BEGIN (* THERE IS A LABEL *) WHILE (I <= 8) AND ((SOURCE.LINE[I] >= 'A') AND (SOURCE.LINE[I] <= 'Z')) OR ((SOURCE.LINE[I] >= '0') AND (SOURCE.LINE[I] <= '9')) DO BEGIN SOURCE.LABL[I] := SOURCE.LINE[I]; I := I + 1; END; END; FOR J := I TO 9 DO IF SOURCE.LINE[J] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[1] := TRUE; (* ILLEGAL LABEL FIELD *) END; I := 10; IF (SOURCE.LINE[I] >= 'A') AND (SOURCE.LINE[I] <= 'Z') THEN BEGIN (* THERE IS AN OPERATION CODE *) WHILE (I <= 15) AND ((SOURCE.LINE[I] >= 'A') AND (SOURCE.LINE[I] <= 'Z')) OR ((SOURCE.LINE[I] >= '0') AND (SOURCE.LINE[I] <= '9')) DO BEGIN SOURCE.OPERATION[I-9] := SOURCE.LINE[I]; I := I + 1; END; END ELSE BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[2] := TRUE; (* MISSING OPERATION CODE *) END; FOR J := I TO 17 DO IF SOURCE.LINE[J] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[3] := TRUE; (* ILLEGAL OPERATION FIELD *) END; FOR I := 18 TO 35 DO SOURCE.OPERAND[I-17] := SOURCE.LINE[I]; FOR I := 36 TO 66 DO SOURCE.COMMENT[I-35] := SOURCE.LINE[I]; END; END; END; (* P1_READ_SOURCE *) (***************************************************************) PROCEDURE P1_ASSIGN_LOC (SOURCE : SOURCETYPE; LOCCTR : INTEGER; VAR NEWLOCCTR : INTEGER; VAR ERRORSFOUND : BOOLEAN; VAR ERRFLAGS : ERRTYPE); (* THIS PROCEDURE UPDATES THE LOCATION COUNTER VALUE BASED ON THE TYPE OF STATEMENT BEING PROCESSED, PLACING THE UPDATED VALUE IN NEWLOCCTR. ERRORS DETECTED: 4, 5, 6, 7, 8, 9, 10, 11, 12 *) VAR SCANNING,CONVERROR : BOOLEAN; I,J,TEMPLOC,NWORDS,NBYTES : INTEGER; BEGIN NEWLOCCTR := LOCCTR; IF SOURCE.OPERATION = 'START ' THEN (* START STATEMENT -- CONVERT STARTING ADDRESS AND STORE IN LOCCTR ERRORS DETECTED: 4, 5 *) BEGIN IF SOURCE.OPERAND[1] = ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[4] := TRUE; (* MISSING OR MISPLACED OPERAND IN START STATEMENT *) END ELSE BEGIN TEMPLOC := HEXTONUM(SOURCE.OPERAND,1,I,CONVERROR); IF CONVERROR THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[5] := TRUE; (* ILLEGAL OPERAND IN START STATEMENT *) END; FOR J := I TO 18 DO IF SOURCE.OPERAND[J] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[5] := TRUE; (* ILLEGAL OPERAND IN START STATEMENT *) END; IF (NOT ERRORFLAGS[4]) AND (NOT ERRORFLAGS[5]) THEN NEWLOCCTR := TEMPLOC; END; END ELSE IF SOURCE.OPERATION = 'WORD ' THEN (* WORD STATEMENT -- ADD 3 TO LOCCTR *) BEGIN NEWLOCCTR := LOCCTR + 3; END ELSE IF SOURCE.OPERATION = 'BYTE ' THEN (* BYTE STATEMENT -- ADD NUMBER OF CHARACTERS (FOR C'...') OR NUMBER OF HEX DIGITS DIVIDED BY 2 (FOR X'...') TO LOCCTR. ERRORS DETECTED: 6, 7, 8 *) BEGIN IF SOURCE.OPERAND[1] = 'C' THEN BEGIN IF SOURCE.OPERAND[2] = QUOTE THEN BEGIN I := 3; SCANNING := TRUE; WHILE SCANNING DO BEGIN IF SOURCE.OPERAND[I] = QUOTE THEN SCANNING := FALSE ELSE BEGIN I := I + 1; IF I > 18 THEN SCANNING := FALSE; END; END; IF I > 18 THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[6] := TRUE; (* ILLEGAL OPERAND IN BYTE STATEMENT *) END; FOR J := I + 1 TO 18 DO IF SOURCE.OPERAND[J] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[6] := TRUE; (* ILLEGAL OPERAND IN BYTE STMT *) END; IF NOT ERRORFLAGS[6] THEN NEWLOCCTR := LOCCTR + I - 3; END ELSE BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[6] := TRUE; (* ILLEGAL OPERAND IN BYTE STMT *) END END ELSE IF SOURCE.OPERAND[1] = 'X' THEN BEGIN IF SOURCE.OPERAND[2] = QUOTE THEN BEGIN I := 3; SCANNING := TRUE; WHILE SCANNING DO BEGIN IF SOURCE.OPERAND[I] = QUOTE THEN SCANNING := FALSE ELSE BEGIN I := I + 1; IF I > 18 THEN SCANNING := FALSE; END; END; IF I > 18 THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[6] := TRUE; (* ILLEGAL OPERAND IN BYTE STATEMENT *) END; FOR J := I + 1 TO 18 DO IF SOURCE.OPERAND[J] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[6] := TRUE; (* ILLEGAL OPERAND IN BYTE STMT *) END; IF ((I - 3) MOD 2) <> 0 THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[7] := TRUE; (* ODD LENGTH HEX STRING IN BYTE STATEMENT *) END; IF NOT ERRORFLAGS[6] THEN NEWLOCCTR := LOCCTR + (I - 3) DIV 2; END ELSE BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[6] := TRUE; (* ILLEGAL OPERAND IN BYTE STMT *) END END ELSE BEGIN ERRORSFOUND := TRUE; IF SOURCE.OPERAND[1] = ' ' THEN ERRORFLAGS[8] := TRUE (* MISSING OR MISPLACED OPERAND IN BYTE STATEMENT *) ELSE ERRORFLAGS[6] := TRUE; (* ILLEGAL OPERAND IN BYTE STATEMENT *) END END ELSE IF SOURCE.OPERATION = 'RESW ' THEN (* RESW STATEMENT -- ADD 3 * (NUMBER OF WORDS RESERVED) TO LOCCTR. ERRORS DETECTED: 9, 10 *) BEGIN IF SOURCE.OPERAND[1] = ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[9] := TRUE; (* MISSING OR MISPLACED OPERAND IN RESW STATEMENT *) END ELSE BEGIN NWORDS := DECTONUM (SOURCE.OPERAND,1,I,CONVERROR); IF CONVERROR THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[10] := TRUE; (* ILLEGAL OPERAND IN RESW *) END; FOR J := I + 1 TO 18 DO IF SOURCE.OPERAND[J] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[10] := TRUE; (* ILLEGAL OPERAND IN RESW *) END; IF NOT ERRORFLAGS[10] THEN NEWLOCCTR := LOCCTR + 3 * NWORDS; END END ELSE IF SOURCE.OPERATION = 'RESB ' THEN (* RESB STATEMENT -- ADD NUMBER OF BYTES RESERVED TO LOCCTR. ERRORS DETECTED: 11, 12 *) BEGIN IF SOURCE.OPERAND[1] = ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[11] := TRUE; (* MISSING OR MISPLACED OPERAND IN RESB STATEMENT *) END ELSE BEGIN NBYTES := DECTONUM (SOURCE.OPERAND,1,I,CONVERROR); IF CONVERROR THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[12] := TRUE; (* ILLEGAL OPERAND IN RESB *) END; FOR J := I + 1 TO 18 DO IF SOURCE.OPERAND[J] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[12] := TRUE; (* ILLEGAL OPERAND IN RESB *) END; IF NOT ERRORFLAGS[12] THEN NEWLOCCTR := LOCCTR + NBYTES; END END ELSE IF SOURCE.OPERATION = 'END ' THEN BEGIN (* NO ACTION IN PASS 1 *) END ELSE (* ASSUME MACHINE INSTRUCTION *) BEGIN NEWLOCCTR := LOCCTR + 3; END; END; (* P1_ASSIGN_LOC *) (***************************************************************) PROCEDURE P1_ASSIGN_SYM (SOURCE : SOURCETYPE; LOCCTR : INTEGER; VAR ERRORSFOUND : BOOLEAN; VAR ERRORFLAGS : ERRTYPE); (* THIS PROCEDURE ADDS THE LABEL FROM A SOURCE STATEMENT TO THE SYMBOL TABLE, USING THE CURRENT LOCATION COUNTER VALUE AS ITS ADDRESS. ERRORS DETECTED: 13,14 *) VAR SYMTABRET : SYMRETTYPE; ADDRESS : INTEGER; BEGIN IF (NOT ERRORFLAGS[1]) AND (SOURCE.LABL <> BLANK8) THEN BEGIN ACCESS_SYMTAB (SEARCH, SYMTABRET, SOURCE.LABL, ADDRESS); IF SYMTABRET = NOTFOUND THEN BEGIN ADDRESS := LOCCTR; ACCESS_SYMTAB (STORE, SYMTABRET, SOURCE.LABL, ADDRESS); IF SYMTABRET = TABLEFULL THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[14] := TRUE; (* SYMBOL TABLE OVERFLOW *) END; END ELSE BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[13] := TRUE; (* DUPLICATE LABEL *) END; END; END; (* P1_ASSIGN_SYM *) (***************************************************************) PROCEDURE P2_SEARCH_OPTAB (MNEMONIC : CHAR6; VAR RETURNCODE : OPRETTYPE; VAR OPCODE : INTEGER); (* THIS PROCEDURE SEARCHES THE OPERATION CODE TABLE (OPTAB) FOR THE MNEMONIC PASSED AS PARAMETER. IF THE MNEMONIC IS FOUND, RETURNCODE IS SET TO VALIDOP AND OPCODE IS SET TO THE VALUE GIVEN IN OPTAB. OTHERWISE, RETURNCODE IS SET TO INVALIDOP AND OPCODE IS SET TO 255. THE ENTRIES IN OPTAB ARE ORDERED BY MNEMONIC. THIS PROCEDURE USES A BINARY SEARCH. *) VAR LOW,MID,HIGH : INTEGER; BEGIN HIGH := MAXOPS; LOW := 1; REPEAT MID := (LOW + HIGH) DIV 2; IF MNEMONIC < OPTAB[MID].MNEMONIC THEN HIGH := MID - 1 ELSE LOW := MID + 1; UNTIL (MNEMONIC = OPTAB[MID].MNEMONIC) OR (HIGH < LOW); IF MNEMONIC = OPTAB[MID].MNEMONIC THEN BEGIN RETURNCODE := VALIDOP; OPCODE := OPTAB[MID].OPCODE; END ELSE BEGIN RETURNCODE := INVALIDOP; OPCODE := 255; END; END; (* P2_SEARCH_OPTAB *) (***************************************************************) PROCEDURE P2_ASSEMBLE_INST (SOURCE : SOURCETYPE; LOCCTR : INTEGER; VAR ERRORSFOUND : BOOLEAN; VAR ERRORFLAGS : ERRTYPE; VAR OBJCT : OBJTYPE); (* THIS PROCEDURE GENERATES THE OBJECT CODE (IF ANY) FOR THE SOURCE STATEMENT CURRENTLY BEING PROCESSED. THE OBJECT CODE GENERATED IS PLACED IN OBJECT (THIS RECORD ALSO INCLUDES AN INDICATION OF THE TYPE OF OBJECT CODE AND THE LENGTH). THIS PROCEDURE ALSO TESTS FOR ERRORS SUCH AS A MISSING START OR END OR STATEMENTS IMPROPERLY FOLLOWING THE END STATEMENT. IN ORDER TO DO THIS, IT MAKES USE OF THE GLOBAL VARIABLES FIRSTSTMT AND ENDFOUND. ERRORS DETECTED: 6, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 *) VAR OPRETURN : OPRETTYPE; SYMTABRETURN : SYMRETTYPE; I,J : INTEGER; TEMP : CHAR4; HEXCHAR : CHAR; OPSYMBOL : CHAR8; OPADDRESS,OPAD1,OPAD2 : INTEGER; INDEXED : BOOLEAN; ASCIIVAL,OPCODE,WORDVALUE : INTEGER; NEGATIVE,CONVERROR : BOOLEAN; BEGIN IF ENDFOUND THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[22] := TRUE; (* STATEMENT SHOULD NOT FOLLOW END *) END; IF SOURCE.OPERATION = 'START ' THEN (* START STATEMENT -- IF THIS IS THE FIRST SOURCE LINE, SET OBJECT TYPE = HEADREC AND OBJECT CODE = PROGRAM NAME. *) BEGIN IF NOT FIRSTSTMT THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[15] := TRUE; (* DUPLICATE OR MISPLACED START STATEMENT *) END; OBJCT.RECTYPE := HEADREC; OBJCT.OBJLENGTH := 0; FOR I := 1 TO 6 DO OBJCT.OBJCODE[I] := SOURCE.LABL[I]; FOR I := 7 TO 15 DO OBJCT.OBJCODE[I] := ' '; END ELSE IF SOURCE.OPERATION = 'WORD ' THEN (* WORD STATEMENT -- THE OPERAND MAY BE EITHER AN INTEGER OR A SYMBOL THAT APPEARS AS A LABEL IN THE PROGRAM. *) BEGIN IF SOURCE.OPERAND[1] = ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[18] := TRUE; (* MISSING OR MISPLACED OPERAND IN WORD *) END ELSE IF (SOURCE.OPERAND[1] >= 'A') AND (SOURCE.OPERAND[1] <= 'Z') THEN (* OPERAND IS A LABEL. SCAN THE OPERAND FIELD FOR THE LABEL, AND LOOK IT UP IN THE SYMBOL TABLE. IF FOUND, GENERATE OBJECT TYPE = TEXTREC AND OBJECT CODE = ADDRESS FOR SYMBOL. ERRORS DETECTED: 17, 21 *) BEGIN OPSYMBOL := BLANK8; I := 1; WHILE (I <= 8) AND (SOURCE.OPERAND[I] <> ' ') DO BEGIN OPSYMBOL[I] := SOURCE.OPERAND[I]; I := I + 1; END; FOR J := I TO 18 DO IF SOURCE.OPERAND[I] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[17] := TRUE; (* ILLEGAL OPERAND IN WORD STMT *) END; ACCESS_SYMTAB (SEARCH, SYMTABRETURN, OPSYMBOL, OPADDRESS); IF SYMTABRETURN <> FOUND THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[21] := TRUE; (* UNDEFINED SYMBOL IN OPERAND *) END; IF (NOT ERRORFLAGS[17]) AND (NOT ERRORFLAGS[21]) THEN BEGIN OBJCT.RECTYPE := TEXTREC; OBJCT.OBJLENGTH := 6; NUMTOHEX(OPADDRESS,TEMP); OBJCT.OBJCODE[1] := '0'; OBJCT.OBJCODE[2] := '0'; FOR I := 1 TO 4 DO OBJCT.OBJCODE[2+I] := TEMP[I]; END; END ELSE (* OPERAND IS AN INTEGER -- USE DECTONUM TO CONVERT TO A NUMERIC VALUE, THEN SCAN THE REST OF THE OPERAND FIELD TO BE SURE NOTHING ELSE IS THERE. IF THE LEADING CHARACTER OF THE INTEGER WAS A MINUS SIGN, CONVERT TO 2'S COMPLEMENT REPRESENTATION. GENERATE OBJECT TYPE = TEXTREC AND OBJECT CODE = VALUE OF THE CONVERTED INTEGER IN HEX. ERRORS DETECTED: 17 *) BEGIN IF SOURCE.OPERAND[1] = '-' THEN BEGIN WORDVALUE := DECTONUM (SOURCE.OPERAND,2,I,CONVERROR); NEGATIVE := TRUE; END ELSE BEGIN WORDVALUE := DECTONUM (SOURCE.OPERAND,1,I,CONVERROR); NEGATIVE := FALSE; END; IF CONVERROR THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[17] := TRUE; (* ILLEGAL OPERAND IN WORD STATEMENT *) END; FOR J := I+1 TO 18 DO IF SOURCE.OPERAND[J] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[17] := TRUE; (* ILLEGAL OPERAND IN WORD STMT *) END; IF (NOT ERRORFLAGS[17]) AND (NOT ERRORFLAGS[18]) THEN BEGIN OBJCT.RECTYPE := TEXTREC; OBJCT.OBJLENGTH := 6; IF NEGATIVE THEN BEGIN WORDVALUE := 65536 - WORDVALUE; NUMTOHEX(WORDVALUE,TEMP); OBJCT.OBJCODE[1] := 'F'; OBJCT.OBJCODE[2] := 'F'; FOR I := 1 TO 4 DO OBJCT.OBJCODE[I+2] := TEMP[I]; END ELSE BEGIN NUMTOHEX(WORDVALUE,TEMP); OBJCT.OBJCODE[1] := '0'; OBJCT.OBJCODE[2] := '0'; FOR I := 1 TO 4 DO OBJCT.OBJCODE[I+2] := TEMP[I]; END; END; END; END ELSE IF SOURCE.OPERATION = 'BYTE ' THEN (* BYTE STATEMENT -- THE OPERAND MUST BE EITHER C'...' OR X'...'. IF A FORMAT ERROR IN THE OPERAND (ERRORS 6, 7, 8) WAS DETECTED PREVIOUSLY, DO NOT ATTEMPT TO ASSEMBLE *) BEGIN IF (NOT ERRORFLAGS[6]) AND (NOT ERRORFLAGS[7]) AND (NOT ERRORFLAGS[8]) THEN BEGIN (* OPERAND IS C'...' -- USE THE ASCII CONVERSION TABLE TO FIND THE ASCII CODE FOR EACH CHARACTER AND PACK INTO OBJECT CODE. SET OBJECT TYPE = TEXTREC. *) IF SOURCE.OPERAND[1] = 'C' THEN BEGIN I := 1; WHILE (SOURCE.OPERAND[2+I] <> QUOTE) DO BEGIN ASCIIVAL := ASCII[ORD(SOURCE.OPERAND[2+I])]; NUMTOHEX(ASCIIVAL,TEMP); OBJCT.OBJCODE[2*I-1] := TEMP[3]; OBJCT.OBJCODE[2*I] := TEMP[4]; I := I + 1; END; OBJCT.OBJLENGTH := 2 * (I - 1); OBJCT.RECTYPE := TEXTREC; END ELSE (* OPERAND IS X'...' -- PACK HEX DIGITS INTO OBJECT CODE AND SET OBJECT TYPE = TEXTREC. ERRORS DETECTED: 6 *) BEGIN I := 1; WHILE (SOURCE.OPERAND[2+I] <> QUOTE) DO BEGIN HEXCHAR := SOURCE.OPERAND[2+I]; IF ((HEXCHAR >= '0') AND (HEXCHAR <= '9')) OR ((HEXCHAR >= 'A') AND (HEXCHAR <= 'F')) THEN OBJCT.OBJCODE[I] := HEXCHAR ELSE BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[6] := TRUE; (* ILLEGAL OPERAND IN BYTE *) END; I := I + 1; END; OBJCT.OBJLENGTH := I - 1; OBJCT.RECTYPE := TEXTREC; END; END; END ELSE IF (SOURCE.OPERATION = 'RESB ') OR (SOURCE.OPERATION = 'RESW ') THEN (* NO OBJECT CODE FOR RESB OR RESW *) BEGIN OBJCT.RECTYPE := NONE; END ELSE IF SOURCE.OPERATION = 'END ' THEN (* END STATEMENT -- THE OPERAND MUST BE A SYMBOL USED AS A LABEL IN THE PROGRAM. LOOK UP THIS LABEL IN THE SYMBOL TABLE TO FIND THE TRANSFER ADDRESS. GENERATE OBJECT CODE = TRANSFER ADDRESS AND OBJECT TYPE = ENDREC. ERRORS DETECTED: 19, 20 *) BEGIN ENDFOUND := TRUE; IF SOURCE.OPERAND[1] = ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[19] := TRUE; (* MISSING OR MISPLACED OPERAND IN END *) END ELSE BEGIN OPSYMBOL := BLANK8; I := 1; WHILE (I <= 8) AND (SOURCE.OPERAND[I] <> ' ') DO BEGIN OPSYMBOL[I] := SOURCE.OPERAND[I]; I := I + 1; END; FOR J := I TO 18 DO IF SOURCE.OPERAND[I] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[20] := TRUE; (* ILLEGAL OPERAND IN END STMT *) END; ACCESS_SYMTAB (SEARCH, SYMTABRETURN, OPSYMBOL, OPADDRESS); IF SYMTABRETURN <> FOUND THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[21] := TRUE; (* UNDEFINED SYMBOL IN OPERAND *) END; IF (NOT ERRORFLAGS[20]) AND (NOT ERRORFLAGS[21]) THEN BEGIN OBJCT.RECTYPE := ENDREC; OBJCT.OBJLENGTH := 6; NUMTOHEX(OPADDRESS,TEMP); OBJCT.OBJCODE[1] := '0'; OBJCT.OBJCODE[2] := '0'; FOR I := 1 TO 4 DO OBJCT.OBJCODE[2+I] := TEMP[I]; END; END; END ELSE (* NOT AN ASSEMBLER DIRECTIVE -- PRESUMABLY WE HAVE A MACHINE INSTRUCTION. THE OPERAND SHOULD BE EITHER AN ACTUAL ADDRESS (IN HEX) OR A SYMBOL THAT APPEARS AS A LABEL IN THE PROGRAM. EITHER TYPE OF OPERAND MAY BE FOLLOWED BY ',X' TO INDICATE INDEXED ADDRESSING. *) BEGIN IF (SOURCE.OPERAND[1] >= '0') AND (SOURCE.OPERAND[1] <= '9') THEN (* OPERAND STARTS WITH 0 THROUGH 9 -- IT MUST BE AN ADDRESS. CONVERT THE ADDRESS TO NUMERIC. ERRORS DETECTED: 23 *) BEGIN OPADDRESS := HEXTONUM(SOURCE.OPERAND,1,I,CONVERROR); IF CONVERROR THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[23] := TRUE; (* ILLEGAL OPERAND FIELD *) END; END ELSE (* OPERAND IS A LABEL -- SCAN FOR THE LABEL AND LOOK IT UP IN THE SYMBOL TABLE. ERRORS DETECTED: 21 *) BEGIN OPSYMBOL := BLANK8; I := 1; WHILE (I <= 8) AND (SOURCE.OPERAND[I] <> ' ') AND (SOURCE.OPERAND[I] <> ',') DO BEGIN OPSYMBOL[I] := SOURCE.OPERAND[I]; I := I + 1; END; ACCESS_SYMTAB (SEARCH, SYMTABRETURN, OPSYMBOL, OPADDRESS); IF SYMTABRETURN <> FOUND THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[21] := TRUE; (* UNDEFINED SYMBOL IN OPERAND *) END; END; IF (SOURCE.OPERAND[I] = ',') AND (SOURCE.OPERAND[I+1] = 'X') THEN (* ADDRESS OR LABEL IS FOLLOWED BY ',X' -- SET INDEXED = TRUE *) BEGIN INDEXED := TRUE; I := I + 2; END ELSE INDEXED := FALSE; IF (I = 1) AND (SOURCE.OPERATION <> 'RSUB ') THEN (* EVERY INSTRUCTION EXCEPT RSUB MUST HAVE AN OPERAND *) BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[25] := TRUE; (* MISSING OR MISPLACED OPERAND *) END; FOR J := I TO 18 DO (* BE SURE THE REST OF THE OPERAND FIELD IS BLANK *) IF SOURCE.OPERAND[I] <> ' ' THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[23] := TRUE; (* ILLEGAL OPERAND FIELD *) END; (* LOOK UP THE OPERATION CODE IN OPTAB TO FIND THE MACHINE OPCODE. GENERATE THE OBJECT CODE INSTRUCTION *) P2_SEARCH_OPTAB (SOURCE.OPERATION, OPRETURN, OPCODE); IF OPRETURN <> VALIDOP THEN BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[24] := TRUE; (* UNRECOGNIZED OPERATION CODE *) END; IF (NOT ERRORFLAGS[21]) AND (NOT ERRORFLAGS[23]) AND (NOT ERRORFLAGS[24]) THEN BEGIN OBJCT.RECTYPE := TEXTREC; OBJCT.OBJLENGTH := 6; NUMTOHEX(OPCODE,TEMP); OBJCT.OBJCODE[1] := TEMP[3]; OBJCT.OBJCODE[2] := TEMP[4]; OPAD1 := OPADDRESS DIV 256; OPAD2 := OPADDRESS MOD 256; IF INDEXED THEN OPAD1 := OPAD1 + 128; NUMTOHEX(OPAD1,TEMP); OBJCT.OBJCODE[3] := TEMP[3]; OBJCT.OBJCODE[4] := TEMP[4]; NUMTOHEX(OPAD2,TEMP); OBJCT.OBJCODE[5] := TEMP[3]; OBJCT.OBJCODE[6] := TEMP[4]; END; END; IF (FIRSTSTMT) AND (SOURCE.OPERATION <> 'START ') THEN (* THE FIRST SOURCE STATEMENT (EXCEPT FOR COMMENTS) MUST BE START *) BEGIN ERRORSFOUND := TRUE; ERRORFLAGS[16] := TRUE; (* MISSING OR MISPLACED START STATEMENT *) END; FIRSTSTMT := FALSE; END; (* P2_ASSEMBLE_INST *) (***************************************************************) PROCEDURE P2_WRITE_LIST; (* THIS PROCEDURE WRITES A LINE OF THE ASSEMBLY LISTING, WHICH CONTAINS THE SOURCE STATEMENT AND (EXCEPT FOR COMMENT LINES) THE CURRENT LOCATION COUNTER VALUE AND ANT OBJECT CODE THAT WAS GENERATED. IF ANY ERRORS WERE DETECTED, THE ERROR MESSAGES ARE PRINTED FOLLOWING THE SOURCE STATEMENT. A MAXIMUM OF 6 HEX DIGITS OF OBJECT CODE ARE PRINTED PER LINE. IF THE OBJECT CODE GENERATED FROM THE STATEMENT IS LONGER THAN THIS, ADDITIONAL LINES ARE PRINTED. *) VAR I,J : INTEGER; TEMP : CHAR4; BEGIN IF SOURCE.COMLINE THEN BEGIN WRITE (LISFILE,' '); END ELSE BEGIN NUMTOHEX(LOCCTR,TEMP); WRITE(LISFILE,TEMP,' '); I := 1; IF SOURCE.OPERATION <> 'END ' THEN WHILE (I <= 6) AND (I <= OBJCT.OBJLENGTH) DO BEGIN WRITE(LISFILE,OBJCT.OBJCODE[I]); I := I + 1; END; FOR J := I TO 6 DO WRITE(LISFILE,' '); END; WRITELN(LISFILE,' ',SOURCE.LINE); IF OBJCT.OBJLENGTH > 6 THEN BEGIN FOR I := 7 TO OBJCT.OBJLENGTH DO BEGIN IF (I MOD 6) = 1 THEN BEGIN IF I <> 7 THEN WRITELN(LISFILE); WRITE(LISFILE,' '); END; WRITE(LISFILE,OBJCT.OBJCODE[I]); END; WRITELN(LISFILE); END; FOR I := 1 TO MAXERRORS DO IF ERRORFLAGS[I] THEN WRITELN(LISFILE,' **** ',ERRMSG[I]); END; (* P2_WRITE_LIST *) (***************************************************************) PROCEDURE P2_WRITE_OBJ (OBJCT : OBJTYPE; LOCCTR : INTEGER; PROGNAME : CHAR6; PROGLENGTH : INTEGER); (* THIS PROCEDURE PLACES THE GENERATED OBJECT CODE INTO THE OBJECT PROGRAM. THERE ARE THREE TYPES OF OBJECT CODE TO BE HANDLED -- HEADREC (FROM START STATEMENT), ENDREC (FROM END STATEMENT), AND TEXTREC (FROM INSTRUCTIONS AND WORD AND BYTE STATEMENTS). TO KEEP TRACK OF THE TEXT RECORD CURRENTLY BEING CONSTRUCTED, THIS PROCEDURE USES THE GLOBAL VARIABLES TEXTSTART, TEXTADDR, TEXTLENGTH, AND TEXTARRAY. *) VAR I : INTEGER; TEMP : CHAR4; TEXTBYTES : INTEGER; BEGIN IF OBJCT.RECTYPE = HEADREC THEN (* HEADREC -- GENERATE HEADER RECORD IN OBJECT PROGRAM *) BEGIN WRITE(OBJFILE,'H',PROGNAME); NUMTOHEX(LOCCTR,TEMP); WRITE(OBJFILE,'00',TEMP); NUMTOHEX(PROGLENGTH,TEMP); WRITE(OBJFILE,'00',TEMP); WRITELN(OBJFILE); END ELSE IF OBJCT.RECTYPE = TEXTREC THEN (* TEXTREC -- PUT OBJECT CODE INTO A TEXT RECORD. IF THE OBJECT CODE WILL NOT FIT INTO THE CURRENT TEXT RECORD, OR IF ADDRESSES ARE NOT CONTIGUOUS, A NEW TEXT RECORD MUST BE STARTED. *) BEGIN IF TEXTLENGTH = 0 THEN BEGIN TEXTADDR := LOCCTR; TEXTSTART := LOCCTR; END; IF ((TEXTLENGTH + OBJCT.OBJLENGTH) > 60) OR (LOCCTR <> TEXTADDR) THEN BEGIN WRITE(OBJFILE,'T'); NUMTOHEX(TEXTSTART,TEMP); WRITE(OBJFILE,'00',TEMP); TEXTBYTES := TEXTLENGTH DIV 2; NUMTOHEX(TEXTBYTES,TEMP); WRITE(OBJFILE,TEMP[3],TEMP[4]); FOR I := 1 TO TEXTLENGTH DO WRITE(OBJFILE,TEXTARRAY[I]); WRITELN(OBJFILE); TEXTLENGTH := 0; TEXTSTART := LOCCTR; END; FOR I := 1 TO OBJCT.OBJLENGTH DO TEXTARRAY[TEXTLENGTH+I] := OBJCT.OBJCODE[I]; TEXTLENGTH := TEXTLENGTH + OBJCT.OBJLENGTH; TEXTADDR := LOCCTR + OBJCT.OBJLENGTH DIV 2; END ELSE IF OBJCT.RECTYPE = ENDREC THEN (* ENDREC -- WRITE OUT THE LAST TEXT RECORD (IF THERE IS ANYTHING IN IT) AND THEN GENERATE THE END RECORD *) BEGIN IF TEXTLENGTH <> 0 THEN BEGIN WRITE(OBJFILE,'T'); NUMTOHEX(TEXTSTART,TEMP); WRITE(OBJFILE,'00',TEMP); TEXTBYTES := TEXTLENGTH DIV 2; NUMTOHEX(TEXTBYTES,TEMP); WRITE(OBJFILE,TEMP[3],TEMP[4]); FOR I := 1 TO TEXTLENGTH DO WRITE(OBJFILE,TEXTARRAY[I]); WRITELN(OBJFILE); END; WRITE(OBJFILE,'E'); FOR I := 1 TO OBJCT.OBJLENGTH DO WRITE(OBJFILE,OBJCT.OBJCODE[I]); WRITELN(OBJFILE); END; END; (* P2_WRITE_OBJ *) (***************************************************************) PROCEDURE PASS_1; (* THIS IS THE MAIN PROCEDURE FOR PASS 1. IT USES P1_READ_SOURCE TO READ EACH INPUT STATEMENT (UNTIL ENDOFINPUT = TRUE). FOR NON-COMMENT LINES, IT CALLS P1_ASSIGN_LOC AND P1_ASSIGN_SYM. FOR ALL SOURCE LINES, IT USES ACCESS_INT_FILE TO WRITE THE INTERMEDIATE FILE *) VAR ENDOFINPUT : BOOLEAN; I,ADDRESS : INTEGER; SYMTABRETURN : SYMRETTYPE; INTRETURN : INTRETTYPE; NEWLOCCTR : INTEGER; BEGIN (* INITIALIZATION *) ENDOFINPUT := FALSE; RESET(SRCFILE); REWRITE(INTFILE); LOCCTR := 0; (* END OF INITIALIZATION *) P1_READ_SOURCE (SOURCE, ENDOFINPUT, ERRORSFOUND, ERRORFLAGS); WHILE NOT ENDOFINPUT DO BEGIN IF SOURCE.COMLINE THEN NEWLOCCTR := LOCCTR ELSE BEGIN P1_ASSIGN_LOC (SOURCE, LOCCTR, NEWLOCCTR, ERRORSFOUND, ERRORFLAGS); IF SOURCE.OPERATION = 'START ' THEN BEGIN LOCCTR := NEWLOCCTR; PROGSTART := LOCCTR; FOR I := 1 TO 6 DO PROGNAME[I] := SOURCE.LABL[I]; END; P1_ASSIGN_SYM (SOURCE, LOCCTR, ERRORSFOUND, ERRORFLAGS); END; ACCESS_INT_FILE (WRITELINE, INTRETURN, SOURCE, ERRORSFOUND, ERRORFLAGS); LOCCTR := NEWLOCCTR; P1_READ_SOURCE (SOURCE, ENDOFINPUT, ERRORSFOUND, ERRORFLAGS); END; CLOSE(SRCFILE); END; (* PASS_1 *) (***************************************************************) PROCEDURE PASS_2; (* THIS IS THE MAIN PROCEDURE FOR PASS 2. IT READS EACH LINE FROM THE INTERMEDIATE FILE, AND CALLS P2_ASSEMBLE_INST AND P2_WRITE_OBJ FOR EACH NON-COMMENT LINE. HOWEVER, P2_WRITE_OBJ IS CALLED ONLY IF GENOBJECT = TRUE. GENOBJECT IS SET TO FALSE (TO SUPPRESS THE OBJECT PROGRAM) IF ANY ASSEMBLY ERRORS ARE DETECTED. P2_WRITE_LIST IS CALLED FOR EVERY LINE PROCESSED. *) VAR PROGLENGTH : INTEGER; GENOBJECT : BOOLEAN; INTRETURN : INTRETTYPE; BEGIN PROGLENGTH := LOCCTR - PROGSTART; GENOBJECT := TRUE; FIRSTSTMT := TRUE; ENDFOUND := FALSE; TEXTLENGTH := 0; RESET(INTFILE); REWRITE(LISFILE); REWRITE(OBJFILE); ACCESS_INT_FILE (READLINE, INTRETURN, SOURCE, ERRORSFOUND, ERRORFLAGS); WHILE INTRETURN <> ENDFILE DO BEGIN OBJCT.RECTYPE := NONE; OBJCT.OBJLENGTH := 0; OBJCT.OBJCODE := BLANK30; IF NOT SOURCE.COMLINE THEN BEGIN P2_ASSEMBLE_INST (SOURCE, LOCCTR, ERRORSFOUND, ERRORFLAGS, OBJCT); IF ERRORSFOUND THEN GENOBJECT := FALSE; IF GENOBJECT THEN P2_WRITE_OBJ (OBJCT, LOCCTR, PROGNAME, PROGLENGTH); END; P2_WRITE_LIST; ACCESS_INT_FILE (READLINE, INTRETURN, SOURCE, ERRORSFOUND, ERRORFLAGS); END; CLOSE(INTFILE); CLOSE(LISFILE); CLOSE(OBJFILE); END; (* PASS_2 *) (***************************************************************) BEGIN (* ASSEMBLER *) (* THIS IS THE MAIN PROCEDURE FOR THE ASSEMBLER. IT CONSISTS OF AN INITIALIZATION SECTION, FOLLOWED BY CALLS TO PASS_1 AND PASS_2 *) ASSIGN(SRCFILE,'SRCFILE'); ASSIGN(OBJFILE,'OBJFILE'); ASSIGN(LISFILE,'LISFILE'); ASSIGN(INTFILE,'INTFILE'); (* INITIALIZATION OF SYMBOL TABLE *) FOR I := 0 TO SYMTABLIMIT DO BEGIN SYMTAB[I].SYMBOL := BLANK8; SYMTAB[I].ADDRESS := 0; END; (* INITIALIZATION OF OPCODE TABLE. THE ENTRIES IN THE TABLE ARE FIRST INITIALIZED TO CONTAIN MNEMONICS AND MACHINE OPCODES. THESE ENTRIES ARE THEN SORTED (USING A SIMPLE BUBBLE SORT) TO BE SURE THEY ARE IN ORDER BY MNEMONIC. THIS SORT PROCESS IS NECESSARY BECAUSE DIFFERENT OF THE DIFFERENT PLACEMENT OF THE CHARACTER CODE FOR BLANK IN THE COLLATING SEQUENCE FOR DIFFERENT COMPUTERS *) OPTAB[ 1].MNEMONIC := 'ADD '; OPTAB[ 1].OPCODE := 24; OPTAB[ 2].MNEMONIC := 'AND '; OPTAB[ 2].OPCODE := 64; OPTAB[ 3].MNEMONIC := 'COMP '; OPTAB[ 3].OPCODE := 40; OPTAB[ 4].MNEMONIC := 'DIV '; OPTAB[ 4].OPCODE := 36; OPTAB[ 5].MNEMONIC := 'J '; OPTAB[ 5].OPCODE := 60; OPTAB[ 6].MNEMONIC := 'JEQ '; OPTAB[ 6].OPCODE := 48; OPTAB[ 7].MNEMONIC := 'JGT '; OPTAB[ 7].OPCODE := 52; OPTAB[ 8].MNEMONIC := 'JLT '; OPTAB[ 8].OPCODE := 56; OPTAB[ 9].MNEMONIC := 'JSUB '; OPTAB[ 9].OPCODE := 72; OPTAB[10].MNEMONIC := 'LDA '; OPTAB[10].OPCODE := 0; OPTAB[11].MNEMONIC := 'LDCH '; OPTAB[11].OPCODE := 80; OPTAB[12].MNEMONIC := 'LDL '; OPTAB[12].OPCODE := 8; OPTAB[13].MNEMONIC := 'LDX '; OPTAB[13].OPCODE := 4; OPTAB[14].MNEMONIC := 'MUL '; OPTAB[14].OPCODE := 32; OPTAB[15].MNEMONIC := 'OR '; OPTAB[15].OPCODE := 68; OPTAB[16].MNEMONIC := 'RD '; OPTAB[16].OPCODE := 216; OPTAB[17].MNEMONIC := 'RSUB '; OPTAB[17].OPCODE := 76; OPTAB[18].MNEMONIC := 'STA '; OPTAB[18].OPCODE := 12; OPTAB[19].MNEMONIC := 'STCH '; OPTAB[19].OPCODE := 84; OPTAB[20].MNEMONIC := 'STL '; OPTAB[20].OPCODE := 20; OPTAB[21].MNEMONIC := 'STX '; OPTAB[21].OPCODE := 16; OPTAB[22].MNEMONIC := 'SUB '; OPTAB[22].OPCODE := 28; OPTAB[23].MNEMONIC := 'TD '; OPTAB[23].OPCODE := 224; OPTAB[24].MNEMONIC := 'TIX '; OPTAB[24].OPCODE := 44; OPTAB[25].MNEMONIC := 'WD '; OPTAB[25].OPCODE := 220; SWITCHOP := TRUE; WHILE SWITCHOP DO BEGIN SWITCHOP := FALSE; FOR I := 1 TO MAXOPS-1 DO IF OPTAB[I].MNEMONIC > OPTAB[I+1].MNEMONIC THEN BEGIN SWITCHOP := TRUE; TEMPM := OPTAB[I+1].MNEMONIC; TEMPO := OPTAB[I+1].OPCODE; OPTAB[I+1].MNEMONIC := OPTAB[I].MNEMONIC; OPTAB[I+1].OPCODE := OPTAB[I].OPCODE; OPTAB[I].MNEMONIC := TEMPM; OPTAB[I].OPCODE := TEMPO; END; END; (* INITIALIZATION OF ERROR MESSAGES *) ERRMSG[ 1] := 'ILLEGAL FORMAT IN LABEL FIELD '; ERRMSG[ 2] := 'MISSING OPERATION CODE '; ERRMSG[ 3] := 'ILLEGAL FORMAT IN OPERATION FIELD '; ERRMSG[ 4] := 'MISSING OR MISPLACED OPERAND IN START STATEMENT '; ERRMSG[ 5] := 'ILLEGAL OPERAND IN START STATEMENT '; ERRMSG[ 6] := 'ILLEGAL OPERAND IN BYTE STATEMENT '; ERRMSG[ 7] := 'ODD LENGTH HEX STRING IN BYTE STATEMENT '; ERRMSG[ 8] := 'MISSING OR MISPLACED OPERAND IN BYTE STATEMENT '; ERRMSG[ 9] := 'MISSING OR MISPLACED OPERAND IN RESW STATEMENT '; ERRMSG[10] := 'ILLEGAL OPERAND IN RESW STATEMENT '; ERRMSG[11] := 'MISSING OR MISPLACED OPERAND IN RESB STATEMENT '; ERRMSG[12] := 'ILLEGAL OPERAND IN RESB STATEMENT '; ERRMSG[13] := 'DUPLICATE LABEL DEFINITION '; ERRMSG[14] := 'TOO MANY SYMBOLS IN SOURCE PROGRAM '; ERRMSG[15] := 'DUPLICATE OR MISPLACED START STATEMENT '; ERRMSG[16] := 'MISSING OR MISPLACED START STATEMENT '; ERRMSG[17] := 'ILLEGAL OPERAND IN WORD STATEMENT '; ERRMSG[18] := 'MISSING OR MISPLACED OPERAND IN WORD STATEMENT '; ERRMSG[19] := 'MISSING OR MISPLACED OPERAND IN END STATEMENT '; ERRMSG[20] := 'ILLEGAL OPERAND IN END STATEMENT '; ERRMSG[21] := 'UNDEFINED SYMBOL IN OPERAND '; ERRMSG[22] := 'STATEMENT SHOULD NOT FOLLOW END STATEMENT '; ERRMSG[23] := 'ILLEGAL OPERAND FIELD '; ERRMSG[24] := 'UNRECOGNIZED OPERATION CODE '; ERRMSG[25] := 'MISSING OR MISPLACED OPERAND IN INSTRUCTION '; (* INITIALIZATION OF ASCII CONVERSION TABLE *) FOR I := 0 TO 255 DO ASCII[I] := I; (* END INITIALIZATION *) PASS_1; PASS_2; END.