/**************************************************************************** * * * a s s e m b l e r . c * * * ***************************************************************************** 1. Purpose: -This program takes a file (or stdin) of microinstructions and assembles the microcode into 32 bit instructions. The microcode should be in the format of the examples in Tannenbaum's "Stuctured Computer Organization". The user is given the option to use '-l' in the command to produce an echo of the input with the assembled program. A -o was added to allow the user to use stdin for input and a specified file for the output. 2. Programmer: ? Revised By: Suzanne M. Cybulski, January 1989 Combined mic1.assem.list.c and mic1.assem.nolist.c into this one program. Added a -l option to provide flexibility with the command line. ( -l = provide list with output.) Changes were made in function GetCh and OutputMicInstr. Revised By: Suzanne M. Cybulski, January 1989 Substituted this programs Compare and MatchTok functions with strcmp functions. Deleted functions Compare and MatchTok. Revised By: Suzanne M. Cybulski, January 1989 Wrote procedure GetIOFiles to make program calling easier. Revised By: Suzanne M. Cybulski, January 1989 Added the -o option for the command line. Offers the user the flexibility to pass the output file and use stdin for the input. 3. Input: - A file which contians microinstructions as described in Tannenbaum's "Structured Computer Organization". The lines must be numbered sequentially and there must be no blank lines between instructions. 4. Output: - A file containing either listed or non-listed microinstructions which have been assembled into 32-bit instructions will be printed to stdout unless an output file is specified in the command line. - Error messages will be printed to stderr. 5. Procedures/Functions called: GetIOFiles, InitMicInstr, InitBuses, GetTok, ParseMicInstr, OutputMicInstr 6. Procedures/Functions calling: Program: mic1.assem will call this program. 7. Local Variables: none 8. Global Variables: ListFlag - 1=echo back micro code, 0=there s/b no list made CharType - E=eof, L=letter, D=digit, S=symbol, N=nil, or C=carriage return TokType - E=eof, N=name, D=digit, S=symbol, or C=carriage return NextCh - next character to be looked at MicInstr[] - the microinstruction Counter - keeps track of number of microinstructions SaveCh - flag to indicate if new character is needed marReg - flag to indicate if mar is being loaded NextTok[] - next token to be looked at Abus[] - indicates which register is loaded on bus A Bbus[] - indicates which register is loaded on bus B Cbus[] - indicates which register is loaded on bus C FirstReg[] - indicates which register is loaded first 9. Global Constants: TOKSIZE - maximum token size INSTRSIZE - number of bits in microinstruction REGSIZE - maximum size of a register name 10.Bugs: Limitations with multiple statements: - 'mar', when used, should appear first in the microinstruction. ex. correct mar:=sp; sp:=sp+1; incorrect sp:=sp+1; mar:=sp; - 'mbr', when used in an add operation, must appear first. ex. correct ac:=mbr + ac; incorrect ac:=ac + mbr; Problem: - The following will produce incorrect code: ex. mar:=ir; pc:=ir; For the first assignment, the assembler puts the 'ir' on the b-bus to get it into the 'mar'. For the second assignment, the assembler finds that 'ir' has already been loaded onto the b-bus, so it does nothing extra. Unfortunately, to get 'ir' into the 'pc' using the default alu function of 'pass the contents of the a-bus through', the 'ir' must also be placed on the a-bus. Regretably, if this bug is fixed, then the following statement will not assemble even though it is technically correct. mar:=ir; pc:=ir+ac; ****************************************************************************/ #include #define TOKSIZE 7 /* maximum token size */ #define INSTRSIZE 32 /* number of bits in microinstruction */ #define REGSIZE 6 /* maximum size of a register name */ FILE *fopen(), *infile, *outfile; int ListFlag = 0; /* 1=echo micro code, 0=there s/b no list made*/ int CharType; /* Eof, Letter, Digit, Symbol, Nil, */ /* or Carriage return */ int TokType = 'I'; /* Eof, Name, Digit, Symbol, */ /* or Carriage return */ int NextCh; /* next character to be looked at */ int MicInstr[INSTRSIZE]; /* the microinstruction */ int Counter = 0; /* keeps track of number of microinstructions */ int SaveCh = 0; /* flag to indicate if new character is needed */ int marReg = 'N'; /* flag to indicate if mar is being loaded */ char NextTok[TOKSIZE]; /* next token to be looked at */ char Abus[REGSIZE]; /* indicates which register is loaded on bus A */ char Bbus[REGSIZE]; /* indicates which register is loaded on bus B */ char Cbus[REGSIZE]; /* indicates which register is loaded on bus C */ char FirstReg[REGSIZE]; /* indicates which register is loaded first */ /**************************************************************************** * M A I N P R O G R A M * ****************************************************************************/ main( argc, argv ) int argc; char *argv[]; { extern int TokType; extern char NextTok[]; GetIOFiles( argv, argc ); /* procedure added 1/89, S. M. Cybulski */ while (TokType != 'E') { InitMicInstr(); InitBuses(); GetTok(); if (TokType == 'D') { ParseMicInstr(); OutputMicInstr(); } else if ( !(strcmp(NextTok, "{")) ) { /* the line is a comment */ while (NextTok[0] != '\n') GetTok(); } else if (TokType != 'E') { fprintf(stderr, "\nOn line %d, the line ", Counter); fprintf(stderr, "should start with a line number.\n"); ParseMicInstr(); OutputMicInstr(); } } fclose(infile); fclose(outfile); } /**************************************************************************** * procedure: GetIOFiles * ***************************************************************************** 1. Purpose: - This directs the infile and outfile pointers to the appropriate files depending on the command line input. The following command line options are provided: -l - will echo the input to the output to provided listed microcode. -o - if one file is given, the -o will signify that the file is an output file and stdin is the input file. ( Note: This procedure will check that your specified output file does not already exist. ) 2. Programmer: Suzanne M. Cybulski, January 1989. 3. Input Parameters: argv[], argc 4. Output Parameters: error messages will be printed (to stderr) if: - the given output file already exists. - a file cannot be opened for reading. - the command line usage is incorrect. 5. Procedures/Functions called: none 6. Procedures/Functions calling: main 7. Local Variables: none 8. Global Variables (and how modified in this module): ListFlag - is changed to a 1 if a list is to be produced with the output, otherwise ListFlag will equal 0 for no list. *infile - will be the input file to be assembled. *outfile - will be used to store the file which has been assembled. 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ GetIOFiles( argv, argc ) char *argv[]; int argc; { int i, j; int OptionO; /* is the flag for -o, 1= -o was in the command line */ extern int ListFlag; i=1; while (( i < argc ) && ( argv[i][0] == '-' )) { j=1; while ( argv[i][j] != '\0' ) { switch ( argv[i][j] ) { case 'l' : ListFlag = 1; break; case 'o' : OptionO = 1; break; default : fprintf( stderr, "Usage error: -%c is ", argv[i][j]); fprintf( stderr, "not a valid option.\n"); exit(1); } j++; } i++; } if ( i == argc ) /* then no input or output files are given */ if ( OptionO == 1 ) { fprintf( stderr, "Usage error: -o needs an output file\n"); exit(1); } else { infile = stdin; outfile = stdout; } else if ( (i+1) == argc ) /* then one file is given */ if ( OptionO == 1 ) { if (( outfile = fopen(argv[i], "r" )) == NULL ) { outfile = fopen(argv[i], "w" ); infile = stdin; } else { fclose( outfile ); fprintf(stderr, "%s already ", argv[i]); fprintf(stderr, "exists\n"); exit(1); } } else { if (( infile = fopen( argv[i], "r" )) == NULL ) { fprintf(stderr, "Can't open %s for reading\n", argv[i]); exit(1); } else outfile = stdout; } else if ( (i+2) == argc ) /* then 2 files are given */ if ( OptionO == 1 ) { fprintf( stderr, "Usage error: Too many "); fprintf( stderr, "arguments for -o option\n"); exit(1); } else { if (( outfile = fopen( argv[i+1], "r" )) == NULL) if (( infile = fopen( argv[i], "r" )) == NULL) { fprintf(stderr, "Can't open %s for reading\n", argv[i]); exit(1); } else outfile = fopen( argv[i+1], "w" ); else { fclose( outfile ); fprintf(stderr, "%s already ", argv[i+1]); fprintf(stderr, "exists\n"); exit(1); } } else { fprintf(stderr, "Usage: assembler [-lo] source_file"); fprintf(stderr, " [result_file];\n or assembler [-lo] "); fprintf(stderr, "[source_file]\n"); exit(1); } } /**************************************************************************** * procedure: GetTok * ***************************************************************************** 1. Purpose: -GetTok gets the next token from the input file and puts it in NextTok. It also sets up TokType which describes what type of token NextTok is: End of file, Name, Symbol, Digit or Carriage return. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: none 5. Procedures/Functions called: GetCh, InitNextTok 6. Procedures/Functions calling: ParseMicInstr, ParseCommand, ParseAssign, ParseExpression, SetReg, ParseLeftShift, ParseRightShift, ParseInvert, ParseBinAnd 7. Local Variables: i - Used as a counter for NextTok[] 8. Global Variables (and how modified in this module): SaveCh - flag is set to indicate if new character is needed CharType - is set to indicate the character type from input, E=eof L=letter, D=digit, S=symbol, N-nil, C=carriage return TokType - is set to indicate the token type, E=eof, N=name, D=digit, S=symbol, C=carriage return NextCh - contains the next character to be looked at NextTok[] - next token to be looked at is built with each call to this procedure 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ GetTok() { int i = 0; extern int SaveCh, CharType, TokType, NextCh; extern char NextTok[]; if (SaveCh == 1) SaveCh = 0; else GetCh(); if (CharType == 'E') TokType = 'E'; /* at end of file */ else while (CharType == 'N') { GetCh(); /* skip over the blank spaces */ if (CharType == 'E') TokType = 'E'; } InitNextTok(); if (CharType == 'L') { TokType = 'N'; /* the next token is a name */ while (CharType == 'L') { NextTok[i] = NextCh; i++; GetCh(); } NextTok[i] = '\0'; SaveCh = 1; /* save current char for next token */ } else if (CharType == 'D') { TokType = 'D'; /* the next token is a number */ while (CharType == 'D') { NextTok[i] = NextCh; i++; GetCh(); } NextTok[i] = '\0'; SaveCh = 1; } else if (CharType == 'S') { TokType = 'S'; /* the next token is a symbol */ NextTok[0] = NextCh; NextTok[1] = '\0'; } else if (CharType == 'C') { TokType = 'C'; /* the next token is a carriage return */ NextTok[0] = '\n'; NextTok[1] = '\0'; } } /**************************************************************************** * procedure: GetCh * ***************************************************************************** 1. Purpose: - GetCh gets one character from the input file at a time. It puts that character in NextCh, and also sets CharType which describes what type of character NextCh is: E=end of file, C=carriage return, N=nil, L=letter, D=digit and S=symbol. 2. Programmer: ? Revised By: Suzanne M. Cybulski, 01/03/89, to allow input and output files to be specified on the command line. 3. Input: - A file of microinstructions. 4. Output Parameters: - If a list is to be produced with the output, then the microinstructions are echoed to output. 5. Procedures/Functions called: none 6. Procedures/Functions calling: GetTok 7. Local Variables: c - used to hold each character as it is read from input 8. Global Variables (and how modified in this module): ListFlag - 1=echo microcode to output, 0=there s/b no list made, not modified CharType - is set depending on the type of character which is read in, E=eof, L=letter, D=digit, S=symbol, N=nil and C=carriage return NextCh - is modified to contain the next character to be looked at 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ GetCh() { int c; extern int ListFlag, CharType, NextCh; c = getc(infile); if ( feof ( infile ) ) CharType = 'E'; else { if (ListFlag == 1) /*if listed output, echo char back to terminal */ putc(c, outfile); NextCh = c; if (c == '\n') CharType = 'C'; /* char is carriage return */ else if ((c == ' ') || (c == '\t')) CharType = 'N'; /* char is a space or a tab */ else if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) CharType = 'L'; /* char is a letter */ else if ((c >= '0') && (c <= '9')) CharType = 'D'; /* char is a number */ else CharType = 'S'; /* char is a symbol */ } } /**************************************************************************** * procedure: InitNextTok * ***************************************************************************** 1. Purpose: - InitNextTok initializes NextTok with all spaces. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: none 5. Procedures/Functions called: none 6. Procedures/Functions calling: GetTok 7. Local Variables: i - used as a counter 8. Global Variables (and how modified in this module): NextTok[] - is returned containing spaces 9. Global Constants: TOKSIZE 10.Bugs: none known. ****************************************************************************/ InitNextTok() { int i; extern char NextTok[]; for (i = 0; i < TOKSIZE; i++) NextTok[i] = ' '; } /**************************************************************************** * procedure: InitMicInstr * ***************************************************************************** 1. Purpose: - InitMicInstr initializes the microinstructions to all zeroes, except in the ALU's position where a binary 3 is initialized to indicate that the ALU doesn't change its input. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: none 5. Procedures/Functions called: none 6. Procedures/Functions calling: main 7. Local Variables: i - is used as a counter 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction will be initialized to zeroes, except for the alu field which will be 10 9. Global Constants: INSTRSIZE 10.Bugs: none known. ****************************************************************************/ InitMicInstr() { int i; extern int MicInstr[]; for (i = 0; i < INSTRSIZE; i++) MicInstr[i] = 0; MicInstr[3] = 1; /* ALU doesn't change input */ } /**************************************************************************** * procedure: InitBuses * ***************************************************************************** 1. Purpose: - InitBuses initializes buses A, B and C to all spaces. This initialization takes place before each microinstruction is parsed. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: none 5. Procedures/Functions called: none 6. Procedures/Functions calling: main 7. Local Variables: i - used as a loop counter 8. Global Variables (and how modified in this module): Abus[] - is initialized to spaces Bbus[] - is initialized to spaces Cbus[] - is initialized to spaces 9. Global Constants: REGSIZE 10.Bugs: none known. ****************************************************************************/ InitBuses() { int i; extern char Abus[], Bbus[], Cbus[]; for (i = 0; i < (REGSIZE-1); i++) { Abus[i] = ' '; Bbus[i] = ' '; Cbus[i] = ' '; } Abus[i] = '\0'; /* \0 = null */ Bbus[i] = '\0'; Cbus[i] = '\0'; } /**************************************************************************** * procedure: OutputMicInstr * ***************************************************************************** 1. Purpose: - OutputMicInstr outputs the 32 bit microinstructions. 2. Programmer: ? Revised By: Suzanne M. Cybulski, 1/4/89, to allow -l option in program. 3. Input Parameters: none 4. Output Parameters: none 5. Procedures/Functions called: none 6. Procedures/Functions calling: main 7. Local Variables: i - is used as a loop counter. 8. Global Variables (and how modified in this module): ListFlag - 1=echo back micro code, 0=there s/b no list made, not modified. MicInstr[] - the microinstruction to be printed, not modified. 9. Global Constants: INSTRSIZE 10.Bugs: none known. ****************************************************************************/ OutputMicInstr() { int i; extern int ListFlag, MicInstr[]; for (i = 0; i < INSTRSIZE; i++) fprintf(outfile, "%d", MicInstr[i]); fprintf(outfile, "\n"); if (ListFlag == 1) /*if listed output, insert row between instructions */ fprintf(outfile, "\n"); } /**************************************************************************** * procedure: ComputeAddr * ***************************************************************************** 1. Purpose: - ComputeAddr takes the decimal address of a jump which is in NexTok and converts it to binary. It is then put in the address field of the microinstruction. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: none 5. Procedures/Functions called: ConvertToInt 6. Procedures/Functions calling: ParseCommand 7. Local Variables: i - used as a loop counter addr - used as temporary storage for the decimal form of the address 8. Global Variables (and how modified in this module): MicInstr[] - the address portion of the microinstruction will be set 9. Global Constants: INSTRSIZE 10.Bugs: none known. ****************************************************************************/ ComputeAddr() { int i, addr; extern int MicInstr[]; addr = ConvertToInt(); for (i = (INSTRSIZE - 1); addr > 0; i--) { if ((addr % 2) != 0) MicInstr[i] = 1; addr = addr / 2; } } /**************************************************************************** * function: ConvertToInt * ***************************************************************************** 1. Purpose: - ConvertToInt changes NextTok's character array of some number into an integer, and returns that integer. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: none 5. Procedures/Functions called: none 6. Procedures/Functions calling: ComputeAddr, ParseMicInstr 7. Local Variables: i - is used as a loop counter n - will be returned containing the integer value of a string 8. Global Variables (and how modified in this module): NextTok[] - contains the string which needs to be converted to an integer, it is not modified 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ ConvertToInt() { int i, n; extern char NextTok[]; n = 0; for (i = 0; NextTok[i] != '\0'; i++) n = (10 * n) + (NextTok[i] - '0'); return(n); } /**************************************************************************** * procedure: SetBus * ***************************************************************************** 1. Purpose: - SetBus places NextTok (which at this point, holds a register name) in the array, bus, which indicates which register has been loaded onto that bus. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: bus[] - the value of bus[] is returned with the content of NextTok[] 5. Procedures/Functions called: none 6. Procedures/Functions calling: ParseExpression, LoadReg 7. Local Variables: i - is used as a counter 8. Global Variables (and how modified in this module): NextTok[] - is only used to copy from, it is not modified 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ SetBus(bus) char bus[]; { int i; extern char NextTok[]; for (i = 0; NextTok[i] != '\0'; i++) bus[i] = NextTok[i]; bus[i] = '\0'; } /**************************************************************************** * procedure: FlagReg * ***************************************************************************** 1. Purpose: - FlagReg keeps track of the first register loaded on a bus. This is done because it is needed in cases of tir:=ir+ir. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: none 5. Procedures/Functions called: none 6. Procedures/Functions calling: Parse, Expression 7. Local Variables: i - is used as a counter 8. Global Variables (and how modified in this module): NextTok[] - is used to copy from, it is not modified FirstReg[] - will contain the value of NextTok[]. 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ FlagReg() { int i; extern char NextTok[], FirstReg[]; for (i = 0; NextTok[i] != '\0'; i++) FirstReg[i] = NextTok[i]; FirstReg[i] = '\0'; } /**************************************************************************** * procedure: ParseMicInstr * ***************************************************************************** 1. Purpose: - ParseMicInstr parses one microinstruction at a time. It continues to parse the microinstruction until a carriage return is read. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: ConvertToInt, GetTok, ParseCommand 6. Procedures/Functions calling: main 7. Local Variables: LineNum - will contain the line number given from the input microcode 8. Global Variables (and how modified in this module): NextTok[] - next token to be looked at, it is modified Counter - keeps track of number of microinstructions, it is incremented as necessary TokType - is used to decode the microinstruction, E=eof, N=name, D=digit, S=symbol or C=carriage return 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ ParseMicInstr() { int LineNum; extern char NextTok[]; extern int Counter, TokType; LineNum = ConvertToInt(); /* convert NexTok[] to an integer */ if (LineNum == Counter) { Counter++; GetTok(); } else { fprintf(stderr,"\nOn line %d, the line number is incorrect.\n",Counter); Counter++; if (TokType == 'D') GetTok(); } if ( !(strcmp(NextTok, ":")) ) GetTok(); else fprintf(stderr, "\nOn line %d, a ':' was expected.\n", (Counter - 1)); while ((NextTok[0] != '\n') && (NextTok[0] != '{')) ParseCommand(); if (NextTok[0] == '{') { while (NextTok[0] != '\n') GetTok(); } } /**************************************************************************** * procedure: ParseCommand * ***************************************************************************** 1. Purpose: - ParseCommand parses one command at a time. It sets the microinstruction according to the command. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: GetTok, ComputeAddr, ParseAssign 6. Procedures/Functions calling: ParseMicInstr 7. Local Variables: none 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction, the wr, rd, halt and goto fields are set. TokType - is modified through calls to other procedures, E=eof, N=name, D=digit, S=symbol or C=carriage return Counter - keeps track of number of microinstructions, it is not modified NextTok[] - next token to be looked at, is modified through calls to other procedures 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ ParseCommand() { extern int MicInstr[], TokType, Counter; extern char NextTok[]; if ( !(strcmp(NextTok, "rd")) ) /* set read field */ { MicInstr[9] = 1; GetTok(); } else if ( !(strcmp(NextTok, "wr")) ) /* set write field */ { MicInstr[10] = 1; GetTok(); } else if ( !(strcmp(NextTok, "halt")) ) /* set shift field to halt */ { MicInstr[5] = 1; MicInstr[6] = 1; GetTok(); } else if ( !(strcmp(NextTok, "goto")) ) /* set cond field */ { MicInstr[1] = 1; MicInstr[2] = 1; GetTok(); if (TokType != 'D') { fprintf(stderr, "\nOn line %d, ",(Counter-1)); fprintf(stderr, "expected jump address.\n"); } else ComputeAddr(); GetTok(); } else if ( !(strcmp(NextTok, "if")) ) { GetTok(); if ( !(strcmp(NextTok, "n")) ) /* set cond field */ MicInstr[2] = 1; else if ( !(strcmp(NextTok, "z")) ) /* set cond field */ MicInstr[1] = 1; else { fprintf(stderr, "\nOn line %d, ", (Counter - 1)); fprintf(stderr, "expected 'n' or 'z' register.\n"); } GetTok(); if ( !(strcmp(NextTok, "then")) ) GetTok(); else { fprintf(stderr, "\nOn line %d, ", (Counter - 1)); fprintf(stderr, "expected 'then'.\n"); } if ( !(strcmp(NextTok, "goto")) ) GetTok(); else { fprintf(stderr, "\nOn line %d, ", (Counter - 1)); fprintf(stderr, "expected 'goto'.\n"); } if (TokType != 'D') { fprintf(stderr, "\nOn line %d, ",(Counter-1)); fprintf(stderr, "expected jump address.\n"); } else ComputeAddr(); GetTok(); } else ParseAssign(); if ( !(strcmp(NextTok, ";")) ) GetTok(); else fprintf(stderr, "\nOn line %d, ';' expected.\n", (Counter - 1)); } /**************************************************************************** * procedure: ParseAssign * ***************************************************************************** 1. Purpose: - ParseAssign parses an assignment statement. It sets the microinstruction accordingly. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: GetTok, LoadReg, ParseExpression 6. Procedures/Functions calling: ParseCommand 7. Local Variables: none 8. Global Variables (and how modified in this module): marReg - flag to indicate if mar is being loaded, is set accordingly Counter - keeps track of number of microinstructions, is not modified NextTok[] - next token to be looked at ,is modified through calls to other procedure 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ ParseAssign() { extern int marReg, Counter; extern char NextTok[]; if ( !(strcmp(NextTok, "alu")) ) { marReg = 'N'; GetTok(); } else { LoadReg(); if ( !(strcmp(NextTok, "mar")) ) marReg = 'Y'; else marReg = 'N'; GetTok(); } if ( !(strcmp(NextTok, ":")) ) GetTok(); else fprintf(stderr, "\nOn line %d, ':' expected.\n", (Counter - 1)); if ( !(strcmp(NextTok, "=")) ) GetTok(); else fprintf(stderr, "\nOn line %d, '=' expected.\n", (Counter - 1)); ParseExpression(); } /**************************************************************************** * procedure: ParseExpression * ***************************************************************************** 1. Purpose: - ParseExpression parses the expression of the assignment statement and sets the microinstruction accordingly. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: ParseLeftShift, ParseRightShift, ParseInvert, ParseBinAnd, GetTok, SetBus, SetReg, FlagReg, SetAbus 6. Procedures/Functions calling: ParseAssign 7. Local Variables: none 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction, various fields are set marReg - flag to indicate if mar is being loaded, is not modified Bbus[] - indicates which register is loaded on bus, is modified Abus[] - indicates which register is loaded on bus A, is modified Counter - keeps track of number of microinstructions, not modified NextTok[] - next token to be looked at, is modified through calls to other procedures FirstReg[] - indicates which register is loaded first, is not modified 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ ParseExpression() { extern int MicInstr[], Counter, marReg; extern char NextTok[], Abus[], Bbus[], FirstReg[]; if ( !(strcmp(NextTok, "lshift")) ) ParseLeftShift(); else if ( !(strcmp(NextTok, "rshift")) ) ParseRightShift(); else if ( !(strcmp(NextTok, "inv")) ) ParseInvert(); else if ( !(strcmp(NextTok, "band")) ) ParseBinAnd(); else { if (marReg == 'Y') { /* check if bus B can be used */ if ( !(strcmp(Bbus, NextTok)) ) GetTok(); /* register previously loaded on bus B */ else if ( !(strcmp(Bbus, " ")) ) { /* load mar from bus B */ SetBus(Bbus); SetReg('B'); GetTok(); } else { fprintf(stderr,"\nOn line %d, ",(Counter-1)); fprintf(stderr,"BUS ERROR: "); fprintf(stderr,"bus B loaded with more "); fprintf(stderr,"then one register.\n"); } } else { /* mar not being loaded -- check which bus to load */ FlagReg(); /* take note of register being loaded */ if ( !(strcmp(Bbus, NextTok)) ) GetTok(); /* register previously loaded on bus B */ else if ( !(strcmp(Abus,NextTok)) ) GetTok(); /* register previously loaded on bus A */ else if ( !(strcmp(Abus, " ")) ) { /* bus A is available */ SetBus(Abus); SetAbus(); GetTok(); } else if ( !(strcmp(Bbus, " ")) ) { /* bus B is available */ SetBus(Bbus); SetReg('B'); GetTok(); } else { fprintf(stderr, "\nOn line %d, ",(Counter-1)); fprintf(stderr, "BUS ERROR: neither bus "); fprintf(stderr, "A nor B are available.\n"); } if ( !(strcmp(NextTok, "+")) ) { MicInstr[3] = 0; /* set alu field */ MicInstr[4] = 0; GetTok(); if ( !(strcmp(FirstReg, NextTok))) /* load which bus? */ { /* the same register is being added */ /* Special consideration */ if ( !(strcmp(Abus, NextTok)) ) { /* load the register on the other bus */ SetBus(Bbus); SetReg('B'); GetTok(); } else { SetBus(Abus); SetAbus(); GetTok(); } } else if ( !(strcmp(Bbus, NextTok)) ) GetTok(); /* register previously loaded on Bbus */ else if ( !(strcmp(Abus, NextTok)) ) GetTok(); /*register previously loaded on Abus*/ else if ( !(strcmp(Bbus, " ")) ) { /* bus B is avaiable */ SetBus(Bbus); SetReg('B'); GetTok(); } else if ( !(strcmp(Abus, " ")) ) { /* bus A is available */ SetBus(Abus); SetAbus(); GetTok(); } else { fprintf(stderr,"\nOn line "); fprintf(stderr,"%d, BUS ",(Counter-1)); fprintf(stderr,"ERROR: neither bus A "); fprintf(stderr,"nor B are available.\n"); } } else MicInstr[3] = 1; /* ALU doesn't change input */ } } } /**************************************************************************** * procedure: SetAbus * ***************************************************************************** 1. Purpose: - SetAbus sets the microinstruction for the A bus multiplexer or the register which will be loaded onto the A bus. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: none 5. Procedures/Functions called: SetReg 6. Procedures/Functions calling: ParseLeftShift, ParseRightShift, ParseInvert, ParseBinAnd, ParseExpression 7. Local Variables: none 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction, various fields are set NextTok[] - next token to be looked at, is not modified 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ SetAbus() { extern int MicInstr[]; extern char NextTok[]; if ( !(strcmp(NextTok, "mbr")) ) MicInstr[0] = 1; else SetReg('A'); } /**************************************************************************** * procedure: LoadReg * ***************************************************************************** 1. Purpose: - LoadReg sets the microinstruction for the mar, mbr, or the register which the output of the ALU will be loaded. It also enables the C bus if necessary. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: SetBus, SetReg 6. Procedures/Functions calling: ParseAssign 7. Local Variables: none 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction, fields mar, mbr and enable C are set Counter - keeps track of number of microinstructions, not modified NextTok[] - next token to be looked at, is not modified Cbus[] - indicates which register is loaded on bus C 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ LoadReg() { extern int MicInstr[], Counter; extern char NextTok[], Cbus[]; if ( !(strcmp(NextTok, "mar")) ) MicInstr[8] = 1; else if ( !(strcmp(NextTok, "mbr")) ) MicInstr[7] = 1; else if ( !(strcmp(Cbus, " ")) ) { /* C bus is available */ MicInstr[11] = 1; SetBus(Cbus); SetReg('C'); } else { fprintf(stderr, "\nOn line %d, BUS ERROR: ", (Counter - 1)); fprintf(stderr, "bus C is not available.\n"); } } /**************************************************************************** * procedure: SetReg * ***************************************************************************** 1. Purpose: - SetReg sets the A, B or C field of the microinstruction according to which register name is in NextTok, and which bus is passed as an argument. 2. Programmer: ? 3. Input Parameters: bus 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: GetTok 6. Procedures/Functions calling: ParseLeftShift, ParseRightShift, ParseBinAnd, ParseExpression, SetAbus, LoadReg 7. Local Variables: i - is used to hold the bit number of the microinstruction which will be set. (ei: field 'A' will begin at the 20th bit in the microinstruction. ) 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction, fields A, B and/or C are set Counter - keeps track of number of microinstructions, is not modified NextTok[] - next token to be looked at, is modified through calls to other procedures 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ SetReg(bus) int bus; { int i; extern int MicInstr[], Counter; extern char NextTok[]; if (bus == 'A') i = 20; else if (bus == 'B') i = 16; else i = 12; /***** NOTE: Due to the extent of the nested if-else's, *****/ /***** the coding standard is slightly modified. *****/ if ( !(strcmp(NextTok, "pc")) ) ; /* microinstruction is initialized to the 'pc' */ else if ( !(strcmp(NextTok, "ac")) ) MicInstr[i + 3] = 1; else if ( !(strcmp(NextTok, "sp")) ) MicInstr[i + 2] = 1; else if ( !(strcmp(NextTok, "ir")) ) { MicInstr[i + 2] = 1; MicInstr[i + 3] = 1; } else if ( !(strcmp(NextTok, "tir")) ) MicInstr[i + 1] = 1; else if ( !(strcmp(NextTok, "0")) ) { MicInstr[i + 1] = 1; MicInstr[i + 3] = 1; } else if ( !(strcmp(NextTok, "1")) ) { MicInstr[i + 1] = 1; MicInstr[i + 2] = 1; } else if ( !(strcmp(NextTok, "(")) ) { GetTok(); if ( !(strcmp(NextTok, "-")) ) GetTok(); else fprintf(stderr, "\nOn line %d, '-1' expected.\n", (Counter - 1)); if ( !(strcmp(NextTok, "1")) ) GetTok(); else fprintf(stderr, "\nOn line %d, '-1' expected.\n", (Counter - 1)); if ( !(strcmp(NextTok, ")")) ) { MicInstr[i + 1] = 1; MicInstr[i + 2] = 1; MicInstr[i + 3] = 1; } else fprintf(stderr, "\nOn line %d, ')' expected.\n", (Counter - 1)); } else if ( !(strcmp(NextTok, "-")) ) { GetTok(); if ( !(strcmp(NextTok, "1")) ) { MicInstr[i + 1] = 1; MicInstr[i + 2] = 1; MicInstr[i + 3] = 1; } else fprintf(stderr, "\nOn line %d, '-1' expected.\n", (Counter - 1)); } else if ( !(strcmp(NextTok, "amask")) ) MicInstr[i] = 1; else if ( !(strcmp(NextTok, "smask")) ) { MicInstr[i] = 1; MicInstr[i + 3] = 1; } else if ( !(strcmp(NextTok, "a")) ) { MicInstr[i] = 1; MicInstr[i + 2] = 1; } else if ( !(strcmp(NextTok, "b")) ) { MicInstr[i] = 1; MicInstr[i + 2] = 1; MicInstr[i + 3] = 1; } else if ( !(strcmp(NextTok, "c")) ) { MicInstr[i] = 1; MicInstr[i + 1] = 1; } else if ( !(strcmp(NextTok, "d")) ) { MicInstr[i] = 1; MicInstr[i + 1] = 1; MicInstr[i + 3] = 1; } else if ( !(strcmp(NextTok, "e")) ) { MicInstr[i] = 1; MicInstr[i + 1] = 1; MicInstr[i + 2] = 1; } else if ( !(strcmp(NextTok, "f"))) { MicInstr[i] = 1; MicInstr[i + 1] = 1; MicInstr[i + 2] = 1; MicInstr[i + 3] = 1; } else { fprintf(stderr,"\nOn line %d, expected register name,\n", (Counter-1)); fprintf(stderr," or bad function name "); fprintf(stderr,"or tried to load MBR on bus B.\n"); } /***** NOTE: The coding standard now resumes. *****/ } /**************************************************************************** * procedure: ParseLeftShift * ***************************************************************************** 1. Purpose: - ParseLeftShift parses a left-shift instruction and sets the microinstruction accordingly. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: GetTok, ParseBinAnd, ParseInvert, SetAbus, SetReg 6. Procedures/Functions calling: ParseExpression 7. Local Variables: none 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction, sh and alu fields are set Counter - keeps track of number of microinstructions, not modified NextTok[] - next token to be looked at, is modified through calls to other procedures 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ ParseLeftShift() { extern int MicInstr[], Counter; extern char NextTok[]; MicInstr[5] = 1; /* shift field set to loft shift (10) */ GetTok(); /* continues parsing the instruction */ if ( !(strcmp(NextTok, "(")) ) GetTok(); else fprintf(stderr, "\nOn line %d, '(' expected.\n", (Counter - 1)); if ( !(strcmp(NextTok, "band")) ) ParseBinAnd(); else if ( !(strcmp(NextTok, "inv")) ) ParseInvert(); else { SetAbus(); GetTok(); if ( !(strcmp(NextTok, "+")) ) { MicInstr[3] = 0; /* set the alu field to A+B (00) */ MicInstr[4] = 0; GetTok(); SetReg('B'); GetTok(); } else MicInstr[3] = 1; /* ALU doesn't change input */ } if ( !(strcmp(NextTok, ")")) ) GetTok(); else fprintf(stderr, "\nOn line %d, ')' expected.\n", (Counter - 1)); } /**************************************************************************** * procedure: ParseRightShift * ***************************************************************************** 1. Purpose: - ParseRightShift parses a right-shift instruction and sets the microinstruction accordingly. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: GetTok, ParseBinAnd, ParseInvert, SetAbus, SetReg 6. Procedures/Functions calling: ParseExpression 7. Local Variables: none 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction, sh and alu fields are set Counter - keeps track of number of microinstructions, not modified NextTok[] - next token to be looked at, is modified through calls to other procedures 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ ParseRightShift() { extern int MicInstr[], Counter; extern char NextTok[]; MicInstr[6] = 1; /* set shift field to right shift (01) */ GetTok(); /* continue parsing instruction */ if ( !(strcmp(NextTok, "(")) ) GetTok(); else fprintf(stderr, "\nOn line %d, '(' expected.\n", (Counter - 1)); if ( !(strcmp(NextTok, "band")) ) ParseBinAnd(); else if ( !(strcmp(NextTok, "inv")) ) ParseInvert(); else { SetAbus(); GetTok(); if ( !(strcmp(NextTok, "+")) ) { MicInstr[3] = 0; /* set the alu to A+B (00) */ MicInstr[4] = 0; GetTok(); SetReg('B'); GetTok(); } else MicInstr[3] = 1; /* ALU doesn't change input */ } if ( !(strcmp(NextTok, ")")) ) GetTok(); else fprintf(stderr, "\nOn line %d, ')' expected.\n", (Counter - 1)); } /**************************************************************************** * procedure: ParseInvert * ***************************************************************************** 1. Purpose: - ParseInvert parses an invert instruction and sets the microinstruction accordingly. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: GetTok, SetAbus 6. Procedures/Functions calling: ParseExpress, ParseLeftShift, ParseRightShift 7. Local Variables: none 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction, the alu field is set Counter - keeps track of number of microinstructions, not modified NextTok[] - next token to be looked at, is modified through calls to other procedures 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ ParseInvert() { extern int MicInstr[], Counter; extern char NextTok[]; MicInstr[3] = 1; /* set alu field to NOT A (11) */ MicInstr[4] = 1; GetTok(); if ( !(strcmp(NextTok, "(")) ) GetTok(); else fprintf(stderr, "\nOn line %d, '(' expected.\n", (Counter - 1)); SetAbus(); GetTok(); MicInstr[3] = 1; /* ALU doesn't change input */ if ( !(strcmp(NextTok, ")")) ) GetTok(); else fprintf(stderr, "\nOn line %d, ')' expected.\n", (Counter - 1)); } /**************************************************************************** * procedure: ParseBinAnd * ***************************************************************************** 1. Purpose: - ParseBinAnd parses a binary-and instruction and sets the microinstruction accordingly. 2. Programmer: ? 3. Input Parameters: none 4. Output Parameters: Any error messages are printed to stderr. 5. Procedures/Functions called: GetTok, SetAbus, SetReg 6. Procedures/Functions calling: ParseExpression, ParseLeftShift, ParseRightShift 7. Local Variables: none 8. Global Variables (and how modified in this module): MicInstr[] - the microinstruction, the alu field is set Counter - keeps track of number of microinstructions, not modified NextTok[] - next token to be looked at, is modified through calls to other procedures 9. Global Constants: none 10.Bugs: none known. ****************************************************************************/ ParseBinAnd() { extern int MicInstr[], Counter; extern char NextTok[]; MicInstr[3] = 0; /* set alu to A AND B (01) */ MicInstr[4] = 1; GetTok(); if ( !(strcmp(NextTok, "(")) ) GetTok(); else fprintf(stderr, "\nOn line %d, '(' expected.\n", (Counter - 1)); SetAbus(); GetTok(); if ( !(strcmp(NextTok, ",")) ) GetTok(); else fprintf(stderr, "\nOn line %d, ',' expected.\n", (Counter - 1)); SetReg('B'); GetTok(); if ( !(strcmp(NextTok, ")")) ) GetTok(); else fprintf(stderr, "\nOn line %d, ')' expected.\n", (Counter - 1)); }