/**************************************************************************** * * * M a c r o A s s e m b l e r * * * ***************************************************************************** 0. Copyright, 1989 by Professor Paul D. Amer Computer and Information Science Department University of Delaware Newark, Delaware 19716 (302) 451 1944 email: amer@dewey.udel.edu 1. Purpose: - This is a two pass macro assembler for the MAC1 assembly language described by Andrew Tanenbaum in "Structured Computer Organization" on page 141. - The first pass builds a symbol table and checks for syntax errors and non alphanumeric characters. As each line is inspected and approved it is written to the temporary file. However labels, comments, literals, and constants are not written. - If there are any errors, writing to the temporary file is disabled and the second pass is not initiated. The second pass codes the the opcodes and operands from the temporary file and prints them out in hex, one to a line. 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input: The 2nd command line argument (if given) is the path name of an assembly language file. The 3rd command line argument (if given) is a path name to a file to hold the assembled code. Parameters: argc, argv 4. Output: The assembled code (in a file, if requested), or appropriate error messages. Parameters: none 5. Procedures/Functions called by this module: initialize, firstpass, secondpass 6. Known modules calling this module: none known 7. Local Variables: none 8. Global Variables: targetptr - file pointer to the output file (will contain object code) sourceptr - file pointer to the users input file temporaryptr - file pointer to the temporary file temporary - name of temporary file that holds 1st pass output do_second_pass - flag for aborting the second pass, writing to the temporary file, and the actual assembly process if FALSE ENTRY - linked list structure containing each element in the symbol table (labels, symbols, etc) symbol_table_head - pointer to first element in linked list of ENTRY symbol_table_tail - pointer to last element in linked list of ENTRY temptr - work pointer and place holder workptr - work pointer and place holder, used in looking up an entry in the symbol table. MAC - linked list structure listing all the names of the macros mhead - pointer to the first element in the MAC linked list mcurrent - pointer to the current macro being expanded mbuffer - pointer to a newly created MAC node buffer - pointer to a newly created node NODE - linked list structure containing the bodies of the macros nhead - pointer to the first element in the NODE linked list ncurrent - pointer to the current line in the macro being expanded nbuffer - pointer to a newly added line is_constant - flag set to TRUE if the symbol is a constant linenum - the current line of the data file, used for error messages label_field - array for holding the 1st field on the line operand_field - array for holding the 2nd field on the line opcode_field - array for holding the 3rd field on the line comment_field - array for holding the 4th field on the line, only the first char is checked (against `/') line - holds the next line from the data file ILC - Instruction location counter, always incremented by 1 is_label - flag set if `label_field' contains a label print_label - flag for writing the label in the temporary file print_opcode - flag for writing the opcode in the temporary file print_operand - flag for writing the operand in the temporary file increment_ILC - flag for incrementing the ILC is_literal - flag set to TRUE if line contains a literal 9. Global Constants: TRUE - boolean true, 1 FALSE - boolean false, 0 MAXLINE - The max length of a line MAX_FIELD_LENGTH - The max length of a field (label,symbol,operand) BINARYLENGTH - number of bits in word HIGHEST_NUMBER - highest number allowable for word size LOWEST_NUMBER - lowest number allowable for word size HIGHEST_12_BIT - highest decimal number for 12 bits HIGHEST_8_BIT - highest decimal number for 8 bits 10.Bugs: none known *****************************************************************************/ #include #include #include /* * The following include statement is necessary to make the * source code compatible with Microsoft C verison 6.00A. * (WRM 12/31/90) */ #include /* * If you are compiling this code on a VMS system, uncomment * the VMS define statement. If you are compiling on a SPARC * Station, uncomment the SPARC define statement. (WRM 2/10/92) */ /*#define VMS 1*/ /*#define SPARC 1*/ #define TRUE 1 #define FALSE 0 #define MAXLINE 256 #define MAX_FIELD_LENGTH 30 #define BINARYLENGTH 16 #define HIGHEST_NUMBER 32767 #define LOWEST_NUMBER -32768 #define HIGHEST_12_BIT 4095 #define HIGHEST_8_BIT 255 FILE *targetptr, *sourceptr, *temporaryptr; char *upper_to_lower(); /* * The following declarations are now necessary to account for * the fact that these routines now pass unsigned integers. */ /* * The following code was added to facilitate portability to VMS. * Originally the code passed the input parameter types, a practice * which the SPARC compiler seems to object to! (WRM 2/10/92). */ #ifdef SPARC unsigned break_down(); unsigned evaluate(); #else unsigned break_down( char * ); unsigned evaluate( char *, char *, int, int ); #endif /* End of modification */ int do_second_pass = TRUE; typedef struct entry { /* contains: symbols, literals, labels, constants, macro defn */ char symbol[MAX_FIELD_LENGTH]; unsigned value; struct entry *next; } ENTRY; typedef struct nextline { /* each macro is made up of several lines stored in NODE's */ char line[MAXLINE]; /* The current line of the macro */ struct nextline *nextl; /* pointer to the next line in the macro */ } NODE; typedef struct macro_list { struct macro_list *nextm; /* pointer to the next macro */ struct nextline *nextl; /* pointer to the first line in the macro */ char name[MAX_FIELD_LENGTH]; /* The name of the macro */ } MAC; ENTRY *symbol_table_head, *symbol_table_tail; ENTRY *lookup(); ENTRY *temptr, *buffer, *workptr; MAC *mhead, *mcurrent, *mbuffer; NODE *nhead, *ncurrent, *nbuffer; char label_field[MAX_FIELD_LENGTH]; char operand_field[MAX_FIELD_LENGTH]; char opcode_field[MAX_FIELD_LENGTH]; char comment_field[MAX_FIELD_LENGTH]; char line[MAXLINE]; int is_constant = 0; int linenum = 1; int ILC = 0; int is_label = FALSE; int print_label = TRUE; int print_opcode = TRUE; int print_operand = TRUE; int increment_ILC = TRUE; int is_literal = FALSE; void initialize(); void firstpass(); void secondpass(); void instructions(); void error(); void write_temp(); void binarycalc(); void convertneg(); void twoscompliment(); int install(); int startmacro(); int loadmacro(); int field1(); int field2(); int field3(); int valid(); int to_int(); int isnum(); int binary_to_decimal(); /**************************************************************************** * M A I N P R O G R A M * ****************************************************************************/ void main (argc,argv) int argc; char *argv[]; { initialize (argv, argc); firstpass ( ); if (do_second_pass) { fclose(temporaryptr); temporaryptr = fopen("macasm.tmp", "r"); /* write buffer to file so it can be read again. */ secondpass ( ); } else fprintf(stderr, "Assembly terminated after PASS 1 - syntax errors\n\n"); /* * The following code was added to facilitate portability to VMS. * Originally the code only contained the "Unlink ("macasm.tmp)" * statement. (WRM 2/10/92). */ #ifdef VMS delete("macasm.tmp"); #else unlink("macasm.tmp"); #endif /* End of modification */ } /**************************************************************************** * procedure/function: INITIALIZE * ***************************************************************************** 1. Purpose: - checks to see if files are valid, and creates pointers to them - enters the instruction set into the symbol table 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: argv - array of all arguments given by the user on the command line argv[1] - the name of the file containing the program to be assembled, or standard input if user didn't give a file name argv[2] - the name of the file that will contain the assembled program, or standard output if user didn't give a file name argc - counter of how many arguments given by the user on the command line 4. Returned Value: none Output Parameters: none 5. Procedures/Functions called by this module: instructions 6. Known modules calling this module: main 7. Local Variables: none 8. Global Variables used (and how modified in this module): sourceptr - set to input file containing the assembly language code targetptr - set to output file containing the assembly language code temporaryptr - set to the temporary file temptr - set to NULL workptr - set to NULL mhead - set to NULL mcurrent - set to NULL nhead - set to NULL ncurrent - set to NULL 9. Global Constants used in this module: 10.Bugs: none known ****************************************************************************/ void initialize ( argv, argc ) char *argv[]; int argc; { switch (argc) { case 1 : sourceptr = stdin; targetptr = stdout; break; case 2 : if ((sourceptr = fopen( argv[1],"r" )) == NULL ) { fprintf(stderr, "Cant open %s for reading\n",argv[1]); exit (1); } targetptr = stdout; break; case 3 : if ((targetptr = fopen(argv[2], "r")) == NULL) /* if the output file does not exist then */ if ((sourceptr = fopen(argv[1], "r")) == NULL) { fprintf(stderr, "Can't open %s for reading\n", argv[1]); exit(1); } else /* the input file is fine, open the output file */ targetptr = fopen(argv[2], "w"); /* "w" creates a new file */ else { fprintf(stderr, "%s already exists\n", argv[2]); exit(1); } break; default : fprintf(stderr, "Usage: mac.assem [f1] [f2]\n"); exit(1); } /* check to see if temporary file already exists */ if ((temporaryptr = fopen("macasm.tmp", "r")) == NULL) { if ((temporaryptr = fopen("macasm.tmp","w")) == NULL) { fprintf(stderr, "Can't open `macasm.tmp,'"); fprintf(stderr, " and it is needed for execution.\n"); exit(1); } } else { fprintf(stderr, "File `macasm.tmp' already"); fprintf(stderr, " exists, and is needed for execution.\n"); exit(1); } instructions(); temptr = NULL; workptr = NULL; mhead = mcurrent = NULL; nhead = ncurrent = NULL; } /**************************************************************************** * procedure/function: FIRSTPASS * ***************************************************************************** 1. Purpose: - This function reads through the input file, making the symbol table and checking for errors. As it does this it writes the opcodes and operands to a temporary file which will be read during the second pass. Only the first four fields are scanned, any additional fields are ignored. 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: none 4. Returned Value: none Output Parameters: none 5. Procedures/Functions called by this module: field1, field2, field3, write_temp, error 6. Known modules calling this module: main 7. Local Variables: none 8. Global Variables used (and how modified in this module): sourceptr - is advanced along the file linenum - is incremented after each line is_constant - cleared to FALSE is_label - cleared to FALSE is_literal - cleared to FALSE label_field - set to contain the first string on the current line opcode_field - set to contain the second string on the current line operand_field - set to contain the third string on the current line comment_field - set to contain the fourth string on the current line do_second_pass- if still TRUE, second pass is initiated 9. Global Constants used in this module: FALSE 10.Bugs: none known ****************************************************************************/ void firstpass( ) { while (fgets(line,MAXLINE,sourceptr) != NULL) { is_constant = is_label = is_literal = FALSE; *label_field = '\0'; /* clear the fields so old data is removed */ *opcode_field = '\0'; *operand_field = '\0'; *comment_field = '\0'; if (sscanf(line,"%s%s%s%s",label_field,opcode_field,operand_field,comment_field) > 0) { if (!strcmp(label_field,"startmacro")) startmacro( ); else if (!strcmp(label_field,"macro")) loadmacro( ); else if (field1() == FALSE) if (field2() == FALSE) if ( (field3() == FALSE ) && (comment_field[0] != '\0') && (comment_field[0] != '/' ) ) error(1,comment_field[0]); if (do_second_pass) write_temp(); } linenum++; } fprintf(stderr, "\n\t\tFirst Pass Completed\n"); } /**************************************************************************** * procedure/function: WRITE_TEMP * ***************************************************************************** 1. Purpose: Writes operands and opcodes (only) to the temporary file. Builds the symbol table, and converts upper case letters in opcodes to lower case. Increments the Instruction Location Counter, and resets the various flags. 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: none 4. Returned Value: none Output Parameters: none 5. Procedures/Functions called by this module: install, break_down, upper_to_lower, isnum 6. Known modules calling this module: firstpass, loadmacro 7. Local Variables: convert - flag to ensure that only an instruction has its case changed, not a label or constant. 8. Global Variables used (and how modified in this module): is_constant, is_label, is_literal, temporaryptr, ILC - is incremented if the line will produce code label_field - set to lower case if an instruction operand_field - set to lower case if an instruction opcode_field - set to lower case if an instruction print_label - cleared to TRUE after temporary file is written print_opcode - cleared to TRUE after temporary file is written print_operand - cleared to TRUE after temporary file is written increment_ILC - cleared to TRUE after temporary file is written 9. Global Constants used in this module: TRUE, FALSE 10.Bugs: none known ****************************************************************************/ void write_temp() { int convert = TRUE; if (is_literal) install(label_field,break_down(operand_field)); else if (is_constant) install(label_field,ILC); else if (is_label) { if (isnum(opcode_field,TRUE)) install(label_field,break_down(opcode_field)); else install(label_field,ILC); } if (increment_ILC) ILC++; increment_ILC = TRUE; if (print_label) { upper_to_lower(label_field); if (label_field) fprintf(temporaryptr,"%s ",label_field); convert = FALSE; } if (print_opcode) { if (convert) upper_to_lower(opcode_field); if (opcode_field) if (isnum(opcode_field,TRUE)) /* * commented out "d" formatting variable and changed it * to the "u" which is requred to be compatible with the * returned value of break_down(). (WRM, 01/01/90) */ /* fprintf(temporaryptr,"%d ",(break_down(opcode_field))); */ fprintf(temporaryptr,"%u ",(break_down(opcode_field))); else fprintf(temporaryptr,"%s ",opcode_field); convert = FALSE; } if (print_operand) { if (convert) upper_to_lower(operand_field); if (operand_field) fprintf(temporaryptr,"%s ",operand_field); convert = FALSE; } if (convert == FALSE) /* put a carriage return after each line */ fprintf(temporaryptr,"\n"); print_label = print_opcode = print_operand = TRUE; } /**************************************************************************** * procedure/function: UPPER_TO_LOWER * ***************************************************************************** 1. Purpose: Converts all upper case letters to lower case in the string passed 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: instruction - a character string 4. Returned Value: instruction - a character string with all lower case letters, no caps Output Parameters: none 5. Procedures/Functions called by this module: none 6. Known modules calling this module: field2, write_temp 7. Local Variables: i - loop counter 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: 10.Bugs: none known ****************************************************************************/ char *upper_to_lower(instruction) char *instruction; { char *i; for (i = instruction; *i; i++ ) if ( isupper( *i ) ) *i = tolower( *i ); return (instruction); } /**************************************************************************** * procedure/function: FIELD1 * ***************************************************************************** 1. Purpose: Evaluates label_field for syntax and sets print flags. Looks for labels (:), and comments (/). 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: none 4. Returned Value: TRUE if end of line or syntax error found, else FALSE Output Parameters: none 5. Procedures/Functions called by this module: valid, error 6. Known modules calling this module: firstpass,secondpass, startmacro, loadmacro 7. Local Variables: i - pointer to next character in label_field finished - flag for stopping evaluation of the field, usually due to errors eoln - set if finished processing a line (comment detected, or error found) 8. Global Variables used (and how modified in this module): increment_ILC - cleared to FALSE if the line is a comment print_label - cleared to FALSE if the line is a comment print_opcode - cleared to FALSE if the line is a comment print_operand - cleared to FALSE if the line is a comment is_label - set to TRUE if the first field is a label label_field - NULL added to the end if field is a label 9. Global Constants used in this module: TRUE, FALSE 10.Bugs: none known ****************************************************************************/ int field1() { int i = 0; int finished = FALSE; int eoln = FALSE; while ( *(label_field + i) && !finished) { switch ( *(label_field + i)) { case '/': /* comment? */ if ( i != 0 ) { eoln = TRUE; error(2,'\0'); /* characters before the '/' aren't allowed */ } eoln = finished = TRUE; increment_ILC = FALSE; print_label = print_opcode = print_operand = FALSE; break; case ':': /* label or constant? */ if (i == 0) { eoln = TRUE; error(3,'\0'); /* missing label name */ } else if ( *(label_field + i + 1) != '\0' ) { eoln = TRUE; error ( 4,*(label_field + 1 + i) ); /* characters after the ':' not allowed */ } else { is_label = TRUE; print_label = FALSE; *(label_field + i) = '\0'; } break; default : if ( !valid( *(label_field + i),1 ) ) { eoln = FALSE; error( 5,*(label_field + i) ); /* invalid character */ } break; } i++; } return(eoln); } /**************************************************************************** * procedure/function: FIELD2 * ***************************************************************************** 1. Purpose: Evaluates 'opcode_field' for syntax and sets print flags. Also looks for comments (/) and literal statements (=). 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: none 4. Returned Value: TRUE if end of line or syntax error found, else FALSE Output Parameters: none 5. Procedures/Functions called by this module: valid, error, lookup, isnum 6. Known modules calling this module: firstpass, secondpass, startmacro, loadmacro 7. Local Variables: i - pointer to next element in opcode_field finished - flag for stopping evaluation of the field, usually due to errors eoln - flag set if finished processing a line (comment detected, or error found) 8. Global Variables used (and how modified in this module): is_label, is_constant - cleared to TRUE if line is a constant increment_ILC - cleared to FALSE if the line is a literal print_label - cleared to FALSE if the line is a literal print_opcode - cleared to FALSE if the line is a comment or literal print_operand - cleared to FALSE if the line is a comment or literal opcode_field - set to lower case if an instruction 9. Global Constants used in this module: TRUE, FALSE 10.Bugs: none known ****************************************************************************/ int field2() { int i = 0; int finished = FALSE; int eoln = FALSE; while( *(i + opcode_field) && !finished) /* step through the entire opcode field */ { switch( *(i + opcode_field)) { /* examine next character */ case '/': /* comment? */ if ( i != 0 ) error(1,'\0'); /* comment must begin with a '/' */ if (is_label) error(6,'\0'); /* line has only a label, no data */ print_opcode = print_operand = FALSE; eoln = finished = TRUE; break; case '=': /* literal? */ if ( i != 0 ) { eoln = TRUE; error(7,'\0'); /* no characters allowed before a '=' */ } if ( *(i + 1 + opcode_field) ) { eoln = TRUE; error( 8,'\0'); /* no characters allowed after a '=' */ } if (is_label) { eoln = TRUE; error(9,'\0'); /* missing left side of '=' */ } is_literal = finished = TRUE; increment_ILC = FALSE; print_label = print_opcode = print_operand = FALSE; break; default: if ( !valid( *(i + opcode_field),2 ) ) { eoln = TRUE; error( 5,*(i + opcode_field) ); /* non alphanumeric */ } break; } i++; } if (is_label && print_opcode ) { if ( lookup(upper_to_lower(opcode_field) ) == NULL ) /* line is a constant if the opcode field is not in the symbol table */ if (isnum(opcode_field,TRUE)) is_constant = increment_ILC = print_opcode = TRUE; else increment_ILC = print_opcode = FALSE; /* just a label, not a constant */ } return(eoln); } /**************************************************************************** * procedure/function: FIELD3 * ***************************************************************************** 1. Purpose: Evaluates 'operand_field' for syntax and sets print flags. Also looks for comments (/) and is_literal statement termination (an '=' in the second field needs an argument in the third). 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: none 4. Returned Value: TRUE if end of line or syntax error found, else FALSE Output Parameters: none 5. Procedures/Functions called by this module: valid, error 6. Known modules calling this module: firstpass, secondpass, startmacro, loadmacro 7. Local Variables: i - pointer to next character being examined in operand_field finished - flag for stopping evaluation of the field, usually due to errors eoln - set if finished processing a line (comment detected, or error found) 8. Global Variables used (and how modified in this module): is_literal, operand_field, print_operand - cleared to FALSE if the line is a comment is_label - set to TRUE if the first field is a label 9. Global Constants used in this module: TRUE, FALSE 10.Bugs: none known ****************************************************************************/ int field3() { int i = 0; int finished = FALSE; int eoln = FALSE; while( *(i + operand_field) && !finished) { /* test every character in operand_field unless there is an error */ switch( *(i + operand_field)) { /* test next character */ case '/': if (i != 0) error(2,'\0'); /* no chars before a '/' allowed */ if (is_literal) error(10,'\0'); /* missing right hand side of '=' */ else finished = TRUE; eoln = TRUE; print_operand = FALSE; break; default: if ( !valid( *(i + operand_field),2 ) ) { eoln = TRUE; error( 5, *(i + operand_field) ); /* non alphanumeric */ } break; } i++; } return(eoln); } /**************************************************************************** * procedure/function: VALID * ***************************************************************************** 1. Purpose: tests for invalid characters (non alpha-numeric), if type is 2 it will also check for a '+' or '-' 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: character - a character to be tested type - 1 if checking only for alpha-numeric characters 2 if checking for both alpha-numeric and '-' and '+' 4. Returned Value: TRUE (valid) or FALSE (invalid). Output Parameters: none 5. Procedures/Functions called by this module: none 6. Known modules calling this module: field1, field2, field3 7. Local Variables: correct - flag for testing non alpha-numeric characters 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: TRUE, FALSE 10.Bugs: none known ****************************************************************************/ int valid(character, type) char character; int type; { int correct = FALSE; /* flag for testing non alphanumeric char */ if ( (type == 2) && ((character == '+') || (character == '-')) ) correct = TRUE; if ( isalpha(character) || isdigit(character) ) correct = TRUE; return(correct); } /**************************************************************************** * procedure/function: ISNUM * ***************************************************************************** 1. Purpose: - checks if a string is composed of all digits, or (if the flag is set, all digits preceeded by a '-' or '+'). This differs from the function 'valid' because it checks a string, as opposed to 1 character. 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: param - a character string csigned - flag to say if sign should be considered 4. Returned Value: FALSE - not all digits ( or sign ) or TRUE - all digits ( and sign ) Output Parameters: none 5. Procedures/Functions called by this module: none 6. Known modules calling this module: field2, secondpass, write_temp 7. Local Variables: i - character string pointer to next character being checked 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: TRUE, FALSE 10.Bugs: none known ****************************************************************************/ int isnum(param,csigned) char *param; int csigned; { char *i = param; if (csigned) if ((param[0] == '-') || (param[0] == '+')) ++i; if (*i) while(*i) { if (!isdigit(*i)) return(FALSE); ++i; } else return(FALSE); return(TRUE); } /**************************************************************************** * procedure/function: ERROR * ***************************************************************************** 1. Purpose: This function prints out syntactical error messages, prevents the second pass, and prevents writing to the temporary file. 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: type - error number badchar - character or string in error 4. Returned Value: error message on standard error Output Parameters: none 5. Procedures/Functions called by this module: none 6. Known modules calling this module: firstpass, secondpass, install, evaluate, to_int, startmacro, loadmacro, field1, field2, field3 7. Local Variables: stop - set if a fatal error occurs, this terminates assembly immediately 8. Global Variables used (and how modified in this module): opcode_field, do_second_pass - is set to false, the second pass will not be done 9. Global Constants used in this module: TRUE, FALSE, HIGHEST_NUMBER, LOWEST_NUMBER, HIGHEST_12_BIT, HIGHEST_8_BIT 10.Bugs: none known ****************************************************************************/ void error(type,badchar) int type; char *badchar; { int stop = FALSE; fprintf(stderr, "\nSyntax error on line %d \n",linenum); switch(type) { case 1: fprintf(stderr, "Comment field must begin with a `/'\n"); break; case 2: fprintf(stderr, "Characters before `/' not allowed \n"); break; case 3: fprintf(stderr, "Missing label before `:' \n"); break; case 4: fprintf(stderr, "Characters after `:' not allowed \n"); break; case 5: fprintf(stderr, "Invalid character `%c' must be ",badchar); fprintf(stderr, "alpha numeric \n"); break; case 6: fprintf(stderr, "Line contains only label and comment\n"); break; case 7: fprintf(stderr, "Characters before `=' not allowed\n"); break; case 8: fprintf(stderr, "Characters after `=' not allowed\n"); break; case 9: fprintf(stderr, "Missing symbol before `=' \n"); break; case 10: fprintf(stderr, "Missing value after `=' \n"); break; case 11: fprintf(stderr, "Undefined string - %s\n",badchar); break; case 12: fprintf(stderr, "No operand allowed after 0 operand opcode\n"); break; case 13: fprintf(stderr, "Missing operand\n"); break; case 14: fprintf(stderr, "%s redefined\n",badchar); break; case 15: fprintf(stderr, "%s undefined\n",badchar); break; case 16: fprintf(stderr, "Negative value only allowed in a constant\n"); break; case 17: fprintf(stderr, "A macro call within a macro is prohibited\n"); stop = TRUE; break; case 18: fprintf(stderr, "Redefinition of macro `%s'\n",opcode_field); break; case 19: fprintf(stderr, "Macro `%s' undefined\n",opcode_field); break; case 20: fprintf(stderr, "Addresses must be between 0 and %d\n", HIGHEST_12_BIT); break; case 21: fprintf(stderr, "Numbers must be between %d, and %d\n", LOWEST_NUMBER, HIGHEST_NUMBER); break; case 22: fprintf(stderr, "8 bit constants must be between 0 and %d\n", HIGHEST_8_BIT); break; default: fprintf(stderr, "ERROR, contact your instructor\n"); /*This line CAN'T be reached, but just in case! */ } do_second_pass = FALSE; if ( stop ) { fprintf(stderr, "Fatal error on line %d, assembly terminated\n",ILC); /* * The following code was added to facilitate portability to VMS. * Originally the code only contained the "Unlink ("macasm.tmp)" * statement. (WRM 2/10/92). */ #ifdef VMS delete("macasm.tmp"); #else unlink("macasm.tmp"); #endif /* End of modification */ exit ( 1 ); } } /**************************************************************************** * procedure/function: SECONDPASS * ***************************************************************************** 1. Purpose: - Reads through the temporary file containing the instructions and operands, encodes them into hexadecimal, and writes them into the output file. Checks for 0 and 1 operand instructions and errors 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: none 4. Returned Value: none Output Parameters: none 5. Procedures/Functions called by this module: field1, field2, field3, error, isnum, lookup, to_int, break_down 6. Known modules calling this module: main 7. Local Variables: temphex - holds the hex value of the operand hexcode - holds the final hex value of the line 8. Global Variables used (and how modified in this module): temporaryptr, do_second_pass, sourceptr - is advanced along the file linenum - is incremented after each line line - holds the next line of the temporary file read is_label - reset to FALSE is_literal - reset to FALSE opcode_field - set to first string on current line of temporary file operand_field - set to second string on current line of temporary file temptr - points to an entry in the symbol table 9. Global Constants used in this module: TRUE, FALSE, MAXLINE, HIGHEST_12_BIT 10.Bugs: none known ****************************************************************************/ void secondpass() { /* * These values are among the culprits which are causing the assembler * to blow up during its second pass after compiliation using the MSC * compiler. There appears to be no need for them to be long since the * opcodes and data are all 16 bits. Confusion is caused when performing * the test for a 0 operand code. For example, fffff600 is NOT between * 0xf000 and 0xfb00 since the MSC compiler DOES NOT assume sign extension! * (WRM, 01/01/91) */ /* long hexcode; long temphex;*/ unsigned hexcode; unsigned temphex; linenum = 1; while (fgets(line,MAXLINE,temporaryptr) != NULL) /* get next line of macrocode */ { *operand_field = '\0'; /* set to NULL in case next line of code has no operand */ if (sscanf(line,"%s%s",opcode_field,operand_field) > 0) { hexcode = 0; if ( ( temptr = lookup(opcode_field) ) == NULL ) /* * This comment appears to be incorrect. It should * read: "Opcode NOT FOUND in symbol table." * (WRM, 01/01/90) */ /* opcode found in symbol table */ if (isnum(opcode_field,FALSE)) fprintf(targetptr,"%04x\n",to_int(opcode_field)); else { fprintf(targetptr,"%s\n",opcode_field); error(11,opcode_field); /* undefined string */ } else /* * This comment also appears to be incorrect. It should * read: "Opcode FOUND in symbol table." (WRM, 01/01/90) */ /* opcode not found in symbol table */ { hexcode = temptr->value; if ( ( hexcode == 0xffff ) || (( hexcode >= 0xf000 ) && ( hexcode <= 0xfb00 )) || (( hexcode >= 0xfd00 ) && ( hexcode <= 0xfd80 )) ) { if (*operand_field) error(12,'\0'); /* No operand allowed after 0 operand opcode */ } else if (!*operand_field) error(13,'\0'); /* Missing operand */ else if ((temptr = lookup(operand_field)) == NULL) { temphex = break_down(operand_field); if ( ((hexcode == 0xfc00) || (hexcode == 0xfe00)) & (temphex > HIGHEST_8_BIT) ) { error(22,'\0'); /* 8-bit constant too large */ fprintf(targetptr,"%s",line); linenum++; continue; /* go to the next line, don't bother writing */ } if (temphex > HIGHEST_12_BIT) { error(20,'\0'); /* address too large */ fprintf(targetptr,"%s",line); linenum++; continue; /* go to the next line, don't bother writing */ } else hexcode += temphex; } else hexcode += temptr->value; fprintf(targetptr,"%04x\n",hexcode); } linenum++; } } if (do_second_pass) fprintf(stderr, "\n\t\tSecond Pass Completed\n"); } /**************************************************************************** * procedure/function: INSTRUCTIONS * ***************************************************************************** 1. Purpose: - This function enters all the instructions into the symbol table 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: none 4. Returned Value: none Output Parameters: none 5. Procedures/Functions called by this module: install 6. Known modules calling this module: initialize 7. Local Variables: none 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: none 10.Bugs: none known ****************************************************************************/ void instructions() { install("lodd",0x0); install("stod",0x1000); install("addd",0x2000); install("subd",0x3000); install("jpos",0x4000); install("jzer",0x5000); install("jump",0x6000); install("loco",0x7000); install("lodl",0x8000); install("stol",0x9000); install("addl",0xa000); install("subl",0xb000); install("jneg",0xc000); install("jnze",0xd000); install("call",0xe000); install("pshi",0xf000); install("ldcl",0xf100); /* Load C local instruction */ install("popi",0xf200); install("ldcd",0xf300); /* Load C direct instruction */ install("push",0xf400); install("lddl",0xf500); /* Load D local instruction */ install("pop",0xf600); install("lddd",0xf700); /* Load D direct instruction */ install("retn",0xf800); install("ldel",0xf900); /* Load E local instruction */ install("swap",0xfa00); install("adcl",0xfb00); /* Add C local instruction */ install("insp",0xfc00); install("sbcl",0xfd00); /* Subtract C local instruction */ install("jlp",0xfd40); /* Loop on address instruction */ install("nand",0xfd80); install("desp",0xfe00); install("halt",0xffff); } /**************************************************************************** * procedure/function: LOOKUP * ***************************************************************************** 1. Purpose: determines if a symbol is in the symbol table, returns its value, or NULL if not found 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: symbol - a character string representing a field. 4. Returned Value: NULL if the symbol is not in the symbol table or a pointer to the node containing the symbol-value pair. Output Parameters: none 5. Procedures/Functions called by this module: none 6. Known modules calling this module: field2, secondpass, evaluate, startmacro, install 7. Local Variables: none 8. Global Variables used (and how modified in this module): workptr - set to points to the node in the symbol table containing the value. 9. Global Constants used in this module: none 10.Bugs: none known ****************************************************************************/ ENTRY *lookup(symbol) char *symbol; { for (workptr = symbol_table_head; workptr != NULL; workptr = workptr->next) if ( !strcmp(symbol,workptr->symbol) ) return(workptr); return(NULL); } /**************************************************************************** * procedure/function: INSTALL * ***************************************************************************** 1. Purpose: enters a symbol into the symbol table with its value 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: symbol - label, constant or instruction value - value of the symbol 4. Returned Value: none Output Parameters: none 5. Procedures/Functions called by this module: lookup, error 6. Known modules calling this module: startmacro, write_temp, instructions 7. Local Variables: none 8. Global Variables used (and how modified in this module): buffer - the new node to be added to the symbol table temptr - the pointer to the symbol table symbol_table_head, symbol_table_tail - both may be altered when adding a new node 9. Global Constants used in this module: none 10.Bugs: none known ****************************************************************************/ /* * Due to modifications in the break_down() routine, the parameter * "value" will be an unsigned integer. Thus, int must be changed * to unsigned. (WRM, 01/01/90) */ int install(symbol,value) char *symbol; /*int*/unsigned value; { if ( (temptr = lookup(symbol)) == NULL ) /* not found, create new node */ { buffer = (ENTRY *) malloc(sizeof(ENTRY)); strcpy(buffer->symbol,symbol); buffer->value = value; buffer->next = NULL; } else if ( value != temptr->value) { error(14,symbol); /* redefining symbol */ return(0); } if (symbol_table_head == NULL) symbol_table_tail = symbol_table_head = buffer; else { symbol_table_tail->next = buffer; symbol_table_tail = buffer; } } /**************************************************************************** * procedure/function: BREAK_DOWN * ***************************************************************************** 1. Purpose: - breaks the symbol into two strings and determines sign of the result. this allows a symbol to contain a constant, a literal, or an addition or subtraction of 2 of the above - will honer for example a: -20 b: 20+3 c: z d: -y-3 e: w-z 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: string - a character string that is an operand 4. Returned Value: The sum or difference of the two embedded strings Output Parameters: none 5. Procedures/Functions called by this module: evaluate 6. Known modules calling this module: write_temp, secondpass 7. Local Variables: string1 - array for holding the first string string2 - array for holding the second string i - pointer to next character in original string k - pointer to next character in string1 j - pointer to next character in string2 sign1 - set to -1 if first string is negative sign2 - set to -1 if second string is negative finished - flag set to TRUE on encountering a `-' or `+' after the first string, used to divide string into string1 and string2 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: TRUE, FALSE 10.Bugs: none known ****************************************************************************/ /* * The return value of this routine is the output of the evaluate() * function. Since evaluate() is structured to return a 16 bit unsigned * integer, this return must also output an unsigned integer. * (WRM, 01/01/90) */ unsigned break_down(string) char *string; { char string1[MAX_FIELD_LENGTH]; char string2[MAX_FIELD_LENGTH]; int k = 0; int j = 0; int i = 0; int sign1 = 1; int sign2 = 1; int finished = FALSE; *string1 = '\0'; /* reset pointer */ *string2 = '\0'; /* reset pointer */ if (*string == '-') { sign1 = -1; i++; } else if (*string == '+') i++; while ( *(i + string)) { if (!finished) { switch (*(i + string)) { case '+' : *(string1 + i) = '\0'; finished = TRUE; break; case '-' : *(string1 + i) = '\0'; finished = TRUE; sign2 = -1; break; default: *(k + string1) = *(i + string); k++; break; } } else { *(j + string2) = *(i + string); j++; } i++; } *(string1 + k) = '\0'; *(string2 + j) = '\0'; return( evaluate(string1,string2,sign1,sign2) ); } /**************************************************************************** * procedure/function: EVALUATE * ***************************************************************************** 1. Purpose: - adds the digits of the two strings, then gives it the correct sign. verifies that the number falls within the size of the word. 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: s1,s2 - strings representing numbers to be converted to decimal sign1 - the sign of the first string sign2 - the sign of the second string 4. Returned Value: the sum or difference of the two strings in decimal Output Parameters: none 5. Procedures/Functions called by this module: to_int, error, lookup, convertneg 6. Known modules calling this module: break_down 7. Local Variables: value - holds the value of the resulting operation on the two strings 8. Global Variables used (and how modified in this module): workptr - will point to the node containing the symbol-value pair for s1 and s2 9. Global Constants used in this module: LOWEST_NUMBER, HIGHEST_NUMBER 10.Bugs: none known ****************************************************************************/ /* * The whole intent of this routing is to return an unsigned 16 bit * integer to the break_down() routine. Thus, to preserve this intent, * the routine must be declared as returning an unsigned integer. * (WRM, 01/01/90) */ unsigned evaluate(s1,s2,sign1,sign2) int sign1, sign2; char *s1, *s2; { int value; if ( isdigit(*s1) ) value = to_int(s1); else if (( workptr = lookup(s1)) == NULL) error(15,s1); /* undefined string */ else value = workptr->value; value *= sign1; if (*s2) if ( isdigit(*s2) ) value += (to_int(s2) * sign2); else if (( workptr = lookup(s2)) == NULL) error(15,s2); /* undefined string */ else value += (workptr->value * sign2); if (value < 0) if (is_constant) { if (value < LOWEST_NUMBER) error(21,'\0'); else convertneg (&value); /* convert negative value to decimal value of twos compliment */ } else error(16,'\0'); /* Negative value only allowed in a constant */ else if ( value > HIGHEST_NUMBER ) error(21,'\0'); /* * Added (unsigned) type cast to ensure correct parameter * passing. (WRM, 01/01/90) */ return( (unsigned) value); } /**************************************************************************** * procedure/function: TO_INT * ***************************************************************************** 1. Purpose: converts a character string containing numbers to an integer value 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: string - a character string containing numbers 4. Returned Value: the decimal version of the string Output Parameters: none 5. Procedures/Functions called by this module: error 6. Known modules calling this module: secondpass, evaluate 7. Local Variables: total - the decimal version of the string 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: none 10.Bugs: none known ****************************************************************************/ int to_int(string) char *string; { int total = 0; while ( *string ) { if ( isdigit(*string) ) total = total * 10 + ( *string - '0'); else error(15,string); string++; } return( total ); } /**************************************************************************** * procedure/function: CONVERTNEG * ***************************************************************************** 1. Purpose: - changes negative integers into the twos compliment equivalent. 2. Programmer: Shirley Peters, January 1989 3. Input Parameters: value - number to be converted to twos compliment 4. Returned Value: twos compliment version of value Output Parameters: none 5. Procedures/Functions called by this module: binarycalc, twoscompliment, binary_to_decimal 6. Known modules calling this module: evaluate 7. Local Variables: binary_number - string holding binary version of value 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: none 10.Bugs: none known ****************************************************************************/ void convertneg (value) int *value; { char binary_number[BINARYLENGTH]; binarycalc (*value,binary_number); twoscompliment (binary_number); *value = binary_to_decimal (binary_number); } /**************************************************************************** * procedure/function: BINARYCALC * ***************************************************************************** 1. Purpose: - calculate a binary number from an integer 2. Shirley Peters, October 1988 3. Input Parameters: tens - integer read from input 4. Returned Value: none Output Parameters: binary_number - binary representation of integer 5. Procedures/Functions called by this module: none 6. Known modules calling this module: convertneg 7. Local Variables: counter - loop variable 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: none 10.Bugs: none known ****************************************************************************/ void binarycalc(tens,binary_number) int tens; char binary_number[]; { int counter; binary_number[0] = 0; for (counter = 0;tens != 0;counter++) { binary_number[counter] = tens % 2; tens /= 2; } for (;counter <= BINARYLENGTH - 1;counter++) binary_number[counter] = 0; /* zero fill to full length */ } /**************************************************************************** * procedure/function: TWOSCOMPLIMENT * ***************************************************************************** 1. Purpose: - convert a binary number to ones compliment notation, by switching every 0 to a 1 and visa versa, then adding one to convert to twos compliment 2. Programmer: Shirley Peters, October 1988 Revised By: Shirley Peters, January 1989 3. Input Parameters: binary_number - array of binary conversion of integer - lowest order bit in zero field 4. Returned Value: binary_number - array of twos compliment binary conversion of integer Output Parameters: none 5. Procedures/Functions called by this module: 6. Known modules calling this module: convertneg 7. Local Variables: x - dummy loop variable carry - overflow number from addition to be added to next higher order position and thrown away after highest order position augend - array of number to be added to the input number 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: BINARYLENGTH 10.Bugs: none known *****************************************************************************/ void twoscompliment(binary_number) char binary_number[]; { int carry = 0; int x; char onescomp[BINARYLENGTH], augend[BINARYLENGTH]; /* initialize augend - zero filled, and a 1 in the lowest order position */ augend[0] = 1; for (x = 1; x <= BINARYLENGTH - 1; x++) augend[x] = 0; /* convert to ones compliment */ for (x = 0; x <= BINARYLENGTH - 1; x++) onescomp[x] = (binary_number[x] + 1) % 2; /* adds a 1 to ones compliment to get twos compliment */ for (x = 0; x <= BINARYLENGTH - 1; x++) { binary_number[x] = (onescomp[x] + augend[x] + carry) % 2; carry = ((onescomp[x] + augend[x] + carry) > 1) ? 1 : 0; } } /**************************************************************************** * procedure/function: BINARY_TO_DECIMAL * ***************************************************************************** 1. Purpose: - convert a binary number array into a decimal number 2. Programmer: Shirley Peters, October 1988 Revised By: Shirley Peters, January 1989 3. Input Parameters: binary_number - array of binary number to be converted 4. Returned Value: decimal version of binary number Output Parameters: none 5. Procedures/Functions called by this module: none 6. Known modules calling this module: convertneg 7. Local Variables: i - dummy loop variable value - decimal version of binary number 8. Global Variables used (and how modified in this module): none 9. Global Constants used in this module: BINARYLENGTH 10.Bugs: none known ****************************************************************************/ int binary_to_decimal (binary_number) char binary_number[]; { int value = 0; int i; for (i = 0; i <= BINARYLENGTH - 1; i++) value += binary_number[i] * (1<name,opcode_field); mbuffer->nextm = NULL; mbuffer->nextl = NULL; if (mhead == NULL) { mhead = mbuffer; mcurrent = mhead; } else { while (mcurrent->nextm != NULL) mcurrent = mcurrent->nextm; mcurrent->nextm = mbuffer; mcurrent = mcurrent->nextm; } nhead = mcurrent->nextl; ncurrent = nhead; while (fgets(line,MAXLINE,sourceptr) != NULL) { is_label = is_literal = FALSE; *label_field = '\0'; *opcode_field = '\0'; *operand_field = '\0'; *comment_field = '\0'; increment_ILC = print_label = print_opcode = print_operand = FALSE; if (sscanf(line,"%s%s",label_field,opcode_field) > 0) { if (!strcmp(label_field,"endmacro")) { /* finished the macro */ increment_ILC = FALSE; linenum++; return(0); } /* start reading in the lines */ nbuffer = (NODE *)malloc(sizeof(NODE)); nbuffer->nextl = NULL; strcpy(nbuffer->line,line); if (nhead == NULL) nhead = ncurrent = mcurrent->nextl = nbuffer; else { ncurrent->nextl = nbuffer; ncurrent = ncurrent->nextl; } if (!strcmp(label_field,"startmacro")) error(17,'\0'); /* no macro calls from a macro */ else if (field1() == FALSE) if (field2() == FALSE) if ((field3() == FALSE) && ( (comment_field[0] != '\0') && (comment_field[0] != '/') )) error(1,comment_field[0]); /* Comment field must begin with a '/' */ } linenum++; if (increment_ILC) ILC++; increment_ILC = TRUE; } } /**************************************************************************** * procedure/function: LOADMACRO * ***************************************************************************** 1. Purpose: expands the macro, and then returns control to firstpass. 2. Programmer: Brandon Einhorn, Spring 1984 Revised By: Shirley Peters, January 1989 3. Input Parameters: none 4. Returned Value: none Output Parameters: none 5. Procedures/Functions called by this module: field1, field2, field3, write_temp 6. Known modules calling this module: firstpass 7. Local Variables: none 8. Global Variables used (and how modified in this module): mhead - pointer to the first element in the MAC linked list nhead - is set to the first line in the macro mcurrent - is set to the current macro being expanded ncurrent - is advanced to the next line in the macro nhead - is set to the first line of the current macro being expanded do_second_pass - is cleared to FALSE if an error is encountered label_field, opcode_field, operand_field, comment_field - are set to the first 4 fields respectively of the next line in the macro is_literal - set to TRUE if the current line contains a literal is_label - set to TRUE if the current line contains a label 9. Global Constants used in this module: 10.Bugs: none known ****************************************************************************/ int loadmacro( ) { mcurrent = mhead; while ( ( mcurrent != NULL) && ( strcmp(opcode_field,mcurrent->name)) ) mcurrent = mcurrent->nextm; /* look for macro definition in macro table */ if (mcurrent == NULL) error(19,'\0'); /* macro definition not found */ ncurrent = nhead = mcurrent->nextl; while ( ncurrent != NULL ) { is_label = is_literal = FALSE; *label_field = '\0'; *opcode_field = '\0'; *operand_field = '\0'; *comment_field = '\0'; if (sscanf(ncurrent->line,"%s%s%s%s",label_field,opcode_field,operand_field,comment_field) > 0) { if (!strcmp(label_field,"endmacro")) { print_opcode = print_operand = print_label = FALSE; return(0); } if (!strcmp(label_field,"startmacro")) error(17,'\0'); /* no recursive macros, or macro calls from a macro */ else if (!strcmp(label_field,"macro")) error(17,'\0'); /* no recursive macros, or macro calls from a macro */ else if (field1() == FALSE) if (field2() == FALSE) if (field3() == FALSE) if ( (comment_field[0] != '\0') && (comment_field[0] != '/') ) error(1,comment_field[0]); if (do_second_pass) write_temp(); } ncurrent = ncurrent->nextl; /* advance pointer to next line */ } increment_ILC = print_opcode = print_label = print_operand = FALSE; }