PROGRAM SICSIM(INPUT,OUTPUT,LOG,DEV00,DEV04,DEV05,DEV06,DEVF1,DEVF2,DEVF3); (* SIC SIMULATOR, VERSION 1.5 REVISED 10/27/89 THIS VERSION OF THE SIC SIMULATOR INCLUDES ALL SIC/XE INSTRUCTIONS AND CAPABILITIES EXCEPT FOR THE FOLLOWING: 1. FLOATING-POINT DATA TYPE AND INSTRUCTIONS (ADDF,COMPF,DIVF,FIX, FLOAT,LDF,MULF,NORM,STF,SUBF) 2. I/O CHANNELS AND ASSOCIATED INSTRUCTIONS (SIO,TIO,HIO) 3. INTERRUPTS AND ASSOCIATED INSTRUCTIONS (LPS,STI,SVC) 4. REGISTER SW AND ASSOCIATED FEATURES (USER/SUPERVISOR MODES, RUNNING/IDLE STATES) 5. VIRTUAL MEMORY AND ASSOCIATED INSTRUCTIONS (LPM) 6. MEMORY PROTECTION AND ASSOCIATED INSTRUCTIONS (SSK) FOR A SIMULATOR THAT SUPPORTS ONLY STANDARD SIC FEATURES, SET THE GLOBAL CONSTANT XE TO FALSE. THE SIMULATOR USES THE FOLLOWING EXTERNAL FILES: INPUT -- COMMANDS ENTERED FROM TERMINAL OUTPUT -- RESULTS DISPLAYED TO TERMINAL LOG -- LOG OF COMMANDS ENTERED AND RESULTS DISPLAYED (MAY BE PRINTED TO OBTAIN HARD COPY RECORD OF TERMINAL SESSION) DEV00 -- REPRESENTS DEVICE 00 (BOOTSTRAP OBJECT CODE) DEVF1, DEVF2, DEVF3 -- REPRESENT INPUT DEVICES F1, F2, F3 DEV04, DEV05, DEV06 -- REPRESENT OUTPUT DEVICES 04, 05, 06 *) CONST MSIZE = 12287; (*LARGEST MAIN MEMORY ADDRESS*) XE = TRUE; (*SIC/XE FEATURES SUPPORTED*) TYPE BYTE = 0..255; WORD = ARRAY[1..3] OF BYTE; HEX = ARRAY[1..6] OF CHAR; DEC = ARRAY[1..6] OF CHAR; ADDRESS = 0..MAXINT; MESSAGE = PACKED ARRAY [1..40] OF CHAR; VAR M : ARRAY[0..MSIZE] OF BYTE; (*MAIN MEMORY*) REGISTERS : ARRAY[0..5] OF WORD; (*REGISTERS A,X,L,B,S,T*) PC : ADDRESS; (*PROGRAM COUNTER*) CC : (LT,EQ,GT); (*CONDITION CODE*) DEV00,DEVF1,DEVF2,DEVF3: TEXT; (*INPUT DEVICES*) DEV04,DEV05,DEV06 : TEXT; (*OUTPUT DEVICES*) LOG : TEXT; (*COMMAND AND MESSAGE LOG*) ECHOING : BOOLEAN; (*'TRUE' MEANS COMMAND LINE BEING ECHOED TO LOG*) DEVCODE : INTEGER; (*DEVICE CODES 1-3 REPRESENT DEVICES F1-F3; CODES 4-6 REPRESENT DEVICES 04-06*) WAIT : ARRAY[1..6] OF INTEGER; (*WAIT[I] IS THE NUMBER OF TD INSTRUCTIONS REQUIRED UNTIL THE DEVICE WITH CODE I IS READY TO SEND OR RECEIVE*) INIT : ARRAY[1..6] OF BOOLEAN; (*INIT[I] TELLS WHETHER OR NOT THE FILE REPRESENTING THE DEVICE WITH CODE I HAS BEEN INITIALIZED*) ENDFILE : ARRAY[1..6] OF BOOLEAN; (*ENDFILE[I] TELLS WHETHER OR NOT THE DEVICE WITH CODE I IS AT END OF FILE*) HTEST : INTEGER; (*NUMBER OF INSTRUCTIONS TO EXECUTE BEFORE HALTING*) BREAKPT : ADDRESS; (*BREAKPOINT ADDRESS*) ERROR : BOOLEAN; (*EXECUTION ERROR FLAG*) WORD1 : WORD; (*WORD WITH VALUE 1*) INTAB,OUTTAB : ARRAY[0..255] OF 0..256; (*CHARACTER CONVERSION TABLES*) MSG : ARRAY[1..48] OF MESSAGE; (*OUTPUT MESSAGES FOR WRITEM*) I,J : INTEGER; COMMAND,NEXTCHAR : CHAR; FIRSTREAD,EOL,RETBLANK : BOOLEAN; (******************************************************************) FUNCTION CHRVAL (DIGIT:INTEGER) : CHAR; (* THIS FUNCTION RETURNS A CHARACTER WHICH REPRESENTS THE HEXADECIMAL DIGIT VALUE OF ITS ARGUMENT -- '0' THROUGH '9' FOR 0-9, 'A' THROUGH 'F' FOR 10-15. IT RETURNS '*' IF ITS INPUT IS NOT IN THE RANGE 0-15. *) BEGIN IF (DIGIT >= 0) AND (DIGIT <= 9) THEN CHRVAL := CHR(DIGIT + ORD('0')) ELSE IF (DIGIT >= 10) AND (DIGIT <= 15) THEN CHRVAL := CHR(DIGIT - 10 + ORD('A')) ELSE CHRVAL := '*'; END; (******************************************************************) FUNCTION HEXVAL (CH:CHAR) : INTEGER; (* THIS FUNCTION RETURNS AN INTEGER VALUE CORRESPONDING TO THE HEXADECIMAL DIGIT REPRESENTED BY ITS ARGUMENT -- 0 THROUGH 15 FOR '0' THROUGH 'F'. IT RETURNS THE VALUE -1 IF THE ARGUMENT IS NOT A LEGAL HEX DIGIT. *) BEGIN IF (CH >= '0') AND (CH <= '9') THEN HEXVAL := ORD(CH) - ORD('0') ELSE IF (CH >= 'A') AND (CH <= 'F') THEN HEXVAL := 10 + ORD(CH) - ORD('A') ELSE HEXVAL := -1; END; (******************************************************************) FUNCTION REGVAL (REG:CHAR) : INTEGER; (* THIS FUNCTION RETURNS THE REGISTER NUMBER CORRESPONDING TO THE REGISTER NAME REPRESENTED BY ITS ARGUMENT -- 0 FOR 'A', 1 FOR 'X', ETC. IT RETURNS THE VALUE -1 IF THE ARGUMENT IS NOT A VALID REGISTER NAME. *) BEGIN IF REG = 'A' THEN REGVAL := 0 ELSE IF REG = 'X' THEN REGVAL := 1 ELSE IF REG = 'L' THEN REGVAL := 2 ELSE IF REG = 'B' THEN REGVAL := 3 ELSE IF REG = 'S' THEN REGVAL := 4 ELSE IF REG = 'T' THEN REGVAL := 5 ELSE REGVAL := -1; END; (******************************************************************) PROCEDURE WRITEM (N : INTEGER); (* THIS PROCEDURE WRITES THE MESSAGE INDICATED BY THE VALUE OF ITS ARGUMENT TO THE OUTPUT AND LOG FILES. IF A COMMAND LINE WAS BEING ECHOED TO THE LOG, A NEW LINE IS STARTED BEFORE WRITING THE MESSAGE. IF THE MESSAGE BEING WRITTEN IS TERMINATED BY ';', A NEW LINE IS STARTED AFTER WRITING THE MESSAGE. IF THE MESSAGE IS TERMINATED BY '-', NO NEW LINE IS STARTED. *) VAR J : INTEGER; CH : CHAR; BEGIN IF ECHOING THEN BEGIN WRITELN(LOG); ECHOING := FALSE; END; IF N = 44 THEN WRITELN(LOG); (* SKIP LINE BEFORE PROMPT MESSAGE *) J := 1; CH := MSG[N,J]; WHILE (CH <> '-') AND (CH <> ';') DO BEGIN WRITE(CH); WRITE(LOG,CH); J := J + 1; CH := MSG[N,J]; END; (* WHILE *) IF CH = ';' THEN BEGIN WRITELN; WRITELN(LOG); END; END; (* WRITEM *) (******************************************************************) PROCEDURE WRITEC (CH : CHAR); (* THIS PROCEDURE WRITES THE CHARACTER WHICH IS ITS PARAMETER TO THE OUTPUT AND LOG FILES. IF A COMMAND LINE WAS BEING ECHOED TO THE LOG, A NEW LINE IS STARTED BEFORE WRITING THE CHARACTER. *) BEGIN IF ECHOING THEN BEGIN WRITELN(LOG); ECHOING := FALSE; END; WRITE(CH); WRITE(LOG,CH); END; (* WRITEC *) (******************************************************************) PROCEDURE READC (VAR CH : CHAR); (* THIS PROCEDURE READS A CHARACTER FROM THE INTERACTIVE COMMAND FILE. EACH CHARACTER READ IS ECHOED TO THE LOG FILE, AND THE VARIABLE ECHOING IS SET TO INDICATE THAT A COMMAND LINE IS BEING ECHOED. IF THE COMMAND FILE IS CURENTLY AT END-OF-LINE OR END-OF-FILE, THE GLOBAL VARIABLE EOL IS SET TO TRUE, AND A BLANK IS RETURNED; NO CHARACTER IS READ FROM THE FILE IN THIS CASE. AS LONG AS EOL REMAINS TRUE, SUCCEEDING READS WILL RETURN (ALTERNATELY) THE CHARACTERS BLANK AND '*', WITHOUT READING FROM THE FILE. EOL IS RESET TO FALSE WHEN THE PROCEDURE READL IS CALLED. THIS METHOD OF HANDLING END-OF-LINE IS INTENDED TO SIMPLIFY ERROR CHECKING IN THE COMMAND-PROCESSING PROCEDURES. *) BEGIN IF EOL THEN IF RETBLANK THEN BEGIN CH := ' '; RETBLANK := FALSE; END ELSE BEGIN CH := '*'; RETBLANK := TRUE; END ELSE BEGIN IF EOF(INPUT) THEN EOL := TRUE ELSE IF EOLN(INPUT) THEN EOL := TRUE; IF EOL THEN BEGIN CH := ' '; RETBLANK := FALSE; END ELSE BEGIN READ(CH); FIRSTREAD := FALSE; WRITE(LOG,CH); ECHOING := TRUE; END; END; END; (*READC*) (******************************************************************) PROCEDURE READL; (* THIS PROCEDURE IS CALLED TO BEGIN READING A NEW LINE OF THE COMMAND FILE. IT IS INCLUDED FOR CONSISTENCY, AND TO MAKE MODIFICATION OF THE PROGRAM FOR DIFFERENT TYPES OF INTERACTIVE INPUT EASIER. *) BEGIN READLN; EOL := FALSE; END; (*READL*) (******************************************************************) PROCEDURE HEXCONV (VAL:ADDRESS; VAR HEXCH:HEX); (* THIS PROCEDURE CONVERTS THE VALUE OF THE ARGUMENT VAL INTO A HEXADECIMAL CHARACTER REPRESENTATION, STORING IT IN HEXCH. *) VAR I,D : INTEGER; TEMP : ADDRESS; BEGIN TEMP := VAL; FOR I := 1 TO 6 DO BEGIN D := TEMP MOD 16; HEXCH[7-I] := CHRVAL(D); TEMP := TEMP DIV 16; END; END; (******************************************************************) PROCEDURE BYTECONV (BYTEVAL:BYTE; VAR HEXCH:HEX); (* THIS PROCEDURE CONVERTS THE VALUE OF BYTEVAL INTO ITS HEXADECIMAL CHARACTER REPRESENTATION, STORING THIS REPRESENTATION IN THE LAST TWO CHARACTERS OF HEXCH.*) BEGIN HEXCH[5] := CHRVAL(BYTEVAL DIV 16); HEXCH[6] := CHRVAL(BYTEVAL MOD 16); END; (******************************************************************) PROCEDURE DECCONV (VAL:ADDRESS; VAR DECCH:DEC); (* THIS PROCEDURE CONVERTS THE VALUE OF THE ARGUMENT VAL INTO A DECIMAL CHARACTER REPRESENTATION, STORING IT IN DECCH. *) VAR I,D : INTEGER; TEMP : ADDRESS; BEGIN TEMP := VAL; FOR I := 1 TO 6 DO BEGIN D := TEMP MOD 10; DECCH[7-I] := CHRVAL(D); TEMP := TEMP DIV 10; END; I := 1; WHILE (I <= 6) AND (DECCH[I] = '0') DO BEGIN DECCH[I] := ' '; I := I + 1; END; END; (******************************************************************) PROCEDURE WRITEPC; (* THIS PROCEDURE IS CALLED WHENEVER SIMULATED PROGRAM EXECUTION IS TERMINATED. IT CONVERTS THE CURRENT PROGRAM COUNTER VALUE TO CHARACTER FORM AND DISPLAYS THE RESULT. *) VAR I : INTEGER; HEXCH : HEX; BEGIN HEXCONV(PC,HEXCH); WRITEM(12) (*P = *); FOR I := 1 TO 6 DO WRITEC(HEXCH[I]); WRITEM(1) (*CR*); END; (******************************************************************) PROCEDURE SHIFT (VAR OP : WORD; N,STYPE : INTEGER); (*THIS PROCEDURE SHIFTS OP LEFT OR RIGHT N BIT POSITIONS. IF STYPE = 0, THE SHIFT IS LEFT CIRCULAR; IF STYPE = 1, THE SHIFT IS RIGHT WITH SIGN EXTENSION. *) VAR CARRY,TEMP,I,J : INTEGER; BEGIN IF STYPE = 0 THEN FOR I := 1 TO N DO BEGIN TEMP := 2 * OP[3]; OP[3] := TEMP MOD 256; CARRY := TEMP DIV 256; TEMP := 2 * OP[2] + CARRY; OP[2] := TEMP MOD 256; CARRY := TEMP DIV 256; TEMP := 2 * OP[1] + CARRY; OP[1] := TEMP MOD 256; CARRY := TEMP DIV 256; OP[3] := OP[3] + CARRY; END; IF STYPE = 1 THEN FOR I := 1 TO N DO BEGIN TEMP := OP[1]; OP[1] := TEMP DIV 2; CARRY := TEMP MOD 2; IF TEMP > 127 THEN OP[1] := OP[1] + 128; TEMP := OP[2]; OP[2] := TEMP DIV 2 + 128 * CARRY; CARRY := TEMP MOD 2; TEMP := OP[3]; OP[3] := TEMP DIV 2 + 128 * CARRY; CARRY := TEMP MOD 2; END; END; (*SHIFT*) (******************************************************************) (* THE FOLLOWING PROCEDURES -- NEGL, ADDL, SUBL, MULL, DIVL, COMPL -- PERFORM INTEGER ARITHMETIC OPERATIONS ON OPERANDS OF TYPE WORD. THEY ARE INCLUDED SO THAT THIS SIMULATOR CAN BE RUN ON MACHINES THAT CANNOT DIRECTLY REPRESENT 24-BIT INTEGERS. *) PROCEDURE NEGL (VAR OP : WORD); (*NEGATE*) VAR I : INTEGER; RES : WORD; BEGIN FOR I := 1 TO 3 DO RES[I] := 255 - OP[I]; IF RES[3] = 255 THEN BEGIN RES[3] := 0; IF RES[2] = 255 THEN BEGIN RES[2] := 0; IF RES[1] = 255 THEN RES[1] := 0 ELSE RES[1] := RES[1] + 1; END ELSE RES[2] := RES[2] + 1; END ELSE RES[3] := RES[3] + 1; FOR I := 1 TO 3 DO OP[I] := RES[I]; END; (*NEGL*) (**************************************) PROCEDURE ADDL(OP1,OP2 : WORD; VAR RESULT : WORD); (*ADD*) VAR I,TEMP,CARRY : INTEGER; RES : WORD; BEGIN TEMP := OP1[3] + OP2[3]; IF TEMP <= 255 THEN BEGIN RES[3] := TEMP; CARRY := 0; END ELSE BEGIN RES[3] := TEMP - 256; CARRY := 1; END; TEMP := OP1[2] + OP2[2] + CARRY; IF TEMP <= 255 THEN BEGIN RES[2] := TEMP; CARRY := 0; END ELSE BEGIN RES[2] := TEMP - 256; CARRY := 1; END; TEMP := OP1[1] + OP2[1] + CARRY; IF TEMP <= 255 THEN RES[1] := TEMP ELSE RES[1] := TEMP - 256; IF ((OP1[1] >= 128) AND (OP2[1] >= 128) AND (RES[1] < 128)) OR ((OP1[1] < 128) AND (OP2[1] < 128) AND (RES[1] >= 128)) THEN BEGIN WRITEM(4) (*ARITHMETIC OVERFLOW*); WRITEPC; ERROR := TRUE; END ELSE FOR I := 1 TO 3 DO RESULT[I] := RES[I]; END; (*ADDL*) (**************************************) PROCEDURE SUBL (OP1,OP2 : WORD; VAR RESULT : WORD); (*SUBTRACT*) VAR RES,TEMP2 : WORD; I : INTEGER; BEGIN FOR I := 1 TO 3 DO TEMP2[I] := OP2[I]; NEGL(TEMP2); ADDL(OP1,TEMP2,RES); FOR I := 1 TO 3 DO RESULT[I] := RES[I]; END; (*SUBL*) (**************************************) PROCEDURE MULL (OP1,OP2 : WORD; VAR RESULT : WORD); (*MULTIPLY*) VAR I : INTEGER; TEMP1,TEMP2 : WORD; BEGIN FOR I := 1 TO 3 DO RESULT[I] := 0; FOR I := 1 TO 3 DO BEGIN TEMP1[I] := OP1[I]; TEMP2[I] := OP2[I]; END; IF OP1[1] > 127 THEN NEGL(TEMP1); IF OP2[1] > 127 THEN NEGL(TEMP2); WHILE (TEMP2[1] <> 0) OR (TEMP2[2] <> 0) OR (TEMP2[3] <> 0) DO BEGIN IF ODD(TEMP2[3]) THEN ADDL(RESULT,TEMP1,RESULT); SHIFT(TEMP2,1,1); SHIFT(TEMP1,1,0); END; IF ((OP1[1] > 127) AND (OP2[1] < 128)) OR ((OP1[1] < 128) AND (OP2[1] > 127)) THEN NEGL(RESULT); END; (*MULL*) (**************************************) PROCEDURE DIVL(OP1,OP2 : WORD; VAR RESULT : WORD); (*DIVIDE*) VAR TEMP1,TEMP2,A : WORD; I,COUNT: INTEGER; BEGIN IF (OP2[1] = 0) AND (OP2[2] = 0) AND (OP2[3] = 0) THEN BEGIN WRITEM(5) (*DIVISION BY ZERO*); WRITEPC; ERROR := TRUE; END ELSE BEGIN FOR I := 1 TO 3 DO RESULT[I] := 0; FOR I := 1 TO 3 DO BEGIN TEMP1[I] := OP1[I]; TEMP2[I] := OP2[I]; A[I] := WORD1[I]; END; IF OP1[1] > 127 THEN NEGL(TEMP1); IF OP2[1] > 127 THEN NEGL(TEMP2); COUNT := 0; WHILE (TEMP2[1] <= TEMP1[1]) AND (TEMP2[1] < 64) DO BEGIN SHIFT(TEMP2,1,0); COUNT := COUNT + 1; END; SHIFT(A,COUNT,0); WHILE (TEMP2[1] <> 0) OR (TEMP2[2] <> 0) OR (TEMP2[3] <> 0) DO BEGIN SUBL(TEMP1,TEMP2,TEMP1); IF TEMP1[1] > 127 THEN ADDL(TEMP1,TEMP2,TEMP1) ELSE ADDL(RESULT,A,RESULT); SHIFT(A,1,1); SHIFT(TEMP2,1,1); END; IF ((OP1[1] > 127) AND (OP2[1] < 128)) OR ((OP1[1] < 128) AND (OP2[1] > 127)) THEN NEGL(RESULT); END; END; (*DIVL*) (**************************************) PROCEDURE COMPL (OP1,OP2 : WORD); (*COMPARE*) (* THIS PROCEDURE COMPARES THE VALUES OF OP1 AND OP2, AND SETS THE CONDITION CODE TO INDICATE THE RESULT. *) BEGIN IF (OP1[1] > 127) AND (OP2[1] < 128) THEN CC := LT ELSE IF (OP1[1] < 128) AND (OP2[1] > 127) THEN CC := GT ELSE IF (OP1[1] = OP2[1]) AND (OP1[2] = OP2[2]) AND (OP1[3] = OP2[3]) THEN CC := EQ ELSE IF (OP1[1] > OP2[1]) OR ((OP1[1] = OP2[1]) AND (OP1[2] > OP2[2])) OR ((OP1[1] = OP2[1]) AND (OP1[2] =OP2[2]) AND (OP1[3] > OP2[3])) THEN CC := GT ELSE CC := LT; END; (*COMPL*) (******************************************************************) PROCEDURE DUMP; (* THIS PROCEDURE DUMPS THE CONTENTS OF REGISTERS OR DESIGNATED MEMORY LOCATIONS. THE ALLOWABLE COMMAND FORMATS ARE DUMP R DUMP SSSS-EEEE DUMP R,SSSS-EEEE WHERE SSSS IS THE STARTING MEMORY ADDRESS OF AN AREA TO BE DUMPED (IN HEXADECIMAL), AND EEEE IS THE ENDING ADDRESS. THE LENGTH OF THE AREA TO BE DUMPED MAY BE FROM 1 TO 140 BYTES (HEXADECIMAL). IF 'R' IS SPECIFIED, THE CONTENTS OF REGISTERS A,X,L,B,S,T AND PC WILL BE DUMPED, ALONG WITH THE CURRENT VALUE OF THE CONDITION CODE CC. *) VAR CH : CHAR; HEXCH : HEX; DIGIT,I,J : INTEGER; BEGADDR,ENDADDR,A : INTEGER; REGDUMP,MEMDUMP,ERR1,ERR2,ERR3,ERR4 : BOOLEAN; (**************************************) PROCEDURE EXDUMP; (* THIS PROCEDURE PERFORMS THE ACTUAL DUMPING OPERATION; THE MAIN BODY OF 'DUMP' ANALYZES THE COMMAND PARAMETERS AND THEN CALLS 'EXDUMP'. *) VAR I,J,LIM : INTEGER; BEGIN IF REGDUMP THEN (*DUMP REGISTERS*) BEGIN IF XE THEN LIM := 3 ELSE LIM := 2; FOR I := 0 TO LIM DO BEGIN CASE I OF 0: WRITEM(6) (*A=*); 1: WRITEM(7) (*X=*); 2: WRITEM(8) (*L=*); 3: WRITEM(9) (*B=*); END; FOR J := 1 TO 3 DO BEGIN BYTECONV(REGISTERS[I,J],HEXCH); WRITEC(HEXCH[5]); WRITEC(HEXCH[6]); END; WRITEM(2) (* *); END; WRITEM(1); (* CR *) IF XE THEN FOR I := 4 TO 5 DO BEGIN IF I = 4 THEN WRITEM(10) (*S=*) ELSE WRITEM(11) (*T=*); FOR J := 1 TO 3 DO BEGIN BYTECONV(REGISTERS[I,J],HEXCH); WRITEC(HEXCH[5]); WRITEC(HEXCH[6]); END; WRITEM(2) (* *); END; WRITEM(12) (*P=*); HEXCONV(PC,HEXCH); FOR I := 1 TO 6 DO WRITEC(HEXCH[I]); WRITEM(2) (* *); CASE CC OF LT: WRITEM(13) (*CC=LT*); EQ: WRITEM(14) (*CC=EQ*); GT: WRITEM(15) (*CC=GT*); END; WRITEM(1); (* CR *) END; IF MEMDUMP THEN (*DUMP MEMORY*) BEGIN WRITEM(1); (* CR *) A := BEGADDR; WHILE A < ENDADDR DO BEGIN HEXCONV(A,HEXCH); FOR I := 3 TO 6 DO WRITEC(HEXCH[I]); FOR I := 0 TO 3 DO BEGIN WRITEM(3) (* *); FOR J := 0 TO 3 DO BEGIN BYTECONV(M[A+4*I+J],HEXCH); WRITEC(HEXCH[5]); WRITEC(HEXCH[6]); END; END; WRITEM(1); (* CR *) A := A + 16; END; (*WHILE*) END; (*IF MEMDUMP*) END; (*EXDUMP*) (**************************************) BEGIN (*DUMP*) ERR1 := FALSE; ERR2 := FALSE; ERR3 := FALSE; ERR4 := FALSE; REGDUMP := FALSE; MEMDUMP := FALSE; READC(CH); WHILE CH = ' ' DO READC(CH); IF CH = 'R' THEN (*CHECK FOR TYPE OF DUMP*) BEGIN REGDUMP := TRUE; READC(CH); IF CH = ',' THEN BEGIN READC(CH); MEMDUMP := TRUE; END; END ELSE MEMDUMP := TRUE; IF MEMDUMP THEN (*READ BEGINNING AND ENDING ADDRESSES*) BEGIN BEGADDR := 0; ENDADDR := 0; WHILE (CH <> ' ') AND (CH <> '-') DO BEGIN DIGIT := HEXVAL(CH); IF DIGIT >= 0 THEN BEGADDR := 16 * BEGADDR + DIGIT ELSE ERR1 := TRUE; READC(CH); END; IF CH = '-' THEN BEGIN READC(CH); WHILE CH <> ' ' DO BEGIN DIGIT := HEXVAL(CH); IF DIGIT >= 0 THEN ENDADDR := 16 * ENDADDR + DIGIT ELSE ERR2 := TRUE; READC(CH); END; END ELSE ERR3 := TRUE; IF (BEGADDR < 0) OR (ENDADDR > MSIZE) THEN ERR4 := TRUE; IF (ENDADDR < BEGADDR) OR (ENDADDR > (BEGADDR + 320)) THEN ERR4 := TRUE; BEGADDR := (BEGADDR DIV 16) * 16; ENDADDR := (ENDADDR DIV 16 + 1) * 16; END; IF ERR1 OR ERR2 OR ERR3 OR ERR4 THEN BEGIN IF ERR1 THEN WRITEM(16) (*INVALID STARTING ADDRESS*); IF ERR2 THEN WRITEM(17) (*INVALID ENDING ADDRESS*); IF ERR3 THEN WRITEM(18) (*NO ENDING ADDRESS SPECIFIED*); IF ERR4 THEN WRITEM(19) (*IMPROPER RANGE OF ADDRESSES*); END ELSE EXDUMP; END; (*DUMP*) (******************************************************************) PROCEDURE ENTER; (* THIS PROCEDURE ENTERS VALUES INTO REGISTERS OR MEMORY LOCATIONS. THE ALLOWABLE COMMAND FORMATS ARE ENTER RX VVVVVV ENTER MMM... VVVVVV... WHERE RX DESIGNATES A REGISTER (RA, RB, ETC.), MMM... IS A STARTING MEMORY ADDRESS IN HEXADECIMAL, AND VVVV... IS THE CONTENTS TO BE ENTERED INTO THE REGISTER OR MEMORY LOCATION (IN HEXADECIMAL). IF A REGISTER IS SPECIFIED, EXACTLY SIX HEXADECIMAL DIGITS MUST BE ENTERED. IF A MEMORY ADDRESS IS SPECIFIED, AN ARBITRARY NUMBER OF BYTES OF DATA MAY BE ENTERED (EACH BYTE REPRESENTED BY TWO HEX DIGITS); THESE VALUES ARE PLACED INTO CONSECUTIVE LOCATIONS IN MEMORY, BEGINNING WITH THE ADDRESS SPECIFIED. *) VAR CH,REGID : CHAR; REGENTER,ERR1,ERR2,ERR3,ERR4 : BOOLEAN; I,REGNO,DIGIT,DLEFT,DRIGHT : INTEGER; VAL,ADDR : ADDRESS; BEGIN ERR1 := FALSE; ERR2 := FALSE; ERR3 := FALSE; ERR4 := FALSE; READC(CH); WHILE CH = ' ' DO READC(CH); (*CHECK FOR TYPE OF ENTRY*) IF CH = 'R' THEN BEGIN REGENTER := TRUE; READC(REGID); REGNO := REGVAL(REGID); IF (REGNO < 0) OR ((REGNO > 2) AND NOT XE) THEN ERR1 := TRUE; READC(CH) END ELSE BEGIN (*READ STARTING ADDRESS*) ADDR := 0; REGENTER := FALSE; WHILE CH <> ' ' DO BEGIN DIGIT := HEXVAL(CH); IF DIGIT >= 0 THEN ADDR := 16 * ADDR + DIGIT ELSE ERR2 := TRUE; READC(CH); END; IF (ADDR < 0) OR (ADDR > MSIZE) THEN ERR2 := TRUE; END; WHILE CH = ' ' DO READC(CH); IF REGENTER THEN (*ENTRY INTO REGISTER*) BEGIN I := 1; VAL := 0; WHILE (CH <> ' ') AND (NOT ERR1) AND (NOT ERR3) DO BEGIN DLEFT := HEXVAL(CH); IF DLEFT < 0 THEN ERR3 := TRUE; READC(CH); DRIGHT := HEXVAL(CH); IF DRIGHT < 0 THEN ERR3 := TRUE; IF NOT ERR1 AND NOT ERR3 THEN REGISTERS[REGNO,I] := 16 * DLEFT + DRIGHT; READC(CH); I := I + 1; IF I > 4 THEN ERR3 := TRUE; END; IF CH <> ' ' THEN ERR3 := TRUE; END ELSE BEGIN (*ENTRY INTO MEMORY*) WHILE (CH <> ' ') AND (NOT ERR4) DO BEGIN DLEFT := HEXVAL(CH); IF DLEFT < 0 THEN ERR4 := TRUE; READC(CH); DRIGHT := HEXVAL(CH); IF DRIGHT < 0 THEN ERR4 := TRUE; IF ADDR > MSIZE THEN ERR2 := TRUE; IF NOT ERR2 AND NOT ERR4 THEN M[ADDR] := 16 * DLEFT + DRIGHT; ADDR := ADDR + 1; READC(CH); END; END; IF ERR1 THEN WRITEM(20) (*INVALID REGISTER NUMBER*); IF ERR2 THEN WRITEM(21) (*INVALID ADDRESS SPECIFIED*); IF ERR3 THEN WRITEM(22) (*INVALID REGISTER CONTENTS SPECIFIED*); IF ERR4 THEN WRITEM(23) (*INVALID MEMORY CONTENTS SPECIFIED*); END; (******************************************************************) PROCEDURE START; (* THIS PROCEDURE READS 128 BYTES OF DATA FROM DEV00 (THE BOOTSTRAP DEVICE) AND ENTERS IT INTO MEMORY BEGINNING AT ADDRESS 0. THE BOOTSTRAP DATA IS STORED ON THE FILE AS FOUR LINES OF 32 CHARACTERS EACH; EACH PAIR OF CHARACTERS GIVES THE HEXADECIMAL REPRESENTATION OF ONE BYTE OF DATA. IF THE BOOTSTRAP IS SHORTER THAN 128 BYTES, IT MUST BE EXTENDED TO 128 BYTES BY PADDING IT WITH LEGAL HEX CHARACTERS SUCH AS '0000...'. *) VAR I,K,R,L : INTEGER; CHL,CHR : CHAR; ERR1 : BOOLEAN; BEGIN ERR1 := FALSE; RESET(DEV00); FOR K := 0 TO 3 DO BEGIN FOR I := 0 TO 31 DO IF NOT ERR1 THEN BEGIN IF EOF(DEV00) THEN CHL := ' ' ELSE READ(DEV00,CHL); IF EOF(DEV00) THEN CHR := ' ' ELSE READ(DEV00,CHR); L := HEXVAL(CHL); R := HEXVAL(CHR); IF (L < 0) OR (R < 0) THEN BEGIN WRITEM(24) (*ILLEGAL BOOTSTRAP DATA*); ERR1 := TRUE; END ELSE M[32*K+I] := 16 * L + R; END; IF NOT EOF(DEV00) THEN READLN(DEV00); END; CLOSE(DEV00); END; (*START*) (******************************************************************) PROCEDURE BKPT; (* THIS PROCEDURE READS THE BREAKPOINT ADDRESS FROM THE COMMAND LINE, CONVERTS IT TO NUMERIC, AND STORES IT IN BREAKPT. *) VAR VAL : ADDRESS; DIGIT : INTEGER; CH : CHAR; ERR1 : BOOLEAN; BEGIN ERR1 := FALSE; READC(CH); WHILE CH = ' ' DO READC(CH); VAL := 0; WHILE CH <> ' ' DO BEGIN DIGIT := HEXVAL(CH); IF DIGIT >= 0 THEN VAL := 16 * VAL + DIGIT ELSE ERR1 := TRUE; READC(CH); END; IF VAL > MSIZE THEN ERR1 := TRUE; IF ERR1 THEN WRITEM(21) (*INVALID ADDRESS SPECIFIED*) ELSE BREAKPT := VAL; END; (******************************************************************) PROCEDURE HCOUNT; (* THIS PROCEDURE SETS THE COUNT OF INSTRUCTIONS TO BE EXECUTED BEFORE THE USER IS PROMPTED FOR A C(ONTINUE OR H(ALT DECISION. THIS VALUE IS READ FROM THE COMMAND LINE, CONVERTED TO NUMERIC, AND STORED IN HTEST. *) VAR I,VAL,DIGIT : INTEGER; CH : CHAR; ERR1 : BOOLEAN; BEGIN ERR1 := FALSE; READC(CH); WHILE CH = ' ' DO READC(CH); VAL := 0; I := 1; WHILE CH <> ' ' DO BEGIN DIGIT := HEXVAL(CH); IF (DIGIT >= 0) AND (DIGIT <= 9) THEN VAL := 10 * VAL + DIGIT ELSE ERR1 := TRUE; READC(CH); END; IF (ERR1) OR (VAL > 9999) THEN WRITEM(25) (*INVALID COUNT SPECIFIED*) ELSE HTEST := VAL; END; (******************************************************************) PROCEDURE RUN; (* THIS PROCEDURE CONTAINS THE MAIN LOOP FOR SIMULATING THE EXECUTION OF MACHINE INSTRUCTIONS. IT CALLS THE PROCEDURES 'FETCH' AND 'EXEC' TO FETCH AND EXECUTE EACH INSTRUCTION IN TURN; IT ALSO CHECKS FOR BREAKPOINTS AND INSTRUCTION COUNTS, ISSUING APPROPRIATE MESSAGES TO THE USER. *) VAR I,ICOUNT,DIGIT,TEMPPC : INTEGER; CH,CH2 : CHAR; DECCH : DEC; ERR1,RUNNING,HALT,BREAK : BOOLEAN; OPCODE,REG1,REG2 : INTEGER; (*CURRENT INSTRUCTION*) DISP,PCWORD,TARGADDR,ADDR : WORD; INDIR,IMMED,INDEX,BREL,PCREL,SICSTD : BOOLEAN; FMT1,FMT2,FMT3,FMT4 : BOOLEAN; (**************************************) PROCEDURE FETCH; (* THIS PROCEDURE FETCHES THE NEXT MACHINE INSTRUCTION FROM THE LOCATION INDICATED BY PC, DECODES THE INSTRUCTION, AND ADVANCES PC TO THE NEXT INSTRUCTION. IF AN ERROR IS DETECTED, IT SETS 'ERROR' TO TRUE. WHEN AN INSTRUCTION IS FETCHED FOR EXECUTION, THE FOLLOWING VARIABLES ARE SET. THE VALUES OF THESE VARIABLES ARE USED IN EXECUTING THE INSTRUCTION. OPCODE -- MACHINE OPERATION CODE FMT1,FMT2,FMT3,FMT4 -- INDICATE INSTRUCTION FORMAT REG1,REG2 -- REGISTERS SPECIFIED (FORMAT 2) DISP -- DISPLACEMENT (FORMAT 3) INDIR,IMMED,INDEX,BREL,PCREL,SICSTD -- INDICATE ADDRESSING MODE (FORMAT 3 AND 4) INTERNAL PROCEDURE DECMODE IS CALLED TO DECODE AND VALIDATE THE ADDRESSING MODE BITS IN A FORMAT 3 OR 4 INSTRUCTION. INTERNAL PROCEDURE DECADDR IS CALLED TO DECODE THE DISPLACEMENT FIELD IN A FORMAT 3 INSTRUCTION OR THE ADDRESS FIELD IN A FORMAT 4 INSTRUCTION AND CONVERT THESE VALUES TO NUMERIC. *) VAR I,FLAGS,MODES : INTEGER; HEXCH : HEX; ERR1,ERR2,ERR3 : BOOLEAN; PCTEMP,NEWPC : ADDRESS; (********************) PROCEDURE DECMODE; (* THIS PROCEDURE DECODES AND VALIDATES THE ADDRESSING MODE BITS FOR A FORMAT 3 OR 4 INSTRUCTION. IT SETS THE VARIABLES INDIR, IMMED, INDEX, BREL, PCREL, AND SICSTD TO INDICATE THE PROPER ADDRESSING MODE. *) BEGIN FLAGS := M[PC] MOD 4; CASE FLAGS OF 0: BEGIN INDIR := FALSE; IMMED := FALSE; SICSTD := TRUE; END; 1: BEGIN INDIR := FALSE; IMMED := TRUE; SICSTD := FALSE; END; 2: BEGIN INDIR := TRUE; IMMED := FALSE; SICSTD := FALSE; END; 3: BEGIN INDIR := FALSE; IMMED := FALSE; SICSTD := FALSE; END; END; MODES := M[PC+1] DIV 32; IF FMT3 THEN IF SICSTD THEN ERR3 := FALSE ELSE IF (NOT INDIR) AND (NOT IMMED) THEN IF (MODES = 3) OR (MODES = 7) THEN ERR3 := TRUE ELSE ERR3 := FALSE ELSE IF MODES > 2 THEN ERR3 := TRUE ELSE ERR3 := FALSE; IF FMT4 THEN IF (NOT INDIR) AND (NOT IMMED) AND (NOT SICSTD) THEN IF (MODES = 0) OR (MODES = 4) THEN ERR3 := FALSE ELSE ERR3 := TRUE ELSE IF (MODES = 0) THEN ERR3 := FALSE ELSE ERR3 := TRUE; IF MODES > 3 THEN INDEX := TRUE ELSE INDEX := FALSE; IF SICSTD THEN BEGIN BREL := FALSE; PCREL := FALSE; END ELSE BEGIN IF MODES MOD 2 = 1 THEN PCREL := TRUE ELSE PCREL := FALSE; IF MODES MOD 4 > 1 THEN BREL := TRUE ELSE BREL := FALSE; END; END; (*DECMODE*) (********************) PROCEDURE DECADDR; (* THIS PROCEDURE DECODES THE 'DISP' AND 'ADDR' FIELDS IN FORMAT 3 AND FORMAT 4 INSTRUCTIONS. IT SETS THE VARIABLE DISP TO INDICATE THE DISPLACEMENT SPECIFIED (FORMAT 3), AND TARGADDR TO INDICATE THE TARGET ADDRESS SPECIFIED (FORMAT 3 OR 4). *) VAR I : INTEGER; BEGIN IF FMT3 THEN BEGIN (*DECODE DISP*) IF SICSTD THEN BEGIN DISP[1] := 0; DISP[2] := M[PC+1] MOD 128; DISP[3] := M[PC+2]; END ELSE BEGIN DISP[1] := 0; DISP[2] := M[PC+1] MOD 16; DISP[3] := M[PC+2]; IF PCREL OR IMMED THEN IF DISP[2] > 7 THEN BEGIN DISP[2] := DISP[2] + 240; DISP[1] := 255; END; END; IF SICSTD THEN FOR I := 1 TO 3 DO TARGADDR[I] := DISP[I] ELSE BEGIN IF BREL THEN ADDL(REGISTERS[3],DISP,TARGADDR) ELSE IF PCREL THEN BEGIN PCTEMP := NEWPC; FOR I := 1 TO 3 DO BEGIN PCWORD[4-I] := PCTEMP MOD 256; PCTEMP := PCTEMP DIV 256; END; ADDL(PCWORD,DISP,TARGADDR); END ELSE FOR I := 1 TO 3 DO TARGADDR[I] := DISP[I]; END; END; IF FMT4 THEN BEGIN (*DECODE ADDR*) TARGADDR[1] := M[PC+1] MOD 16; TARGADDR[2] := M[PC+2]; TARGADDR[3] := M[PC+3]; END; IF INDEX THEN ADDL(TARGADDR,REGISTERS[1],TARGADDR); END; (*DECADDR*) (********************) BEGIN (*FETCH*) FMT1 := FALSE; FMT2 := FALSE; FMT3 := FALSE; FMT4 := FALSE; ERR1 := FALSE; ERR2 := FALSE; ERR3 := FALSE; (*CHECK FOR VALID OPCODE*) OPCODE := (M[PC] DIV 4) * 4; IF ((OPCODE >= 88) AND (OPCODE <= 100)) OR (OPCODE = 112) OR (OPCODE = 128) OR (OPCODE = 136) OR (OPCODE = 176) OR ((OPCODE >= 192) AND (OPCODE <= 200)) OR (OPCODE = 208) OR (OPCODE = 212) OR ((OPCODE >= 228) AND (OPCODE <= 248)) THEN ERR1 := TRUE; IF (OPCODE = 140) OR (OPCODE = 188) OR (OPCODE = 204) OR (OPCODE = 252) THEN ERR2 := TRUE; (*DETERMINE INSTRUCTION FORMAT*) IF (OPCODE <= 140) OR ((OPCODE >= 208) AND (OPCODE <= 236)) THEN FMT3 := TRUE ELSE IF OPCODE <= 188 THEN FMT2 := TRUE ELSE FMT1 := TRUE; IF (FMT3) AND ((M[PC+1] DIV 16) MOD 2 = 1) AND (M[PC] MOD 4 <> 0) THEN BEGIN FMT3 := FALSE; FMT4 := TRUE; END; IF FMT1 THEN NEWPC := PC + 1 ELSE IF FMT2 THEN NEWPC := PC + 2 ELSE IF FMT3 THEN NEWPC := PC + 3 ELSE NEWPC := PC + 4; IF NEWPC > (MSIZE - 2) THEN BEGIN WRITEM(29); (* ADDRESS OUT OF RANGE *) WRITEPC; ERROR := TRUE; END; IF FMT2 AND NOT ERROR THEN (*DECODE REGISTER NUMBERS*) BEGIN REG1 := M[PC+1] DIV 16; REG2 := M[PC+1] MOD 16; END; IF (FMT3 OR FMT4) AND (NOT ERROR) THEN BEGIN DECMODE; DECADDR; END; IF NOT XE AND NOT ERR2 THEN BEGIN IF NOT FMT3 THEN ERR1 := TRUE; IF FMT3 AND NOT SICSTD THEN ERR3 := TRUE; END; IF ERR1 OR ERR2 OR ERR3 THEN BEGIN IF ERR1 THEN WRITEM(26) (*UNSUPPORTED MACHINE INSTRUCTION*); IF ERR2 THEN WRITEM(27) (*ILLEGAL MACHINE INSTRUCTION*); IF ERR3 THEN WRITEM(28) (*ILLEGAL ADDRESSING MODE*); WRITEPC; ERROR := TRUE; END ELSE PC := NEWPC; END; (*FETCH*) (**************************************) PROCEDURE EXEC; (* THIS PROCEDURE SIMULATES THE EXECUTION OF THE MACHINE INSTRUCTION THAT WAS DECODED BY THE PROCEDURE 'FETCH'. IT CALLS AN INTERNAL PROCEDURE, DEPENDING UPON THE VALUE OF OPCODE, TO EXECUTE THE INSTRUCTION. *) VAR I,REGNO : INTEGER; DATA : WORD; OPADDR : ADDRESS; (********************) PROCEDURE GETADDR; (* THIS PROCEDURE GETS THE MAIN MEMORY ADDRESS TO BE USED FOR INSTRUCTION EXECUTION, INCLUDING INDIRECTION IF APPLICABLE, PLACING IT IN 'OPADDR'*) VAR I : INTEGER; TEMP : ADDRESS; BEGIN OPADDR := 0; I := 1; WHILE (NOT ERROR) AND (I <= 3) DO IF MSIZE < 256 * OPADDR + TARGADDR[I] THEN BEGIN WRITEM(29) (*ADDRESS OUT OF RANGE*); WRITEPC; ERROR := TRUE; END ELSE BEGIN OPADDR := 256 * OPADDR + TARGADDR[I]; I := I + 1; END; IF INDIR AND NOT ERROR THEN BEGIN TEMP := 0; I := 0; WHILE (NOT ERROR) AND (I <= 2) DO IF MSIZE < 256 * TEMP + M[OPADDR+I] THEN BEGIN WRITEM(29) (*ADDRESS OUT OF RANGE*); WRITEPC; ERROR := TRUE; END ELSE BEGIN TEMP := 256 * TEMP + M[OPADDR+I]; I := I + 1; END; IF NOT ERROR THEN OPADDR := TEMP; END; IF (OPADDR > (MSIZE - 2)) AND NOT ((OPCODE = 80 (*LDCH*)) OR (OPCODE = 84 (*STCH*)) OR (OPCODE = 216 (*RD*)) OR (OPCODE = 220 (*WD*)) OR (OPCODE = 224 (*TD*))) THEN BEGIN WRITEM(29) (*ADDRESS OUT OF RANGE*); WRITEPC; ERROR := TRUE; END; END (*GETADDR*); (********************) PROCEDURE GETDATA; (*THIS PROCEDURE FETCHES AN OPERAND FROM MEMORY ADDRESS OPADDR, PLACING IT IN 'DATA'. IF THE INSTRUCTION SPECIFIED IMMEDIATE ADDRESSING, THE OPERAND VALUE IS OBTAINED FROM THE INSTRUCTION (TARGADDR) INSTEAD OF FROM MEMORY. *) VAR I : INTEGER; BEGIN IF IMMED THEN FOR I := 1 TO 3 DO DATA[I] := TARGADDR[I] ELSE BEGIN GETADDR; IF ERROR THEN FOR I := 1 TO 3 DO DATA[I] := 0 ELSE FOR I := 1 TO 3 DO DATA[I] := M[OPADDR+I-1]; END; END (*GETDATA*); (********************) PROCEDURE LOAD; (*LDA,LDX,LDL,LDCH,LDB,LDS,LDT*) VAR I : INTEGER; BEGIN GETDATA; CASE OPCODE OF 0 (*LDA*) : REGNO := 0; 4 (*LDX*) : REGNO := 1; 8 (*LDL*) : REGNO := 2; 80 (*LDCH*) : ; 104 (*LDB*) : REGNO := 3; 108 (*LDS*) : REGNO := 4; 116 (*LDT*) : REGNO := 5; END; IF OPCODE = 80 (*LDCH*) THEN IF IMMED THEN REGISTERS[0,3] := DATA[3] ELSE REGISTERS[0,3] := DATA[1] ELSE FOR I := 1 TO 3 DO REGISTERS[REGNO,I] := DATA[I]; END (*LOAD*); (********************) PROCEDURE STORE; (*STA,STX,STL,STCH,STB,STS,STT*) VAR I : INTEGER; BEGIN IF IMMED THEN BEGIN WRITEM(30) (*STORE IMMEDIATE NOT ALLOWED*); WRITEPC; ERROR := TRUE; END ELSE BEGIN GETADDR; CASE OPCODE OF 12 (*STA*) : REGNO := 0; 16 (*STX*) : REGNO := 1; 20 (*STL*) : REGNO := 2; 84 (*STCH*) : ; 120 (*STB*) : REGNO := 3; 124 (*STS*) : REGNO := 4; 132 (*STT*) : REGNO := 5; END; IF OPCODE = 84 (*STCH*) THEN M[OPADDR] := REGISTERS[0,3] ELSE FOR I := 1 TO 3 DO M[OPADDR+I-1] := REGISTERS[REGNO,I]; END; END (*STORE*); (********************) PROCEDURE JUMP; (*JEQ,JGT,JLT,J,JSUB,RSUB*) VAR I : INTEGER; TEMPPC : ADDRESS; JUMPC: BOOLEAN; BEGIN IF IMMED THEN BEGIN WRITEM(31) (*JUMP IMMEDIATE NOT ALLOWED*); WRITEPC; ERROR := TRUE; END ELSE BEGIN IF OPCODE = 76 (*RSUB*) THEN BEGIN TEMPPC := 0; I := 1; WHILE (NOT ERROR) AND (I <= 3) DO IF MSIZE < 256 * TEMPPC + REGISTERS[2,I] THEN BEGIN WRITEM(29) (*ADDRESS OUT OF RANGE*); WRITEPC; ERROR := TRUE; END ELSE BEGIN TEMPPC := 256 * TEMPPC + REGISTERS[2,I]; I := I + 1; END; IF NOT ERROR THEN PC := TEMPPC; END ELSE BEGIN JUMPC := FALSE; CASE OPCODE OF 48 (*JEQ*) : IF CC = EQ THEN JUMPC := TRUE; 52 (*JGT*) : IF CC = GT THEN JUMPC := TRUE; 56 (*JLT*) : IF CC = LT THEN JUMPC := TRUE; 60 (*J*) : JUMPC := TRUE; 72 (*JSUB*): JUMPC := TRUE; END; (*CASE*) IF JUMPC THEN GETADDR; IF OPCODE = 72 (*JSUB*) THEN BEGIN REGISTERS[2,3] := PC MOD 256; PC := PC DIV 256; REGISTERS[2,2] := PC MOD 256; REGISTERS[2,1] := PC DIV 256; END; IF JUMPC AND NOT ERROR THEN PC := OPADDR; END; END; END (*JUMP*); (********************) PROCEDURE ARITH; (*ADD,SUB,MUL,DIV,COMP,TIX*) VAR I : INTEGER; RESULT : WORD; BEGIN GETDATA; CASE OPCODE OF 24 (*ADD*) : ADDL(REGISTERS[0],DATA,REGISTERS[0]); 28 (*SUB*) : SUBL(REGISTERS[0],DATA,REGISTERS[0]); 32 (*MUL*) : MULL(REGISTERS[0],DATA,REGISTERS[0]); 36 (*DIV*) : DIVL(REGISTERS[0],DATA,REGISTERS[0]); 40 (*COMP*): COMPL(REGISTERS[0],DATA); 44 (*TIX*) : BEGIN ADDL(REGISTERS[1],WORD1,REGISTERS[1]); COMPL(REGISTERS[1],DATA); END; END; (*CASE*) END (*ARITH*); (********************) PROCEDURE LOGIC; (*AND,OR*) VAR I : INTEGER; TEMP : WORD; BEGIN GETDATA; FOR I := 1 TO 3 DO BEGIN TEMP[I] := REGISTERS[0,I]; REGISTERS[0,I] := 0; END; FOR I := 1 TO 24 DO BEGIN IF (OPCODE = 64 (*AND*) ) AND ((ODD(TEMP[1])) AND (ODD(DATA[1]))) THEN REGISTERS[0,1] := REGISTERS[0,1] + 1; IF (OPCODE = 68 (*OR*) ) AND ((ODD(TEMP[1])) OR (ODD(DATA[1]))) THEN REGISTERS[0,1] := REGISTERS[0,1] + 1; SHIFT(TEMP,1,0); SHIFT(DATA,1,0); SHIFT(REGISTERS[0],1,0); END; END (*LOGIC*); (********************) PROCEDURE CHARIO; (*RD,WD,TD*) VAR C : CHAR; BEGIN GETDATA; IF IMMED THEN DATA[1] := DATA[3]; IF OPCODE = 224 (*TD*) THEN BEGIN IF DATA[1] > 240 THEN DEVCODE := DATA[1] - 240 ELSE DEVCODE := DATA[1]; IF (DEVCODE > 0) AND (DEVCODE < 7) THEN IF WAIT[DEVCODE] = 0 THEN BEGIN CC := LT; WAIT[DEVCODE] := DEVCODE MOD 4 + 2; END ELSE BEGIN CC := EQ; WAIT[DEVCODE] := WAIT[DEVCODE] - 1; END ELSE BEGIN WRITEM(32) (*UNSUPPORTED I/O DEVICE*); WRITEPC; ERROR := TRUE; END; END; IF OPCODE = 216 (*RD*) THEN BEGIN IF (DATA[1] < 241) OR (DATA[1] > 243) THEN BEGIN WRITEM(33) (*UNSUPPORTED INPUT DEVICE*); WRITEPC; ERROR := TRUE; END ELSE BEGIN DEVCODE := DATA[1] - 240; IF WAIT[DEVCODE] <> (DEVCODE MOD 4 + 2) THEN BEGIN WRITEM(34) (*INPUT DEVICE NOT READY*); WRITEPC; ERROR := TRUE; END ELSE WAIT[DEVCODE] := WAIT[DEVCODE] - 1; END; IF NOT ERROR THEN IF DEVCODE = 1 THEN BEGIN IF NOT INIT[1] THEN BEGIN RESET(DEVF1); INIT[1] := TRUE; END; IF ENDFILE[1] THEN BEGIN WRITEM(35) (*ATTEMPT TO READ DEVF1 PAST END OF FILE*); WRITEPC; ERROR := TRUE; END ELSE IF EOF(DEVF1) THEN BEGIN ENDFILE[1] := TRUE; REGISTERS[0,3] := 4; END ELSE IF EOLN(DEVF1) THEN BEGIN REGISTERS[0,3] := 0; READLN(DEVF1); END ELSE BEGIN READ(DEVF1,C); REGISTERS[0,3] := INTAB[ORD(C)]; END; END ELSE IF DEVCODE = 2 THEN BEGIN IF NOT INIT[2] THEN BEGIN RESET(DEVF2); INIT[2] := TRUE; END; IF ENDFILE[2] THEN BEGIN WRITEM(36) (*ATTEMPT TO READ DEVF2 PAST END OF FILE*); WRITEPC; ERROR := TRUE; END ELSE IF EOF(DEVF2) THEN BEGIN ENDFILE[2] := TRUE; REGISTERS[0,3] := 4; END ELSE IF EOLN(DEVF2) THEN BEGIN REGISTERS[0,3] := 0; READLN(DEVF2); END ELSE BEGIN READ(DEVF2,C); REGISTERS[0,3] := INTAB[ORD(C)]; END; END ELSE IF DEVCODE = 3 THEN BEGIN IF NOT INIT[3] THEN BEGIN RESET(DEVF3); INIT[3] := TRUE; END; IF ENDFILE[3] THEN BEGIN WRITEM(37) (*ATTEMPT TO READ DEVF3 PAST END OF FILE*); WRITEPC; ERROR := TRUE; END ELSE IF EOF(DEVF3) THEN BEGIN ENDFILE[3] := TRUE; REGISTERS[0,3] := 4; END ELSE IF EOLN(DEVF3) THEN BEGIN REGISTERS[0,3] := 0; READLN(DEVF3); END ELSE BEGIN READ(DEVF3,C); REGISTERS[0,3] := INTAB[ORD(C)]; END; END; END; IF OPCODE = 220 (*WD*) THEN BEGIN IF (DATA[1] < 4) OR (DATA[1] > 6) THEN BEGIN WRITEM(38) (*UNSUPPORTED OUTPUT DEVICE*); WRITEPC; ERROR := TRUE; END ELSE BEGIN DEVCODE := DATA[1]; IF WAIT[DEVCODE] <> (DEVCODE MOD 4 + 2) THEN BEGIN WRITEM(39) (*OUTPUT DEVICE NOT READY*); WRITEPC; ERROR := TRUE; END ELSE WAIT[DEVCODE] := WAIT[DEVCODE] - 1; END; IF NOT ERROR THEN IF DEVCODE = 4 THEN BEGIN IF NOT INIT[4] THEN BEGIN REWRITE(DEV04); INIT[4] := TRUE; END; IF REGISTERS[0,3] = 0 THEN WRITELN(DEV04) ELSE WRITE(DEV04,CHR(OUTTAB[REGISTERS[0,3]])); END ELSE IF DEVCODE = 5 THEN BEGIN IF NOT INIT[5] THEN BEGIN REWRITE(DEV05); INIT[5] := TRUE; END; IF REGISTERS[0,3] = 0 THEN WRITELN(DEV05) ELSE WRITE(DEV05,CHR(OUTTAB[REGISTERS[0,3]])); END ELSE IF DEVCODE = 6 THEN BEGIN IF NOT INIT[6] THEN BEGIN REWRITE(DEV06); INIT[6] := TRUE; END; IF REGISTERS[0,3] = 0 THEN WRITELN(DEV06) ELSE WRITE(DEV06,CHR(OUTTAB[REGISTERS[0,3]])); END; END; END; (*CHARIO*) (********************) PROCEDURE REGREG; (*ADDR,SUBR,MULR,DIVR,COMPR,TIXR*) BEGIN IF (REG1 > 5) OR ((REG2 > 5) AND (OPCODE <> 184 (*TIXR*) )) THEN BEGIN WRITEM(47) (*ILLEGAL REGISTER NUMBER*); WRITEPC; ERROR := TRUE; END ELSE CASE OPCODE OF 144: ADDL(REGISTERS[REG2],REGISTERS[REG1],REGISTERS[REG2]); 148: SUBL(REGISTERS[REG2],REGISTERS[REG1],REGISTERS[REG2]); 152: MULL(REGISTERS[REG2],REGISTERS[REG1],REGISTERS[REG2]); 156: DIVL(REGISTERS[REG2],REGISTERS[REG1],REGISTERS[REG2]); 160: COMPL(REGISTERS[REG1],REGISTERS[REG2]); 184: (*TIXR*) BEGIN ADDL(REGISTERS[1],WORD1,REGISTERS[1]); COMPL(REGISTERS[1],REGISTERS[REG1]); END; END; (*CASE*) END (*REGREG*); (********************) PROCEDURE REGMAN; (*SHIFTL,SHIFTR,RMO,CLEAR*) VAR I,STYPE : INTEGER; BEGIN IF (REG1 > 5) OR ((OPCODE = 172 (*RMO*) ) AND (REG2 > 5)) THEN BEGIN WRITEM(47) (*ILLEGAL REGISTER NUMBER*); WRITEPC; ERROR := TRUE; END ELSE IF OPCODE = 180 THEN (*CLEAR*) FOR I := 1 TO 3 DO REGISTERS[REG1,I] := 0 ELSE IF OPCODE = 172 THEN (*RMO*) FOR I := 1 TO 3 DO REGISTERS[REG2,I] := REGISTERS[REG1,I] ELSE (*SHIFTL,SHIFTR*) BEGIN IF OPCODE = 164 THEN STYPE := 0 ELSE STYPE := 1; SHIFT(REGISTERS[REG1],REG2+1,STYPE); END; END (*REGMAN*); (********************) BEGIN (*EXEC*) CASE OPCODE OF 0,4,8,80,104,108,116: (*LDA,LDX,LDL,LDCH,LDB,LDS,LDT*) LOAD; 12,16,20,84,120,124,132: (*STA,STX,STL,STCH,STB,STS,STT*) STORE; 48,52,56,60,72,76: (*JEQ,JGT,JLT,J,JSUB,RSUB*) JUMP; 24,28,32,36,40,44: (*ADD,SUB,MUL,DIV,COMP,TIX*) ARITH; 64,68: (*AND,OR*) LOGIC; 216,220,224: (*RD,WD,TD*) CHARIO; 144,148,152,156,160,184: (*ADDR,SUBR,MULR,DIVR,COMPR,TIXR*) REGREG; 164,168,172,180: (*SHIFTL,SHIFTR,RMO,CLEAR*) REGMAN; END; (*CASE*) END; (*EXEC*) (**************************************) BEGIN (*RUN*) RUNNING := TRUE; ERROR := FALSE; HALT := FALSE; BREAK := FALSE; ICOUNT := 0; ERR1 := FALSE; READC(CH); WHILE CH = ' ' DO READC(CH); IF NOT EOL THEN BEGIN TEMPPC := 0; WHILE CH <> ' ' DO BEGIN DIGIT := HEXVAL(CH); IF DIGIT >= 0 THEN TEMPPC := 16 * TEMPPC + DIGIT ELSE ERR1 := TRUE; READC(CH); END; IF TEMPPC > MSIZE THEN ERR1 := TRUE; IF ERR1 THEN BEGIN WRITEM(21) (*INVALID ADDRESS SPECIFIED*); RUNNING := FALSE; END ELSE PC := TEMPPC; END; WHILE RUNNING AND NOT ERROR DO BEGIN FETCH; IF NOT ERROR THEN EXEC; ICOUNT := ICOUNT + 1; IF ICOUNT = HTEST THEN BEGIN HALT := TRUE; ICOUNT := 0; DECCONV(HTEST,DECCH); FOR I := 1 TO 6 DO WRITEC(DECCH[I]); WRITEM(3) (* *); WRITEM(40) (* INSTRUCTIONS EXECUTED*); END; IF PC = BREAKPT THEN BEGIN BREAK := TRUE; WRITEM(41) (*BREAKPOINT REACHED*); END; IF (NOT ERROR) AND (HALT OR BREAK) THEN BEGIN WRITEPC; HALT := FALSE; BREAK := FALSE; RUNNING := FALSE; END; END; END; (******************************************************************) PROCEDURE INITIALIZE; (* THIS PROCEDURE IS CALLED AT THE BEGINNING OF THE SIMULATION TO SET UP INITIAL VALUES. INTAB AND OUTTAB ARE SET TO REFLECT THE CHARACTER COLLATING SEQUENCE OF THE HOST MACHINE (SEE NOTES ON INSTALLING THE SIMULATOR). *) VAR I,J,DEVCODE : INTEGER; BEGIN ASSIGN(LOG,'LOG'); REWRITE(LOG); ASSIGN(DEV00,'DEV00'); ASSIGN(DEV04,'DEV04'); ASSIGN(DEV05,'DEV05'); ASSIGN(DEV06,'DEV06'); ASSIGN(DEVF1,'DEVF1'); ASSIGN(DEVF2,'DEVF2'); ASSIGN(DEVF3,'DEVF3'); FOR I := 0 TO 255 DO INTAB[I] := 256; FOR I := 0 TO 255 DO INTAB[I] := I; (* +++++INITIALIZATION FOR NON-ASCII CHARACTER SETS GOES HERE++++++++++ *) FOR I := 0 TO 255 DO IF INTAB[I] = 256 THEN INTAB[I] := 46 ELSE OUTTAB[INTAB[I]] := I; FOR DEVCODE := 1 TO 6 DO BEGIN INIT[DEVCODE] := FALSE; WAIT[DEVCODE] := 0; ENDFILE[DEVCODE] := FALSE; END; WORD1[1] := 0; WORD1[2] := 0; WORD1[3] := 1; HTEST := 1000; BREAKPT := MSIZE; FOR I := 0 TO MSIZE DO (*INITIALIZE MEMORY TO HEX 'FF'*) M[I] := 255; FOR I := 0 TO 5 DO (*INITIALIZE REGISTERS TO HEX 'FF'*) FOR J := 1 TO 3 DO REGISTERS[I,J] := 255; PC := 0; CC := LT; FIRSTREAD := TRUE; EOL := FALSE; MSG[1] := '; '; MSG[2] := ' - '; MSG[3] := ' - '; MSG[4] := 'ARITHMETIC OVERFLOW; '; MSG[5] := 'DIVISION BY ZERO; '; MSG[6] := 'A=- '; MSG[7] := 'X=- '; MSG[8] := 'L=- '; MSG[9] := 'B=- '; MSG[10] := 'S=- '; MSG[11] := 'T=- '; MSG[12] := 'P=- '; MSG[13] := 'CC=LT- '; MSG[14] := 'CC=EQ- '; MSG[15] := 'CC=GT- '; MSG[16] := 'INVALID STARTING ADDRESS; '; MSG[17] := 'INVALID ENDING ADDRESS; '; MSG[18] := 'NO ENDING ADDRESS SPECIFIED; '; MSG[19] := 'IMPROPER RANGE OF ADDRESSES; '; MSG[20] := 'INVALID REGISTER NUMBER; '; MSG[21] := 'INVALID ADDRESS SPECIFIED; '; MSG[22] := 'INVALID REGISTER CONTENTS SPECIFIED; '; MSG[23] := 'INVALID MEMORY CONTENTS SPECIFIED; '; MSG[24] := 'ILLEGAL BOOTSTRAP DATA; '; MSG[25] := 'INVALID COUNT SPECIFIED; '; MSG[26] := 'UNSUPPORTED MACHINE INSTRUCTION; '; MSG[27] := 'ILLEGAL MACHINE INSTRUCTION; '; MSG[28] := 'ILLEGAL ADDRESSING MODE; '; MSG[29] := 'ADDRESS OUT OF RANGE; '; MSG[30] := 'STORE IMMEDIATE NOT ALLOWED; '; MSG[31] := 'JUMP IMMEDIATE NOT ALLOWED; '; MSG[32] := 'UNSUPPORTED I/O DEVICE; '; MSG[33] := 'UNSUPPORTED INPUT DEVICE; '; MSG[34] := 'INPUT DEVICE NOT READY; '; MSG[35] := 'ATTEMPT TO READ DEVF1 PAST END OF FILE; '; MSG[36] := 'ATTEMPT TO READ DEVF2 PAST END OF FILE; '; MSG[37] := 'ATTEMPT TO READ DEVF3 PAST END OF FILE; '; MSG[38] := 'UNSUPPORTED OUTPUT DEVICE; '; MSG[39] := 'OUTPUT DEVICE NOT READY; '; MSG[40] := 'INSTRUCTIONS EXECUTED; '; MSG[41] := 'BREAKPOINT REACHED; '; MSG[42] := 'C(ONTINUE OR H(ALT?; '; MSG[43] := 'SIC SIMULATOR V1.5; '; MSG[44] := 'COMMAND: S(TART, R(UN, E(NTER, D(UMP, - '; MSG[45] := 'H(COUNT, B(KPT, Q(UIT?; '; MSG[46] := 'UNRECOGNIZED COMMAND; '; MSG[47] := 'ILLEGAL REGISTER NUMBER; '; MSG[48] := 'STANDARD - '; END (* INITIALIZE *); (******************************************************************) BEGIN (*MAIN PROGRAM -- READS COMMANDS AND CALLS APPROPRIATE PROCEDURES*) INITIALIZE; IF NOT XE THEN WRITEM(48) (*STANDARD*); WRITEM(43) (*SIC SIMULATOR*); COMMAND := ' '; WHILE COMMAND <> 'Q' DO BEGIN WRITEM(44); WRITEM(45); (*COMMAND PROMPT LINE*) IF NOT FIRSTREAD THEN READL; (* BEGIN NEXT COMMAND LINE *) READC(COMMAND); IF EOF(INPUT) THEN COMMAND := 'Q'; READC(NEXTCHAR); WHILE NEXTCHAR <> ' ' DO READC(NEXTCHAR); IF COMMAND = 'D' THEN DUMP ELSE IF COMMAND = 'E' THEN ENTER ELSE IF COMMAND = 'R' THEN RUN ELSE IF COMMAND = 'S' THEN START ELSE IF COMMAND = 'B' THEN BKPT ELSE IF COMMAND = 'H' THEN HCOUNT ELSE IF COMMAND <> 'Q' THEN WRITEM(46) (*UNRECOGNIZED COMMAND*); END; CLOSE(LOG); IF INIT[1] THEN CLOSE(DEVF1); IF INIT[2] THEN CLOSE(DEVF2); IF INIT[3] THEN CLOSE(DEVF3); IF INIT[4] THEN CLOSE(DEV04); IF INIT[5] THEN CLOSE(DEV05); IF INIT[6] THEN CLOSE(DEV06); END.