_A GENERIC ONE-PASS ASSEMBLER_ by William E. Ives [LISTING ONE] /**************************************************************************** Main driver for generic assembler. Copyright 1988 by Michigan Technological University Written by : William E. Ives Version : 1.0 Date : Feb 1, 1989 ****************************************************************************/ #include #include #include #include #include #include "68defs.h" #include "68err.h" #include "68parse.h" #include "68list.h" #include "68assem.h" #include "68symtab.h" void assembler_print_errors ( char * message , char * add_mess ) { printf(" %s %s \n",message,add_mess ); } main() { int error_count, warning_count ; FILE * outfile ; FILE * in_file ; char fn[MAXPATH],outname[MAXPATH], temp[MAXPATH], *ptr; /* Following used to parse a path into its components. */ char drive[MAXDRIVE] , dir[MAXDIR], file[MAXFILE], ext[MAXEXT]; error_count = 0 ; warning_count = 0 ; e_printf = assembler_print_errors ; puts(" Generic Assembler. Version 1.0\n"); puts(" Written by : William E. Ives"); puts(" Copyright (c) 1988 by Michigan Technological University.\n"); printf(" Absolute or Relative assembly ? (A/R) "); fn[0] = getche(); putchar('\n'); am_assem_class = (( fn[0] == 'a')||(fn[0] == 'A')) ? am_absolute : am_relative ; printf(" Enter source file name [.ASM] =>"); fn[0]=MAXPATH-1; ptr=cgets(fn); strcpy(fn,ptr); putchar('\n'); fnsplit( fn,drive,dir,file,ext); /* assign list file name.*/ if ( ! ( *ext ) ) fnmerge( fn, drive,dir,file,".ASM"); fnmerge( outname,drive,dir,file,".LST"); printf(" Enter name of list file [%s] =>",outname); temp[0]=MAXPATH-1; ptr = cgets(temp); putchar('\n'); if ( temp[1] ) strcpy(outname,ptr); in_file = fopen( fn,"r"); if ( in_file == NULL ) { e_message(0,30, fn ); return 30 ; } puts(" Assembling.."); e_hold_messages = TRUE ; am_pass1( in_file , &error_count, &warning_count ); e_hold_messages = FALSE ; fclose(in_file); printf(" Total Errors %d Total Warnings %d \n", error_count, warning_count ); puts(" Writing listing file.\n"); outfile = fopen( outname,"w"); if ( outfile == NULL ) { e_message(0,30, outname ); return 30 ; } l_printlisting( outfile ,TRUE ); fprintf(outfile,"\n Total Errors %d Total Warnings %d \n", error_count, warning_count ); fprintf(outfile,"\n\n Name of file :%s\n",fn); fclose(outfile); return 0 ; } /* main */ [LISTING TWO] /* 68000 Assembly Module. This module contains those procedures needed to handle assembly. */ #include #include #include #include #include "68defs.h" #include "68err.h" #include "68parse.h" #include "68list.h" #include "68assem.h" #include "68symtab.h" #include "68pseudo.h" #include "68instr.h" #define LINELEN 80 #define LOCAL near pascal void p_assem_line ( char * line , char label[MAXSYMLEN] , char command[MAXSYMLEN], p_size_type * size , char * numterms , am_term_type ** termlist ) ; unsigned long int am_location_counter = 0L ; char am_end_found = FALSE ; char am_trunc_lines = TRUE ; /* These globals are used for relative symbol/term resolution by linker*/ unsigned long int am_relbase = 0L ; char am_relknown = FALSE ; /* size of absolute address in words. */ /* 1 - abs short, 2 - abs long. */ char am_abs_address_size = 1; am_term_type * am_term_list_head = NULL , * am_term_list_tail = NULL ; am_assem_type am_assem_class = am_absolute ; /***************************************************************************** Function am_resolve_symbol This function resolves a symbol list if it is possible If the symbol list is resolved it deletes every symbol node but the first one which contains the sum. It also compresses the symbol list as much as possible even if all symbol are not resolved. It compresses relative symbols by maintaining a count of relative symbols. It adds or subtracts from this count according to the operator for the symbol. If when the list is fully compressed, the relative count is not zero, then if the relative base is known, the final sum is computed by adding in relcount*am_relbase else relflag '*' is put into symbol[0] and relcount is put into symbol[1] of the first symbol node. Globals am_relknown : flag indicating that relative symbol base value is known. am_relbase : the known relative base. Variable parameters : symlist : the symbol list. Return value The number of symbols still left unresolved. ****************************************************************************/ int am_resolve_symbol( p_sym_type * symlist ) { register int symcount = 0 ; unsigned long sum = 0L ; char rel , relcount = 0 ; p_sym_type * symbol , * temp , * prev ; temp = NULL ; prev = NULL ; symbol = symlist ; while ( symbol ) { rel = ( symbol->sym[0] == '*' ) ; /* set relative flag.*/ if ( !rel && symbol->sym[0] ) { /* if there is a symbol.*/ symcount++ ; prev = symbol ; symbol = symbol->next ; } else { /* there is a value. perform calculations */ if ( symbol->operator == '+' ) { if ( rel ) relcount += symbol->sym[1] ; sum += symbol->val ; } else { if ( rel ) relcount -= symbol->sym[1] ; sum -= symbol->val ; symbol->operator = '+'; } if ( !temp ){ /* if temp == null */ temp = symbol ; prev = symbol ; symbol = symbol->next ; } else { prev->next = symbol->next ; free(symbol); symbol = prev->next ; } } } /* if there were no unresolved symbols but relatives didn't cancel then*/ if ( !symcount && relcount ) if ( am_relknown ) { sum += ( relcount * am_relbase ) ; /* resolve relative if known.*/ relcount = 0; } else /* set symcount to 1 so that caller knows that chain is not resolved.*/ symcount=1 ; if ( temp ) { temp->val = sum ; temp->operator = '+' ; if ( !relcount ) temp->sym[0] = 0 ; else { temp->sym[0] = '*' ; temp->sym[1] = relcount ; } } if ( prev ) prev->next = NULL ; return symcount ; } /* am_resolve_symbol */ /***************************************************************************** Function am_resolve_term This function resolves a term list if it is possible. It only resolves the terms until any the following conditions occur : - the first term class was am_first_instr_term and the next term is not am_other_instr_term - the terms classes are all am_data_terms and the datatermcount has been reached or am_first_instr_term has been reached. - the next term is NULL Variable parameters : termlist : The term list datatermcount : The number of data terms to resolve if the term class is am_data_term. This parameter is ignored if the first term was am_first_instr_term. Return value The number of terms still left unresolved. ****************************************************************************/ int am_resolve_term ( am_term_type * termlist , char datatermcount ) { register int termcount = 0 ; am_term_type * term ; char okay , count ; term = termlist ; okay = TRUE ; count = 1 ; while ( term && okay ) { if ( am_resolve_symbol(term->symptr ) ) termcount++ ; term = term->next ; count++ ; if ( term ) okay = ( termlist->class == am_first_instr_term && term->class == am_other_instr_term ) || ( termlist->class == am_data_term && term->class == am_data_term && count <= datatermcount ) ; } return termcount ; } /* am_resolve_term */ /***************************************************************************** Function am_delete_terms If the input parameter 'ALL' is set to TRUE (1) then this function frees all terms and associated symbols in a term list until null is encountered. The variable 'termlist' is set to NULL upon completion. If the input parameter 'ALL' is set to FALSE (0) then this function frees only one term and associated symbols. The variable 'termlist' is set to 'termlist->next' upon completion. Input all : boolean flag indicating how many terms to delete ( see above ) Variable parameter termlist : a pointer variable which points to the termlist. *****************************************************************************/ void am_delete_terms ( am_term_type ** termlist , char all ) { am_term_type * term ; p_sym_type * sym ; term = *termlist ; if ( all ) { while ( term = *termlist ) { if ( ((term->modereg >> 8) == 7) && ((term->modereg & 15)==10 ) ) { free(term->symptr); /* free the string. */ term->symptr = NULL ; } while ( sym = term->symptr ) { term->symptr = term->symptr->next ; free(sym) ; } *termlist = term->next ; free(term); } } else { if ( term ) { if ( ((term->modereg >> 8) == 7) && ((term->modereg & 15)==10 ) ) { free(term->symptr); /* free the string. */ term->symptr = NULL ; } while ( sym = term->symptr ) { term->symptr = term->symptr->next ; free(sym) ; } *termlist = term->next ; free(term); } } } /* am_delete_terms */ /***************************************************************************** Procedure am_add_terms_to_list This procedure links the list of terms into the global list of terms. This global list is used for all terms associated with data or instructions which contain forward/external references which are not yet resolved. Note : Term list is implemented as a non-circular doubly linked list. Globals : am_term_list_head : points to the head of the term list. am_term_list_tail : points to the tail of the term list. Variable parameter termlist : A pointer variable which points to the termlist to be linked in. It is set to NULL when all the terms are transfered. *****************************************************************************/ void am_add_terms_to_list ( am_term_type ** termlist ) { am_term_type * term ; if ( ! (*termlist) ) return ; /* if NULL then leave. */ if ( !am_term_list_tail ) { /* list is empty */ am_term_list_head = *termlist ; am_term_list_head->prev = NULL ; } else { am_term_list_tail->next = *termlist ; am_term_list_tail->next->prev = am_term_list_tail ; } for ( term = *termlist ; term ; term = term->next ) /* set tail */ am_term_list_tail = term ; *termlist = NULL; return ; /* return used to avoid compilier warning. */ } /* am_add_terms_to_list */ /***************************************************************************** Procedure am_remove_terms_from_list This procedure removes the links from the global list of terms associated with the passed in termptr . It then deletes the terms by calling am_delete_terms. If the termptr points to a term with class am_first_instr_term then this routine will remove all terms for the instruction. If the termptr points to a term with class am_data_term then this routine will ONLY the ONE term. Note : Term list is implemented as a non-circular doubly linked list. Globals : am_term_list_head : points to the head of the term list. am_term_list_tail : points to the tail of the term list. Variable parameter termlist : A pointer variable which points to the termlist to be removed. It is set to NULL when all the terms are deleted. *****************************************************************************/ void am_remove_terms_from_list ( am_term_type ** termlist ) { am_term_type * term ; char i ; if ( ! (*termlist) ) return ; /* if NULL then leave. */ term = *termlist ; i = 1 ; if ( term->class == am_first_instr_term ) { if ( term->next ) if ( term->next->class == am_other_instr_term ) /* remove both instr terms*/ i = 2 ; } if ( term == am_term_list_head ) am_term_list_head = ( i == 1 ) ? term->next : term->next->next ; else term->prev->next = ( i == 1 ) ? term->next : term->next->next ; if ( ( term == am_term_list_tail ) || ( ( i == 2 ) && ( term->next == am_term_list_tail )) ) am_term_list_tail = term->prev ; else if ( i == 1 ) term->next->prev = term->prev ; else if ( term->next->next ) term->next->next->prev = term->prev ; if ( i == 1 ) term->next = NULL ; else term->next->next = NULL ; am_delete_terms( &term, TRUE ); *termlist = NULL; return ; /* return used to avoid compilier warning. */ } /* am_remove_terms_from_list */ /**************************************************************************** Procedure am_backfill This procedure updates the fields within the opcode. It determines actual post words for entire instruction, and places the opcode and post words into the listing. It expects all terms and symbols to be resolved. Input Parameter : termlist - will be resolved when finished creating final opcode. ***************************************************************************/ void am_backfill( am_term_type * termlist ) { char i,j,k, reg ; am_term_type * term ; unsigned int opcode ; l_line_type * lptr ; lptr = termlist->lineptr ; opcode = termlist->opcode; /* get opcode */ /* write opcode to listing */ l_writetoline(1,opcode,0,lptr); term = termlist ; j = ( term->index == 0 ) ? 2 : 1 ; reg = ( opcode & 7 ) ; for (i=1,k=2 ; i <= j && term ; i++ ) { switch ( reg ) { case 1 : /* abs.l */ l_writetoline(k,term->symptr->val>>16,0,lptr); k++ ; case 0 : /* abs.w */ l_writetoline(k,term->symptr->val & 0xFFFF ,0,lptr); k++ ; break ; } term = term->next ; reg = ((opcode>>9)&7) ; } } /* am_backfill */ /***************************************************************************** Function am_readln This function reads in one source line from a file until the end-of-line or the end-of-file is encountered. - it only read up to 'linelen' number of charcters and discards the rest of the line. - expands TABS to 8 space charcters. - returns a blank flag indicating if the first 'linelen' characters are blank or not. - builds a separate string the same as the first but in uppercase in parameter line1. NOTE : characters within single or double qoutes are not affected. Typical calling method : ( for echo of exact file contents ) while ( readln(in_file,LINELEN-1,line,line1,&blank) != EOF ) printf("%s\n",line); Input in_file : the file to be read. linelen : the max number of characters to be read not counting NULL. Variable line : the line read in. line1 : the line read in converted to uppercase blank : flag indicating whether or not the line is blank. Returns 0 : line read okay. EOF : end of file was encountered. ****************************************************************************/ static int LOCAL am_readln ( FILE * infile, int linelen, char * line , char * line1, char * blank ) { int ch, ch1 , i ; char dqoute, sqoute ; /* flags for tracking double & single quotes */ /* if either flag is true then no conversion of case will take place.*/ dqoute = sqoute = FALSE ; *blank = TRUE ; i = 0 ; while ( ch=ch1=fgetc(infile) ) { if ( ch == '\t' ) { /* expand tabs into 8 spaces */ for ( ch = 0 ; ch < 8 ; ch ++, i++ ) { if ( i < linelen ) { line[i] = line1[i] = ' '; line[i+1] = line1[i+1]= NULL; } } continue ; } if ( !isprint(ch) ) break ; if ( ch == '\'' ) { if ( !dqoute ) sqoute = !sqoute ; } else if ( ch == '"' ) { if ( !sqoute ) dqoute = !dqoute ; } else if (!( dqoute || sqoute )) ch1 = toupper(ch) ; if ( i< linelen ) { line[i]= ch ; line1[i]= ch1 ; if ( *blank ) *blank = ( ch == ' ' ); line[i+1] = line1[i+1] = NULL ; } i++; } if ( i ) return 0 ; return ch ; } /* am_readln */ /***************************************************************************** Function am_pass1 This function assembles an entire file and creates the listing and associted data structures as it does so. Input parameters : in_file : the file containing the source assembly code which has already been opened for reading. ****************************************************************************/ void am_pass1 ( FILE * in_file , int * error_count , int * warning_count ) { int i ; ps_pseudos pseudo_class ; char label[MAXSYMLEN], command[MAXSYMLEN] ; char sizeinwords , blank ; p_size_type size ; p_sym_type * sym ; char numterms ; am_term_type * termlist, *term ; unsigned int opcode ; unsigned int index ; char prev_warnings = 0 ; char line[LINELEN], linehold[LINELEN] ; l_line_type * lptr ; e_error.state = 0 ; e_error.warnings = 0 ; while ( !am_end_found && !e_error.out_of_memory && ( am_readln(in_file,LINELEN-1,line,linehold,&blank) != EOF )) { if ( blank ){ /* skip blank lines */ l_addline( l_neither, 0, "", &lptr ); continue ; } if ( line[0] == '*' || line[0] == ';' ) { /* skip comment lines */ l_addline( l_neither, 0, line, &lptr ); continue ; } *error_count += e_error.state ; /* count up total number of errors */ e_error.state = 0 ; label[0] = 0 ; command[0] = 0 ; size = p_unknown ; numterms = 0 ; sizeinwords= 0 ; termlist = NULL ; p_assem_line ( linehold, label, command, &size, &numterms, &termlist ); if ( e_error.state ) { /* add errors to listing and go on to next line */ l_addline( l_neither, 0, line, &lptr); am_delete_terms(&termlist, TRUE ); l_add_errors(lptr); continue ; /* skip to next source line. */ } /* if command is psuedo then handle it. */ if ( ps_lookup_pseudo ( command, &pseudo_class ) ) { ps_pseudo( label, pseudo_class, numterms, &termlist, line); if ( ( e_error.warnings > prev_warnings ) || e_error.state ) l_add_errors(l_line_head->prev); prev_warnings = e_error.warnings ; continue ; /* skip to next source line. */ } if ( am_location_counter & 1 ) { e_message(0,22,NULL); /* location counter is odd */ l_addline(l_neither, 0, line, &lptr); prev_warnings = e_error.warnings ; l_add_errors(lptr); am_location_counter++; /* adjust counter so this error does not repeat*/ continue ; } /* its either an instruction or an error */ if ( numterms > 2 ) { e_message(0,15,NULL) ; /* too many terms on line.*/ l_addline(l_neither, 0, line, &lptr); prev_warnings = e_error.warnings ; l_add_errors(lptr); continue ; } /* set size and initial opcode. */ i = 1 ; switch ( numterms ) { case 0 : /* make source and dest empty */ i = is_validate ( &size , command, 7, 5, 7, 5, &opcode , &sizeinwords, termlist , &index ); break; case 1 : /* make source empty */ i = is_validate ( &size , command, 7, 5, termlist->modereg >> 8, termlist->modereg & 15, &opcode , &sizeinwords, termlist , &index ); break; case 2 : i = is_validate ( &size , command, termlist->modereg >> 8, termlist->modereg & 15, termlist->next->modereg >> 8, termlist->next->modereg & 15, &opcode , &sizeinwords, termlist , &index ); break; } if ( i ) { /* error occured while validating. attach it to listing and go on to next line */ l_addline(l_neither, 0, line, &lptr); prev_warnings = e_error.warnings ; l_add_errors(lptr); continue ; } /* do assembly if possible */ /* process line label if there is one */ if ( label[0] ) if (sym_add_label_symbol(label,am_location_counter,am_assem_class)){ l_addline(l_neither, 0, line, &lptr); l_add_errors(lptr); prev_warnings = e_error.warnings ; am_delete_terms(&termlist, TRUE) ; continue ; /* skip to next source line. */ } /* resolve already known symbols */ for ( term = termlist ; term ; term = term->next ) for ( sym = term->symptr ; sym ; sym = sym->next ) if ( sym->sym[0] ) sym_add_operand_symbol( sym, term, TRUE ); l_addline( l_firstinstr, sizeinwords, line, &lptr ); if ( e_error.out_of_memory ) { am_delete_terms(&termlist,TRUE); break ; } /* its either an instruction or an error */ if ( e_error.warnings > prev_warnings ) l_add_errors(lptr); prev_warnings = e_error.warnings ; if (termlist) { termlist->lineptr = lptr ; /* hold onto line.*/ if ( termlist->next ) termlist->next->lineptr = lptr ; termlist->index = index ; /* hold onto index.*/ termlist->opcode = opcode ; /* hold onto opcode.*/ } l_writetoline(0,am_location_counter,0,lptr); l_writetoline(sizeinwords,0,3,lptr); /* put '?' in listing.*/ /* are all terms knowm? */ if ( ! am_resolve_term(termlist,0) ) { am_backfill( termlist ); if ( e_error.warnings > prev_warnings ) l_add_errors(lptr); prev_warnings = e_error.warnings ; am_location_counter += ( sizeinwords << 1 ) ; am_delete_terms(&termlist, TRUE) ; } else { am_add_terms_to_list( &termlist ); am_location_counter += ( sizeinwords << 1 ) ; } } if ( e_error.out_of_memory ) { l_add_errors(l_line_head->prev); *error_count += e_error.state ; } /* Make sure all local and global symbols are resolved */ e_error.state = 0 ; *error_count += sym_process_unresolved_locals() ; *warning_count = e_error.warnings ; /* Add symbol table to listing */ sym_add_symtabtolisting(); } /* am_pass1 */ [LISTING THREE] /* 68000 error handler module */ #include #include #include #include "68defs.h" #include "68err.h" struct e_struct e_error = { 0,0,0,0,FALSE, NULL, NULL }; char e_hold_messages = TRUE ; e_printf_type e_printf ; /* Global error list array. */ err_type err_list[MAXERRORS] = {{ 1 , 0,"Error 1. Unexpected end of line." ,NULL,NULL }, { 2 , 0,"Error 2. Unexpected symbol.",NULL,NULL }, { 3 , 0,"Error 3. Unexpected token char.",NULL,NULL }, { 4 , 0,"Error 4. Symbol/Literal contains invalid char.",NULL,NULL }, { 10 , 0,"Error 10. Command op size invalid.",NULL,NULL }, { 11 , 0,"Error 11. Invalid address mode.",NULL,NULL }, { 12 , 0,"Error 12. Unrecognized command.",NULL,NULL }, { 13 , 0,"Error 13. Command operands required.",NULL,NULL}, { 14 , 0,"Error 14. Forward references not allowed here.",NULL,NULL}, { 15 , 0,"Error 15. Too many operands.",NULL,NULL}, { 16 , 0,"Error 16. Line label required.",NULL,NULL}, { 17 , 0,"Error 17. Label found in external list.",NULL,NULL}, { 18 , 0,"Error 18. Label already resolved.",NULL,NULL}, { 19 , 0,"Error 19. Operand symbol already resolved.",NULL,NULL}, { 21 , 0,"Error 21. Address collision.",NULL,NULL}, { 22 , 0,"Error 22. Location counter is odd.",NULL,NULL}, { 23 , 0,"Error 23. Unresolved symbol.",NULL,NULL}, { 30 , 0,"Error 30. File not found or unable to open.",NULL,NULL }, { 31 , 0,"Error 31. Unexpected end of file.",NULL,NULL }, { 33 , 0,"Error 33. Disk full while writing file.",NULL,NULL}, { 41 , 0,"Error 41. Out of Dynamic memory.",NULL,NULL }, { 50 , 0,"Warning 50. Symbol too long. Truncated.",NULL,NULL}, { 52 , 0,"Warning 52. Value out of range.",NULL,NULL}, { 70 , 0,"Warning 70. Expression/Syntax imprecise.",NULL,NULL}, { 71 , 0,"Warning 71. Command op size not specified.",NULL,NULL}, { 72 , 0,"Warning 72. Line label not allowed. Ignored.",NULL,NULL}, { 73 , 0,"Warning 73. Command operands not allowed. Ignored.",NULL,NULL}, { 74 , 0,"Warning 74. Symbol already in global list. Ignored.",NULL,NULL}, { 75 , 0,"Warning 75. Symbol already in external list. Ignored.",NULL,NULL}, { 100, 0,"FSE 100. Invalid return from next_token.",NULL,NULL} }; /*************************************************************************** Function e_message This function looks up the code in the errlist array, retrieves the standard message from the array, and inserts the message into the current errptr list . It puts the additional message in the add_message field. Error code map : 0..49 errors. 50..99 warnings 100.. fatal software errors. **************************************************************************/ void e_message( char pos , char code , char * add_mess) { err_type * temp ; int i ; char * tmp; temp = NULL ; if ( e_hold_messages ) { temp = ( err_type * ) malloc ( sizeof(err_type) ) ; if ( !temp ) code = 41 ; /* change code to that of 'out of memory' */ } i = 0 ; while (( i < MAXERRORS ) && ( err_list[i].code != code )) i ++ ; if ( code == 41 ) e_error.out_of_memory = TRUE ; if ( temp ) { temp->message = ( i <= MAXERRORS ) ? err_list[i].message : NULL ; if ( *add_mess ) temp->add_mess = ( char * ) strdup ( add_mess ) ; else temp->add_mess = NULL ; temp->code = code ; temp->position= pos ; temp->next = NULL ; if ( !e_error.errptr ) { e_error.errptr = temp ; e_error.last = temp ; } else { e_error.last->next = temp ; e_error.last = temp ; } } tmp = ( char * ) ( i < MAXERRORS ) ? err_list[i].message : NULL ; if ( e_error.curpos ) pos += e_error.curpos ; e_printf( tmp, add_mess ); if ( ( code < 50 ) || ( code >= 100 ) ) { e_error.errors++ ; e_error.state++ ; } else e_error.warnings++; } /* e_message */ /*************************************************************************** Procedure e_delete_errors This procedure deletes all the errors currently attached to e_error.errptr . **************************************************************************/ void e_delete_errors() { err_type * temp ; while ( e_error.errptr ) { temp = e_error.errptr->next ; if ( e_error.errptr->add_mess ) free( e_error.errptr->add_mess ) ; free( e_error.errptr ); e_error.errptr = temp ; } } /* e_delete_errors */ [LISTING FOUR] /* 68000 Instruction Set Module. This module contains those procedures needed to handle the instruction set. */ #include #include #include #include "68defs.h" #include "68err.h" #include "68parse.h" #include "68list.h" #include "68assem.h" #include "68instr.h" #define LOCAL near pascal /************************************************************************** is_validate This procedure validates an instruction. It first looks up the instruction mnemonic in the is_array, then determines if the source and destination are valid for the particular instruction. Input parameters : size : size of the operation. i.e ADD.W command : the instruction mnemonic string. Variable parameters opcode : the base operation code determined termlist : updates sizeofreserve field index : the instruction array index for the instruction Return code : 0 : instruction addr mode is valid. otherwise : error occured. Returned in error list. **************************************************************************/ int is_validate ( p_size_type * size , char * command, char smode , char sreg , char dmode , char dreg , unsigned int * opcode , char * total_size , am_term_type * termlist , unsigned int * index ) { /* error if destination is not specified */ if ( (dmode==7) && ( dreg==5 ) ) { e_message(0,11," Destination operand required."); return 11 ; } *total_size = 1 ; /* lookup command */ if ( ! strcmp(command,"MOVE") ) { /* error if source is not specified */ if ( (smode==7) && ( sreg==5 ) ) { e_message(0,11," Source operand required."); return 11 ; } if ( *size == p_unknown ) { /* assign default size if it's unknown */ e_message(0,71," Assumed Long."); *size = p_long ; } termlist->sizeofreserve=am_abs_address_size ; termlist->next->sizeofreserve=am_abs_address_size ; *total_size += ( am_abs_address_size << 1 ); *opcode = (dreg << 9 ) | ( dmode <<6 ) | ( smode <<3 ) | sreg ; *opcode |= ( *size == p_long )? 0x2000: (*size==p_word)? 0x3000:0x1000; *index = 0; } else if ( ! strcmp(command,"JMP") ) { *opcode = 0x4EC0 | ( dmode << 3 ) | dreg ; termlist->sizeofreserve=am_abs_address_size ; *total_size += am_abs_address_size ; *index = 1 ; } else { e_message(0,12,command); return 12 ; } return 0 ; } /* is_validate */ [LISTING FIVE] /* 68000 listing module. */ #include #include #include #include #include #include "68defs.h" #include "68err.h" #include "68list.h" /* These are considered module level variables. */ l_line_type * l_line_head = NULL ; char * l_header = " Generic Assembler Version 1.0 : "; char * l_blanks = " " ; int l_number_of_lines = 0 ; /**************************************************************************** Function l_addline This funciton adds a line at the end of the line listing. It does this according to the class of the line : l_firstinstr : It creates a node for the text of the line to be held in. It concats a blank leader before the text to hold the address and opcode. If there are more than two words in the instruction, then an additional line node in created. l_data : It creates a node,and concats a blank leader onto the text line. Form of instruction line : blanks line text addres | | first-> 000000 0000 0000 0000 Lable command operand text 0--------------------23 other-> 0000 0000 0--------------------23 NOTE : This routine copys the line text, so that the caller may reuse the line, or discard it with out affecting the listing. Input parameters : class : the class of line to be added. numofwords : number of words in the instrcuction if its an instruction line. line : the text of the line to be placed in the listing. Variable parameters : lineptr : pointer to the line which was added to the listing. Return 0 : line added okay. 41 : out of memory **************************************************************************/ int l_addline( l_lclass_type class , char numofwords , char * line , l_line_type ** lineptr ) { l_line_type * curline , * tcurline ; register int i ; long secs ; *lineptr = NULL ; curline = NULL ; curline = ( l_line_type * ) malloc ( sizeof(l_line_type) ) ; if ( !curline ) { e_message(0,41," Listing " ) ; /* out of memory */ return 41 ; } curline->lclass = class ; curline->linenum = ( class != l_firstinstr ) ? 0 :l_number_of_lines++ ; if ( !l_line_head ) { l_line_head = ( l_line_type * ) malloc ( sizeof(l_line_type)); if (!l_line_head){ e_message(0,41 ," Listing " ) ; /* out of memory */ return 41 ; } l_line_head->line = l_header ; time(&secs); strcpy ( ((l_line_head->line)+32), asctime( localtime(&secs) ) ); i = strlen(l_line_head->line); for(;!isprint(l_line_head->line[i]);i--) l_line_head->line[i]=0; l_line_head->lclass = l_data ; l_line_head->linenum = 0 ; l_line_head->next = l_line_head ; l_line_head->prev = l_line_head ; } curline->line = ( char * ) malloc( 25 + strlen(line) ); if ( !(curline->line) ) { e_message(0,41 ," Listing " ) ; /* out of memory */ free(curline); return 41 ; } curline->next = l_line_head ; curline->prev = l_line_head->prev ; l_line_head->prev = curline ; curline->prev->next = curline; if ( class != l_neither ) { memset( (curline->line) ,'0', 6 ); memset( ((curline->line)+6),' ',18 ); } else memset( (curline->line) ,' ',24); strcpy( ((curline->line)+24) ,line); tcurline = curline ; if ( numofwords > 3 ) { /* allocate post lines for either instr or data */ i = (numofwords - 3) / 3 ; if ( (numofwords - 3) % 3 ) i++ ; for ( ; i ; i-- ) { curline = ( l_line_type * ) malloc ( sizeof(l_line_type) ) ; if ( !curline ) { e_message(0,41 , NULL ) ; /* out of memory */ return 41 ; } curline->next = l_line_head ; curline->prev = l_line_head->prev ; l_line_head->prev = curline ; curline->prev->next = curline; curline->line = strdup( l_blanks ); if ( !(curline->line) ) { e_message(0,41 , NULL ) ; /* out of memory */ return 41 ; } curline->lclass = ( class == l_firstinstr ) ? l_otherinstr:l_data ; curline->linenum =( class == l_firstinstr ) ? l_number_of_lines++ : 0 ; } } *lineptr = tcurline ; return 0 ; } /* l_addline */ /**************************************************************************** Function l_writetoline This funciton writes to a line in the listing, assuming it's already been created. It does this by refering to specific elements in the address opcode field by identifiers such as : 0 : write 6 hex degit address field 1 : write the 4 hex degit opcode/data field for word 1 . 2 : write the 4 hex degit opcode/data field for word 2 . etc.. It takes the data to be written ( whether address/data/opcode ) from the unsigned long parameter called : VALUE The option specifies special operations as follows : 0 : write only one number as specified by spec . 1 : write question marks in field for only one field specified by spec. 2 : write repeated number over all post words up to the field specified by spec. ( spec 0 is ignored. ) 3 : write repeated question marks over all post words up to the field specified by spec. ( spec 0 is ignored. ) 4 : write only one byte at location specified by spec. Input parameters : spec : the specification number as described above. value : the value to be written on the line. option : flag indicated if question marks are to be printed. line : pointer to the listing node containing the first line. **************************************************************************/ void l_writetoline( int spec , unsigned long value , char option , l_line_type * line ) { register int i ; char * curline ; if ( !line ) return ; if ( !spec ) { if ( option ) strnset(line->line,'?',6); else { sprintf(line->line,"%06X",value); *(line->line+6) = ' ' ; } } else { i = ( spec - 1 ) / 3 ; for ( ; i ; i-- , line = line->next ) { if ( option == 2 ) { sprintf(line->line+6," %04X %04X %04X",( int ) value, ( int ) value, ( int ) value ); *(line->line+23) = ' '; } else if ( option == 3 ) { sprintf(line->line+6," ???? ???? ????" ); *(line->line+23) = ' '; } } i = ( spec - 1 ) % 3 ; curline = line->line + 9 ; for ( ; i ; i-- , curline += 5 ) { if ( option == 2 ) { sprintf( curline,"%04X",( unsigned int ) value); *(curline+4) = ' '; } else if ( option == 3 ) strnset( curline,'?',4 ); } if ( ( option == 0 ) || ( option == 2 ) ) { sprintf(curline,"%04X", ( unsigned int ) value ) ; *(curline+4)=' '; } else if ( option == 4 ) { sprintf(curline,"%02X", ( unsigned char ) value ); *(curline+2)=' '; } else strnset(curline,'?',4); } } /* l_writetoline */ /**************************************************************************** Function l_add_errors This funciton writes all the error messages attached to e_error.errptr to the listing, then deletes the error list. Input lptr : pointer to the line in the listing after which the errors should be attached. **************************************************************************/ void l_add_errors ( l_line_type * lptr ) { l_line_type * tlptr ; err_type * error ; char relink_list ; char message_buf[100] ; if ( !lptr ) /* if lptr == NULL */ lptr = l_line_head->prev ; /* if lptr at end of list do not bother relinking listing.*/ relink_list = ( lptr->next != l_line_head ) ; for ( error = e_error.errptr ; error ; error = error->next ) { strcpy( message_buf , error->message ) ; strcpy( message_buf+strlen(error->message), error->add_mess ); l_addline(l_neither, 0, message_buf , &tlptr ); if ( relink_list ) { /* unlink the line */ tlptr->next->prev = tlptr->prev ; tlptr->prev->next = tlptr->next ; /* link the line into listing after the source line at lptr */ tlptr->next = lptr->next ; tlptr->next->prev = tlptr ; tlptr->prev = lptr ; lptr->next = tlptr ; } } e_delete_errors() ; } /* l_add_errors */ /**************************************************************************** Function l_printlisting This funciton writes all the listing lines into the text file passed in as outfile. It assumes the file was already opened for text output. It will stop writing if an error occurs while writing. Input outfile : the already opened file that the listing is written to. withheader : TRUE if list header is to be written first. FALSE if list header not to be written at all. Return 0 : okay. 33: error while writing to file. **************************************************************************/ int l_printlisting( FILE * outfile, char withheader ) { l_line_type * curline ; if ( withheader ) fprintf(outfile,"%s\n",l_line_head->line); for ( curline = l_line_head->next ; curline != l_line_head ; curline = curline->next ) { if ( (curline->lclass != l_neither)&&(curline->lclass != l_data) ) { if ( fprintf(outfile,"%3d %s\n",curline->linenum, curline->line) == EOF ) { e_message(0,33,NULL); return 33 ; } } else if ( fprintf(outfile," %s\n", curline->line) == EOF ) { e_message(0,33,NULL); return 33 ; } } return 0 ; } /* l_printlisting */ /**************************************************************************** Function l_delete_listing This funciton deletes the entire listing, and resets all of its associated pointers back to their default values. Input deletehead : TRUE if head is to be deleted too. FALSE if head to be left alone. resetcount : TRUE if l_number_of_lines should be reset. **************************************************************************/ void l_delete_listing( char deletehead , char resetcount ) { l_line_type * curline, * tline ; if ( resetcount ) l_number_of_lines = 0 ; if ( l_line_head ) { l_line_head->prev->next = NULL ; curline = l_line_head->next ; l_line_head->next = l_line_head ; l_line_head->prev = l_line_head ; if ( deletehead ) { free(l_line_head); l_line_head = NULL ; } while ( curline ) { tline = curline->next ; if ( curline->line ) free( curline->line ); free( curline ); curline=tline ; } } } /* l_delete_listing */ [LISTING SIX] /* 68000 math module. */ #include #include #include #include "68defs.h" #include "68err.h" #include "68parse.h" /**************************************************************************** Function strtolong This funciton converts a string of base to a long integer. It behaves the way that STRTOL is supposed to. Example num = strtolong("101", &endptr, 2); gives num = 5 ; Input parameters : str : the string of valid numberic ascii characters endptr : if no error points to end of string . if error it points to error location in string. base : the base of the string : either 2, 8, 16, or 10 **************************************************************************/ unsigned long int strtolong ( register char * str , char ** endptr , char base ) { unsigned long int sum = 0L ; register char shift ; switch ( base ) { case 10 : for (; *str ; str++) if ( isdigit(*str) ) sum = ( sum * 10 ) + ( *str - '0' ) ; else { *endptr = str ; return 0 ; } *endptr = str ; return sum ; case 2 : shift = 1 ; break ; case 8 : shift = 3 ; break ; case 16 : shift = 4 ; break ; } for (; *str ; str++ ) if ( isdigit ( *str ) && ( ( base == 10 ) || ( base == 16 ) || ( (base == 2) && ( *str == '0' || *str == '1' ) ) || ( (base == 8) && ( *str <= '7' && *str >= '0' ) ) ) ) sum = ( sum << shift ) | ( *str - '0' ) ; else if ( isxdigit (*str) && ( base == 16 ) ) sum = ( sum << shift ) | ( toupper(*str) - 'A' + 10 ) ; else { *endptr = str ; return 0 ; } *endptr = str ; return sum ; } /* strtolong */ /*************************************************************************** Function m_symtoval This function converts a valid numerical symbol string to its corresponding long value. It follows the format below : All symbols which start with an 0..9,%,@,$," are numerical symbols. All others are ordinary symbols. Numeric $ddd -Hex %ddd - Binary @ddd - Octal ddd - Decimal daaH dddB dddO dddD dddQ '4444' - Quoted literal of max 4 Ascii characters. 0 : a valid number is returned 4 : an invalid char was found in symbol otherwise its not a valid number, although it may be a valid symbol. ***************************************************************************/ int m_symtoval( char * sym , unsigned long int * value ) { char symbol[MAXSYMLEN] ; char ch , * last , base ; strncpy( symbol, sym, MAXSYMLEN ); /* make a local pass by value symbol*/ symbol[MAXSYMLEN-1] = 0 ; last = symbol+strlen(symbol)-1 ; *value = 0L ; if ( isdigit( ch=*symbol ) ) switch ( toupper( *last ) ) { case 'H' : *last = 0 ; base = 16 ; break ; case 'B' : *last = 0 ; base = 2 ; break ; case 'Q' : case 'O' : *last = 0 ; base = 8 ; break ; case 'D' : *last = 0 ; default : base = 10 ; break ; } else { switch ( ch ) { case '$' : symbol[0] = '0' ; base = 16 ; break ; case '@' : symbol[0] = '0' ; base = 8 ; break ; case '%' : symbol[0] = '0' ; base = 2 ; break ; case '\'': /* scan line until next ' */ last = strchr( symbol+1, '\''); if ( !(*last) ) { e_message(0,51,"End quote expected."); last = symbol+5 ; } *last = 0 ; if ( last - symbol > 5 ) { e_message(0,51,NULL); symbol[5] = 0 ; } for ( last = symbol+1 ; *last ; last++ ) *value = ( *value << 8 ) | *last ; return 0 ; default : return 1 ; } } *value = ( unsigned long int ) strtolong( symbol, &last , base ); if ( *last ) e_message(0,4,NULL); return ( *last ) ? 4 : 0 ; } /* m_symtoval */ [LISTING SEVEN] /* 68000 parser module. */ #include #include #include #include #include "68defs.h" #include "68err.h" #include "68parse.h" #include "68list.h" #include "68assem.h" #include "68math.h" #define LOCAL near pascal typedef enum { symbol, token, none } p_tok_type ; typedef enum { pn_sym , /* sym */ pn_empty , /* empty parse node. */ pn_error /* error */ } p_addr_type ; typedef struct { p_addr_type p_nodeclass;/* tag field */ char regnum ; p_sym_type * symptr ; /* symbol list, if any. */ } p_node_type ; char symchars[38] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_" ; p_sym_type curtoksym ; /* current token symbol from next_token */ char curtok ; /* current token character from next_token*/ /* Function prototypes for local functions */ static p_tok_type LOCAL next_token( char * line , int * line_offset , int line_len ); static int LOCAL p_symbol ( char * line , int * line_offset , int line_len , char symknown , char preop , p_sym_type ** symptr , p_node_type * p_node ); static p_size_type LOCAL p_dotsize ( char * line , int * line_offset , int line_len , int prev_offset ); static void LOCAL p_parse_line ( char * line , int * line_offset , int line_len , p_node_type * p_node , char * done ); /*************************************************************************** Function next_token - get next token This function returns the next token in the text line from the current line offset. If rest of line is blank, or if line offset is at end of line, then it returns an end of line indicator. Input parameters : line - the line to be parsed. line_offset - the current offset into the line. line_len - the line length. ( so as to avoid calling strlen ) Calls : p_symtype : to reclassify symbols . Warnings : e_message called as warnings occur. 50 : Symbol too long Globals : curtoksym curtok Return value : symbol : symbol found. Is located in curtoksym. token : token found. Token character is in curtok. none : end of line found. ****************************************************************************/ static p_tok_type LOCAL next_token( char * line , int * line_offset , int line_len ) { char * tokpos, * tline ; int symlen ; (*line_offset) += strspn( line + *line_offset , " \t"); /* skip blanks */ if ( *line_offset >= line_len ) return none ; tline = line + *line_offset ; if ( *tline == '\'') { tokpos = strchr( tline+1, '\''); /* scan for next quote.*/ if ( *tokpos ) /* if found set tok one further. */ tokpos++ ; /* if not , set to shortest of */ else /* (5 forward or the end) */ tokpos = ( tokpos - tline > 5 ) ? tline + 5 : tokpos ; } else tokpos = strpbrk( tline, ".;-+()#/, \"\t"); if ( tokpos != tline ) { /* return symbol between start of line and tokpos. */ if ( !(*tokpos) ) tokpos = line + line_len ; /* correct for NULL */ symlen = tokpos - tline ; (*line_offset) += symlen; if ( symlen >= MAXSYMLEN ) e_message(*line_offset,50,NULL); symlen = ( symlen >= MAXSYMLEN ) ? MAXSYMLEN - 1 : symlen ; strncpy( curtoksym.sym, tline, symlen ) ; curtoksym.sym[symlen] = 0 ; if ( isalpha(curtoksym.sym[0] ) ) if ( strspn( curtoksym.sym, symchars ) == symlen ) return symbol ; else { e_message(*line_offset,4,NULL); return symbol ; } e_error.curpos = *line_offset ; if ( ( !m_symtoval(curtoksym.sym,&curtoksym.val ) ) && ( !e_error.state ) ) curtoksym.sym[0] = 0 ; e_error.curpos = 0 ; return symbol ; } else { /* the token is at the start of the line. */ curtok = *tline ; (* line_offset ) ++ ; return token ; } } /* next_token */ /*************************************************************************** Function p_symbol - parse symbol This function parses a symbol into its components following the diagram : symbol --> operator ---> ^ | |-----------------| symbol - any valid non-key word of SYMMAXLEN length or less. operator - plus '+' minus '-' If an unrecognized operator is encountered, the symbol chain is assumed to have come to an end. The last token is then un-read, so that it can be re-read by subsequent parse code. Input parameters : line - the line to be parsed. line_offset - the current offset into the line. line_len - the line length. ( so as to avoid calling strlen ) symknown - flag ( TRUE or FALSE ) which indicates whether the initial symbol was already parsed. Note : calls e_message to print errors as it recurses up. Warnings : e_message called as warnings occur. 51 : literal too long Return code : 0 : okay 1 : Unexpected eoln 2 : Unexpected symbol. 3 : Unexpected token char. 41 : Out of dynamic memory. 100 : Invalid return from next token. ****************************************************************************/ static int LOCAL p_symbol ( char * line , int * line_offset , int line_len , char symknown , char preop , p_sym_type ** symptr , p_node_type * p_node ) { p_tok_type tok ; int i ; if ( symknown ) tok = symbol ; else tok = next_token( line , line_offset , line_len) ; switch ( tok ) { case symbol : /* Symbol was found */ *symptr = ( p_sym_type * ) malloc ( sizeof(p_sym_type)); if ( ! (*symptr) ) { /* if *symptr == NULL */ e_message(0,41," Parser "); return 41 ; } memcpy( (*symptr) , &curtoksym, sizeof(p_sym_type) ); (*symptr)->operator = preop ; (*symptr)->next = NULL ; i = *line_offset ; /* hold onto line offset */ tok = next_token( line , line_offset , line_len); switch ( tok ) { case token : switch ( curtok ) { case '+' : case '-' : i = p_symbol( line, line_offset, line_len, FALSE, curtok, &( (*symptr)->next ), p_node); return i ; default : /* un-get the token. */ *line_offset = i ; return 0; } case symbol : e_message(*line_offset,2,NULL); return 2 ; case none : return 0 ; default : e_message(*line_offset,100,"p_symbol"); return 100 ; } case token : e_message(*line_offset,3,NULL); return 3 ; case none : e_message(*line_offset,1,NULL); return 1 ; default : e_message(*line_offset,100,"p_symbol"); return 100 ; } } /* p_symbol */ /***************************************************************************** Function p_dotsize This function parses a dot size field and returns the size as a type. It expects that the current token is a token/delemeter and not a symbol If the curtok is not a '.' it unreads it and returns p_unknown. If the curtok is a '.' it follows : .B -- returns p_byte .W -- returns p_word .L -- returns p_long . anything else -- returns p_unknown, and unreads the last token excluding the period. It also prints out the warning that the period was ignored. ****************************************************************************/ static p_size_type LOCAL p_dotsize ( char * line , int * line_offset , int line_len , int prev_offset ) { p_tok_type tok ; if ( curtok == '.' ) { prev_offset = *line_offset ; tok = next_token( line , line_offset, line_len); if ( (tok == symbol )&& ( curtoksym.sym[1] == NULL ) ) switch ( curtoksym.sym[0] ) { case 'B': return p_byte ; case 'W': return p_word ; case 'L': return p_long ; } e_message(prev_offset,70," '.' ignored."); *line_offset = prev_offset ; return p_unknown ; } else { *line_offset = prev_offset ; return p_unknown ; } } /* p_dotsize */ /***************************************************************************** Function parse_line - parse line This function parses a line into its components following the diagram : <---------------------------feedback------------| | | | symbol ---> p_symbol ------------------>','--| |---->';'----> |---->eoln---> |---->error--> symbol - any valid key word of SYMMAXLEN length or less. string literal - quoted string. "example of string." eoln - end of line reached error - error in parsing Input parameters : line - the line to be parsed. ****************************************************************************/ static void LOCAL p_parse_line ( char * line , int * line_offset , int line_len , p_node_type * p_node , char * done ) { p_tok_type tok ; tok = next_token( line , line_offset , line_len) ; p_node->p_nodeclass = pn_empty ; p_node->symptr = NULL ; switch ( tok ) { case symbol : p_node->p_nodeclass = pn_sym ; p_symbol(line, line_offset, line_len , TRUE, '+',&(p_node->symptr),p_node ); break; case token : switch ( curtok ) { case ';' : *done = TRUE ; /* Comment found */ break; default : e_message(*line_offset,3,NULL); *done = TRUE ; break; } break ; case none : *done = TRUE ; /* End of line found */ break ; default : e_message(*line_offset,100,NULL); } *done = ( e_error.state ) ? TRUE:*done ; if ( ! *done ) { tok = next_token( line , line_offset , line_len) ; switch ( tok ) { case symbol : e_message(*line_offset,2,curtoksym.sym); /* Unexpected symbol */ *done = TRUE ; break ; case token : switch ( curtok ) { case ',' : break; case ';' : *done = TRUE ; /* Comment found */ break; default : e_message(*line_offset,3," ',' or ; exp."); *done = TRUE ; break; } break ; case none : *done = TRUE ; /* End of line found */ break ; default : e_message(*line_offset,100,NULL); *done = TRUE ; } } if ( e_error.state ) p_node->p_nodeclass = pn_error ; } /* p_parse_line */ /***************************************************************************** Function p_assem_line This function returns parses one line into a list of terms. Input parameters : line : the line of source text to be parsed. Variable parameters label : the label found for the line. command : the command found on the line. size : the size specification of the command. ie. MOVE.B is p_byte. numterms : the number of terms parsed. termlist : the term list in order of parsing ****************************************************************************/ void p_assem_line ( char * line , char label[MAXSYMLEN] , char command[MAXSYMLEN], p_size_type * size , char * numterms , am_term_type ** termlist ) { register int i ; int line_offset = 0, line_len; char done , mode, reg ; am_term_type * term , * prev ; p_tok_type tok ; p_node_type p_node ; /* current parse node. */ e_error.state = 0 ; /* clear the parameters */ label[0] = 0 ; command[0] = 0 ; *size = p_unknown ; *numterms = 0 ; *termlist = NULL ; line_len = strlen(line) ; /* read the line label */ if ( isalpha( line[0] ) ) { /* scan the line to remove the label string. */ tok = next_token( line , &line_offset , line_len) ; if (tok == symbol) strncpy( label,curtoksym.sym,MAXSYMLEN ); } /* read the command */ i = line_offset ; tok = next_token( line , &line_offset , line_len) ; switch ( tok ) { case symbol : strncpy(command,curtoksym.sym,MAXSYMLEN); /* check for size identifier */ i = line_offset ; tok = next_token( line , &line_offset , line_len) ; switch ( tok ) { case symbol : line_offset = i ; break ; case token : *size = p_dotsize(line,&line_offset, line_len,i); break; case none : line_offset = i ; break ; } break ; case token : case none : line_offset = i; break ; } i = 0 ; done = FALSE ; while ( ! done ) { p_parse_line( line, &line_offset,line_len,&p_node,&done); if ( e_error.state ) break ; if ( p_node.p_nodeclass == pn_empty ) break ; switch ( p_node.p_nodeclass ) { case pn_sym : mode = 7 ; reg = ( am_abs_address_size == 1 )?0:1 ; break ; /* sym */ case pn_empty : mode = 7 ; reg = 5 ; break ; /* empty/none*/ case pn_error : mode = 7 ; reg = 11 ; break ; /* error */ } term = ( am_term_type * ) malloc ( sizeof( am_term_type ) ) ; if ( !term ) { e_message(0,41,"Parser"); /* not enough memory */ break ; } term->modereg = ( mode << 8 ) | reg ; term->symptr = p_node.symptr ; term->next = NULL ; term->prev = NULL ; term->sizeofreserve = 0 ; term->postword = 0 ; term->class = am_other_instr_term ; (*numterms)++ ; if ( !(*termlist) ) /* if termlist = NULL */ *termlist = term ; else { term->prev = prev ; prev->next = term ; } prev = term ; } /* while */ if ( *termlist ) (*termlist)->class = am_first_instr_term ; } /* p_assem_line */ [LISTING EIGHT] /* 68000 Pseudo Module. This module contains those procedures needed to handle psuedo command assembly. */ #include #include #include #include "68defs.h" #include "68err.h" #include "68parse.h" #include "68list.h" #include "68assem.h" #include "68symtab.h" #include "68pseudo.h" typedef struct { char pseudo[10]; ps_pseudos index ; } ps_pseudo_type ; ps_pseudo_type ps_pseudo_array [7] = { { "ABS_LONG" , ps_abslong } , { "ABS_SHORT" , ps_absshort} , { "END" , ps_end } , { "EQU" , ps_equate } , { "EXTERN" , ps_extern } , { "GLB" , ps_global } , { "ORG" , ps_origin } } ; /***************************************************************************** Function ps_lookup_pseudo This function looks up a pseudo command in the pseudo array and returns the psuedo class type if it is a valid pseudo command. Input parameter : pseudo : the pseudo command being looked up. Variable parameter : pseudo_class : the class which is returned. Return code 0 : pseudo not found. 1 : pseudo found. ****************************************************************************/ int ps_lookup_pseudo ( char * pseudo , ps_pseudos * pseudo_class ) { ps_pseudo_type * indx ; if ( indx = ( ps_pseudo_type * ) bsearch( pseudo, ps_pseudo_array, 7, sizeof(ps_pseudo_type),strcmp) ){ *pseudo_class = indx->index ; return 1 ; } else return 0 ; } /* ps_lookup_pseudo */ /***************************************************************************** Function ps_one_symbol_only This function returns true if the symbol list contains only one symbol, and no literals. Otherwise, it returns false. For an empty list it returns false. Used by am_pseudo for EXTERNAL and GLOBAL operand validation. Input parameter : symlist : the symbol list ****************************************************************************/ int ps_one_symbol_only ( p_sym_type * symlist ) { if ( symlist ) if ( symlist->next ) return FALSE ; else if ( symlist->sym[0] ) return TRUE ; else return FALSE ; else return FALSE ; } /* ps_one_symbol_only */ /***************************************************************************** Function ps_validate_pseudo This function validates the pseudo commands depending on the parameters passed in as listed below : Note : each action is based on a TRUE value for the variable. locationeven : If location counter not even then error. onetermonly : If number of terms not equal to one then an error results. zeroterms : If number of terms >= 1 then error. labelrequired : If there is no label then error. ignorlable : If there is a label then warning. forwardsallowed : If symbols are forward referenced then no error else error stringsallowed : If a term is found which is a string then no error is issued. onesymbolonly : Each term can only have one unresolved symbol in it or an error will result. Input parameters : label : pointer to label found on current line. numterms : the number of terms in the termlist. Variable parameter termlist : Pointer to the term list. The termlist is deleted in the event of an error. Note : On any error, the termlist is completely deleted, and the appropriate error message is in the error list created by e_message. Return code 0 : validated. other : error in validation. ****************************************************************************/ int ps_validate_pseudo ( char * label , char numterms , am_term_type ** termlist , char onetermonly , char zeroterms , char forwardsallowed, char labelrequired , char ignorlabel , char stringsallowed , char onesymbolonly ) { am_term_type * term ; p_sym_type * sym ; int i , mode, reg ; if ( ( onetermonly && ( numterms > 1 )) || ( zeroterms && ( numterms )) ) { e_message(0,15,NULL) ; /* requires one operand */ i = 15 ; goto deleteterms ; } if ( onetermonly && ( numterms == 0 ) ) { e_message(0,13,NULL) ; /* requires at least one operand */ return 13 ; } if ( labelrequired && !(*label) ) { e_message(0,16,NULL) ; /* label required */ i = 16 ; goto deleteterms ; } if ( ignorlabel && *label ) e_message(0,72,NULL ) ; /* label ignored. */ if ( numterms ) /* validate each term */ for ( term = *termlist ; term ; term= term->next ) { mode = term->modereg >> 8 ; reg = term->modereg & 15 ; if ( ( mode == 7 ) && ( reg <= 1 ) ) { if ( onesymbolonly ) { if ( ! ps_one_symbol_only(term->symptr) ) { e_message(0,11,NULL) ; /* illegal term */ i = 11 ; goto deleteterms ; } sym_add_operand_symbol( term->symptr, term, FALSE ); if ( ( term->symptr->sym[0] == '*' ) || ( !term->symptr->sym[0] )) { e_message(0,19,NULL); i = 19 ; goto deleteterms ; } } else { for ( sym = term->symptr ; sym ; sym = sym->next ) { if ( sym->sym[0] ) sym_add_operand_symbol( sym, term, FALSE ); } /* compress symbol chain and hold onto number of unresolved */ /* symbols in postword */ term->postword = am_resolve_symbol(term->symptr) ; if ( ( ! forwardsallowed ) && ( term->postword ) ) { e_message(0,14,NULL) ; /* cannot forward reference */ i = 14 ; goto deleteterms ; } } } else { if ( ! ( stringsallowed && ( mode == 7 ) && ( reg == 10 ) )) { e_message(0,11,NULL) ; /* illegal term */ i = 11 ; goto deleteterms ; } } } return 0 ; deleteterms : /* label for exit with deletion of terms */ /* i will contain the error code returned*/ if ( numterms ) /* delete all terms */ am_delete_terms( termlist, TRUE ); return i ; } /* ps_validate_pseudo */ /***************************************************************************** Function ps_pseudo This function handles all the pseudo commands for the assembler. Input parameters : label : pointer to label found on current line. index : the pseudo index size : the size specification of the pseudo command. ( ie .B ) numterms : the number of terms in the termlist. Variable parameter termlist : Pointer to the term list. The termlist is deleted except for those terms which are not resolved for the particular pseudos which allow forward references. Globals : am_location_counter : updated when pseudo requires it. am_end_found : set to TRUE if 'end' pseudo is encountered. am_abs_address_size : set to 1 for abs_short, 2 for abs_long. Note : On any error, the termlist is completely deleted, and the appropriate error message is in the error list created by e_message. Return code 0 : pseudo handled okay. other : error occured. ****************************************************************************/ int ps_pseudo ( char * label , ps_pseudos index , char numterms , am_term_type ** termlist , char * line ) { am_term_type * term ; int i ; l_line_type * lptr ; switch ( index ) { case ps_abslong : case ps_absshort : case ps_even : case ps_end : l_addline( l_neither , 0, line, &lptr); /* add line to listing */ if ( *label ) e_message(0,72,NULL) ; /* label ignored. */ if ( numterms ) e_message(0,73,NULL) ; /* operands ignored. */ switch ( index ) { case ps_abslong : am_abs_address_size = 2 ; break ; case ps_absshort : am_abs_address_size = 1 ; break ; case ps_even : if ( am_location_counter & 1 ) am_location_counter++ ; break; case ps_end : am_end_found = TRUE ; break ; } am_delete_terms( termlist, TRUE ) ; return 0 ; case ps_equate : l_addline(l_neither,0,line,&lptr) ; /* requires one term only. label required. */ if ( i = ps_validate_pseudo ( label, numterms, termlist, TRUE,FALSE,FALSE,TRUE,FALSE,FALSE,FALSE)) return i ; /* add label and value to symbol table */ i = sym_add_label_symbol( label, (*termlist)->symptr->val, am_absolute ) ; am_delete_terms( termlist, TRUE ) ; return i ; case ps_extern : l_addline(l_neither,0,line,&lptr) ; /* one or more terms. label ignored. one symbol per each term */ if ( i = ps_validate_pseudo ( label, numterms, termlist, FALSE,FALSE,FALSE,FALSE,TRUE,FALSE,TRUE)) return i ; /* Add each symbol to the external symbol list. */ for ( term = *termlist ; term ; term = term->next ) if ( i = sym_add_extern(term->symptr->sym) ) { am_delete_terms( termlist, TRUE ); return i ; } am_delete_terms( termlist, TRUE ); return 0 ; case ps_global : l_addline(l_neither,0,line,&lptr) ; /* one or more terms. label ignored. one symbol per each term */ if ( i = ps_validate_pseudo ( label, numterms, termlist, FALSE,FALSE,FALSE,FALSE,TRUE,FALSE,TRUE)) return i ; /* Add each symbol to the global symbol list. */ for ( term = *termlist ; term ; term = term->next ) if ( i = sym_add_global(term->symptr->sym) ) { am_delete_terms( termlist, TRUE ); return i ; } am_delete_terms( termlist, TRUE ); return 0 ; case ps_origin : l_addline(l_neither,0,line,&lptr) ; /* requires one term only. label ignored. */ if ( i = ps_validate_pseudo ( label, numterms, termlist, TRUE,FALSE,FALSE,FALSE,TRUE,FALSE,FALSE)) return i ; am_location_counter = (*termlist)->symptr->val ; am_delete_terms( termlist, TRUE ); return 0 ; } return 0 ; } /* ps_pseudo */ [LISTING NINE] /* 68000 Symbol table Module. This module contains those procedures needed to handle the symbol table. */ #include #include #include #include "68defs.h" #include "68err.h" #include "68parse.h" #include "68list.h" #include "68assem.h" #include "68symtab.h" /* Module level variable */ sym_label_type * sym_glb_lab_head = NULL ; sym_label_type * sym_local_lab_head = NULL ; sym_operand_type * sym_ext_ref_head = NULL ; sym_operand_type * sym_local_ref_head = NULL ; /* Local prototypes */ sym_operand_type * sym_lookup_extern( char * symbol ); sym_label_type * sym_lookup_local ( char * symbol ); int sym_addtolocaloplist ( p_sym_type * symptr , am_term_type * termptr ) ; /***************************************************************************** Function sym_add_symtabtolisting This function is meant to be called at the end of assembly. It adds the symbol tables to the listing if there are any global, external, or local symbols . The symbol table is of the following form : Symbol Value Class ---------- ------ ------ MYSYMBOL 000000 Global Relative MYSYMBOL1 00FFFF Global Absolute MYSYMBOL2 ?????? Global Unknown MYSYMBOL3 001ABC Local Relative MYSYMBOL4 ?????? Extern Unknown Calls l_addline : to add the text lines to the listing. ****************************************************************************/ void sym_add_symtabtolisting() { register sym_label_type * labptr ; sym_operand_type * refptr ; l_line_type * lptr ; char * message_buf , * chptr , count ; int i ; if ( sym_glb_lab_head || sym_local_lab_head || sym_ext_ref_head ) { l_addline(l_neither, 0,"",&lptr); l_addline(l_neither, 0, " Symbol Value Class ",&lptr); l_addline(l_neither, 0, " ---------------- ------ -------",&lptr); } else return ; message_buf = " " ; for (count = 1 ; count <= 2 ; count++ ) { if ( count == 1 ) { strncpy(message_buf+33,"Global ",7); labptr = sym_glb_lab_head ; } else { strncpy(message_buf+33,"Local ",7); labptr = sym_local_lab_head ; } for ( ; labptr ; labptr= labptr->next ) { i = strlen( labptr->symbol ); strncpy( message_buf + 1, labptr->symbol, i ); for ( chptr = message_buf + i + 1 ; i < MAXSYMLEN ; i++, chptr++ ) *chptr = ' ' ; if ( labptr->relative == '?' ) { strncpy( message_buf + 23 ,"??????",6 ); strncpy( message_buf + 43," Unknown ", 10 ); } else { sprintf( message_buf+ 23 ,"%06lX",labptr->val ); message_buf[29] = ' ' ; if ( labptr->relative == '*' ) strncpy( message_buf + 43," Relative ", 10 ); else strncpy( message_buf + 43 ," Absolute ", 10 ); } l_addline(l_neither, 0, message_buf , &lptr ); } } strncpy( message_buf + 23, "??????",6 ); strncpy( message_buf + 33, "Extern ",7); strncpy( message_buf + 43, " Unknown ", 10 ); for ( refptr = sym_ext_ref_head ; refptr ; refptr= refptr->next ) { i = strlen( refptr->symbol ); strncpy( message_buf + 1, refptr->symbol, i ); for ( chptr = message_buf + i + 1 ; i < MAXSYMLEN ; i++, chptr++ ) *chptr = ' ' ; l_addline(l_neither, 0, message_buf , &lptr ); } } /* sym_add_symtabtolisting */ /***************************************************************************** Function sym_process_unresolved_locals This function is meant to be called at the end of assembly. It checks the local reference list to see if any unresolved symbols are left in it. If there are , it generates an unresolved symbol error for each symbol, then deletes the local reference list. It also manually places the errors into the listing following the lines where the unresolved symbol was referenced, as well as calls l_addline to add the error message(s) at the end of the listing. Returns the total number of unresolved symbols. ****************************************************************************/ int sym_process_unresolved_locals() { int i ; sym_ref_type * reflistptr ; sym_operand_type * refptr ; sym_label_type * glbptr ; l_line_type * lptr , * temp_lptr; err_type * error ; char message_buf[100] ; i = 0 ; /* Process global label list first */ for ( glbptr = sym_glb_lab_head ; glbptr ; glbptr= glbptr->next ) { if ( glbptr->relative != '?' ) /* if its known then go to next one */ continue ; e_message(0,23, NULL ); i ++ ; error = e_error.errptr ; strcpy( message_buf , error->message ) ; strcpy( message_buf+strlen(error->message), glbptr->symbol ); l_addline(l_neither, 0, message_buf , &lptr ); e_delete_errors() ; } /* Process the local reference list. */ for ( refptr = sym_local_ref_head ; refptr ; refptr= refptr->next ) { e_message(0,23, refptr->symbol ); i ++ ; error = e_error.errptr ; strcpy( message_buf , error->message ) ; strcpy( message_buf+strlen(error->message), refptr->symbol ); l_addline(l_neither, 0, message_buf , &lptr ); l_addline(l_neither, 0, message_buf , &lptr ); /* unlink the second redundant line */ lptr->next->prev = lptr->prev ; lptr->prev->next = lptr->next ; /* link the second line into listing where symbol was first referenced */ temp_lptr = refptr->list->termptr->lineptr ; lptr->next = temp_lptr->next ; lptr->next->prev = lptr ; lptr->prev = temp_lptr ; temp_lptr->next = lptr ; e_delete_errors() ; } /* delete the local reference list */ while ( refptr = sym_local_ref_head ) { while ( reflistptr = refptr->list ) { refptr->list = refptr->list->next ; free( reflistptr ); } sym_local_ref_head = refptr->next ; free( refptr ); } return i ; } /* sym_process_unresolved_locals */ /***************************************************************************** Function sym_delete_all_tables This function deletes all the symbol tables. Globals sym_glb_label_head, sym_local_lab_head sym_ext_ref_head , sym_local_ref_head : all set to NULL when the tables are deleted. ****************************************************************************/ void sym_delete_all_tables( void ) { sym_operand_type * refptr ; sym_label_type * labptr ; sym_ref_type * reflistptr ; /* delete the global label list */ while ( labptr = sym_glb_lab_head ) { sym_glb_lab_head = labptr->next ; free( labptr ); } /* delete the local label list */ while ( labptr = sym_local_lab_head ) { sym_local_lab_head = labptr->next ; free( labptr ); } /* delete the local reference list */ while ( refptr = sym_local_ref_head ) { while ( reflistptr = refptr->list ) { refptr->list = refptr->list->next ; free( reflistptr ); } sym_local_ref_head = refptr->next ; free( refptr ); } /* delete the external reference list */ while ( refptr = sym_ext_ref_head ) { while ( reflistptr = refptr->list ) { refptr->list = refptr->list->next ; free( reflistptr ); } sym_ext_ref_head = refptr->next ; free( refptr ); } } /* sym_delete_all_tables */ /***************************************************************************** Function sym_resolve_back This function resolves all back references of a particular label symbol passed to it. Input : symptr : pointer to label symbol node with value already resolved. ****************************************************************************/ void sym_resolve_back( sym_label_type * symptr ) { unsigned int i ; sym_operand_type * temp, * prev ; sym_ref_type * refptr , * refhead , * tempref ; p_sym_type * sym ; am_term_type * termptr ; int warnings ; prev = NULL ; for ( temp = sym_local_ref_head ; temp && strncmp(temp->symbol,symptr->symbol, MAXSYMLEN ) ; prev = temp , temp = temp->next ); if ( !temp ) return ; /* if there are back references */ if ( prev ) /* remove the operand node from list.*/ prev->next = temp->next ; else sym_local_ref_head = temp->next ; refhead = temp->list ; free( temp ) ; refptr = refhead ; while ( refptr ) { termptr = refptr->termptr ; for ( sym = termptr->symptr ; sym ; sym= sym->next ) if ( ! strncmp( sym->sym, symptr->symbol, MAXSYMLEN ) ) { /* if the symbol is in the symbol list of the term */ /* then resolve it. */ sym->val = symptr->val ; sym->sym[0] = symptr->relative ; sym->sym[1] = 1 ; /* for relative '*' put in count after it */ } refptr = refptr->next ; } /* compress ref list so that only unique termlists are refered to. */ refptr = refhead ; while ( refptr ) { termptr = refptr->termptr ; if ( termptr ) switch ( termptr->class ) { case am_first_instr_term : /* get rid of any term pointers which refer to the */ /* next term if its class is am_other_instr */ /* this works since instructions can have only 2 terms */ if ( termptr->next ) if ( termptr->next->class == am_other_instr_term ) { for ( tempref = refhead; tempref ; tempref=tempref->next) if ( tempref->termptr == termptr->next ) tempref->termptr = NULL ; } break ; case am_other_instr_term : /* back up to first_instr_term and do same as in case above*/ if ( termptr->prev ) /* there should always be a prev term */ if ( termptr->prev->class == am_first_instr_term ) { for ( tempref = refhead; tempref ; tempref=tempref->next) if ( tempref->termptr == termptr->prev ) tempref->termptr = NULL ; } refptr->termptr = termptr->prev ; /* set ptr to first term */ break ; case am_data_term : /* resolve only one data term at a time. no compression */ break; } refptr = refptr->next ; } /* resolve terms and dispose of reference list. */ refptr = refhead ; while ( refptr ) { termptr = refptr->termptr ; tempref = refptr->next ; free(refptr); refptr = tempref ; if ( !termptr ) continue ; i=am_resolve_term( termptr,( termptr->class == am_data_term)?1:0); if ( !i ) { /* if all resolved */ warnings = e_error.warnings ; am_backfill( termptr ); if ( e_error.warnings > warnings ) l_add_errors( termptr->lineptr ); warnings = e_error.warnings ; am_remove_terms_from_list(&termptr) ; } } } /* sym_resolve_back */ /***************************************************************************** Function sym_lookup_global This function looks up a symbol in the global label list. If the symbol is found, it returns a pointer to its label node, otherwise it returns NULL. Input : symbol : pointer to global symbol string. Returns : described above. ****************************************************************************/ sym_label_type * sym_lookup_global( char * symbol ) { sym_label_type * temp ; for ( temp = sym_glb_lab_head ; temp && strncmp(temp->symbol,symbol, MAXSYMLEN ) ; temp = temp->next ); return temp ; } /* sym_lookup_global */ /***************************************************************************** Function sym_add_global This function will add a symbol to the global list if the symbol is not already in the global list. The symbol string is copied into the label node on success. Note : The relative field of the label node is set to '?' which denotes that the symbol does not yet have a value. Input : symbol : pointer to the symbol string of MAXSYMLEN or less. Globals : sym_glb_lab_head : head pointer is updated when symbol is added to global list. Warnings issued : 74 : Symbol already in global list. Ignored. Returns : 0 : symbol was added okay. 41: not enough memory to add to list. ****************************************************************************/ int sym_add_global( char * symbol ) { sym_label_type * temp ; if ( sym_lookup_global( symbol ) ) { e_message(0,74,NULL) ; /* WARNING symbol already in glb list.*/ return 0 ; } if ( temp = ( sym_label_type * ) malloc ( sizeof(sym_label_type) ) ) { strncpy( temp->symbol, symbol, MAXSYMLEN ); temp->relative = '?' ; temp->next = sym_glb_lab_head ; temp->val = 0L ; sym_glb_lab_head = temp ; return 0 ; } e_message(0,41,NULL) ; /* out of memory */ return 41 ; } /* sym_add_global */ /***************************************************************************** Function sym_lookup_extern This function looks up a symbol in the external label list. If the symbol is found, it returns a pointer to its operand node, otherwise it returns NULL. Input : symbol : pointer to external symbol string. Returns : described above. ****************************************************************************/ sym_operand_type * sym_lookup_extern( char * symbol ) { sym_operand_type * temp ; for ( temp = sym_ext_ref_head ; temp && strncmp(temp->symbol,symbol, MAXSYMLEN ) ; temp = temp->next ); return temp ; } /* sym_lookup_ext */ /***************************************************************************** Function sym_add_extern This function will add a symbol to the external list if the symbol is not already in the external list. The symbol string is copied into the operand node on success. Input : symbol : pointer to the symbol string of MAXSYMLEN or less. Globals : sym_ext_ref_head : reference head pointer is updated when symbol is added to external list. Warnings issued : 75 : Symbol already in external list. Ignored. Returns : 0 : symbol was added okay. 41: not enough memory to add to list. ****************************************************************************/ int sym_add_extern( char * symbol ) { sym_operand_type * temp ; if ( sym_lookup_extern( symbol ) ) { e_message(0,75,NULL) ; /* WARNING symbol already in extern list.*/ return 0 ; } if ( temp = ( sym_operand_type * ) malloc ( sizeof(sym_operand_type) ) ) { strncpy( temp->symbol, symbol, MAXSYMLEN ); temp->next = sym_ext_ref_head ; temp->list = NULL ; sym_ext_ref_head = temp ; return 0 ; } e_message(0,41,NULL) ; /* out of memory */ return 41 ; } /* sym_add_extern */ /***************************************************************************** Function sym_lookup_local This function looks up a symbol in the local label list. If the symbol is found, it returns a pointer to its label node, otherwise it returns NULL. Input : symbol : pointer to local symbol string. Returns : described above. ****************************************************************************/ sym_label_type * sym_lookup_local( char * symbol ) { sym_label_type * temp ; for ( temp = sym_local_lab_head ; temp && strncmp(temp->symbol,symbol, MAXSYMLEN ) ; temp = temp->next ); return temp ; } /* sym_lookup_local */ /***************************************************************************** Function sym_add_label_symbol This function will add a label symbol to a local label list, or resolve an already defined global label following the algorithm : if label already in extern label list then error else if label already in global label list then if its already resolved then error else resolve it and all back references in local op list. else if label in local label list then error else resolve it and all back references in local op list. If the relative field is set to am_relative then the label is treated as a relative label, otherwise it is treated as an absolute label. Note : The relative field is set to '*' if relative or 0 for absolute. Input : symbol : pointer to the symbol string of MAXSYMLEN or less. val : the long value of the symbol. relative : specifies whether or not to treat the label as relative. Globals : sym_local_lab_head : Local label head pointer is updated when symbol is added to label list. Returns : 0 : okay. 17: Cannot resolve an external symbol locally. 18: symbol was already resolved. 41: not enough memory to add to list. ****************************************************************************/ int sym_add_label_symbol ( char * symbol , unsigned long val , am_assem_type relative ) { sym_label_type * temp ; if ( sym_lookup_extern( symbol ) ) { e_message(0,17,NULL) ; /* label symbol already in extern list.*/ return 17; /* cannot resolve locally. */ } if ( temp = sym_lookup_global( symbol ) ) { /* in global list */ if ( temp->relative == '?' ) { /* not resolved yet. */ temp->relative = ( relative == am_relative ) ? '*' : 0 ; temp->val = val ; /* resolve back references */ sym_resolve_back( temp ); return 0 ; } else { e_message(0,18,NULL) ; /* symbol already resolved */ return 18 ; } } if ( sym_lookup_local( symbol ) ) { e_message(0,18,NULL) ; /* symbol already resolved in local list.*/ return 18 ; } if ( temp = ( sym_label_type * ) malloc( sizeof(sym_label_type)) ) { strncpy( temp->symbol, symbol, MAXSYMLEN ); temp->relative = ( relative == am_relative ) ? '*' : 0 ; temp->next = sym_local_lab_head ; temp->val = val ; sym_local_lab_head = temp ; /* resolve back references */ sym_resolve_back( temp ); return 0 ; } else { e_message(0,41,NULL) ; /* out of memory */ return 41 ; } } /* sym_add_label_symbol */ /***************************************************************************** Function sym_add_operand_symbol This function will add a attempt to resolve a symbol contained within a symbol node, or set the reference pointers in the reference lists accordingly depending on whether or not the symbol's value is known. It follows this algorithm : if symbol already in extern label list then add reference to it into the extern list. else if symbol already in global label list then if its already resolved then resolve it else add reference to the symbol to local op list. else if symbol in local label list then resolve it else add reference to the symbol to local op list. Input : symptr : pointer to the symbol node. termptr : pointer to term list that contains the symbol node. addref : boolean flag indicating whether or not a reference pointer should be set up if the symbol is not yet resolved. if TRUE then reference pointers will be set up. if FALSE then reference pointers will not be set up. Note : If addref is set to FALSE then no error can result since no memory allocation is attempted. Therefore the caller need not check the return code. Calls : sym_addtolocaloplist : This adds the refernece pointers for later resolution to the local operand list. Returns : 0 : okay. 41: not enough memory to add to list. ****************************************************************************/ int sym_add_operand_symbol ( p_sym_type * symptr , am_term_type * termptr , char addref ) { sym_operand_type * temp ; sym_ref_type * temp2 ; sym_label_type * temp3 ; if ( temp = sym_lookup_extern( symptr->sym ) ) { /* in extern list */ if ( ! addref ) return 0 ; if ( temp2 = ( sym_ref_type * ) malloc( sizeof(sym_ref_type)) ) { temp2->termptr = termptr ; temp2->next = temp->list ; temp->list = temp2 ; return 0 ; } else { e_message(0,41,NULL) ; /* not enough memory */ return 41 ; } } if ( temp3 = sym_lookup_global( symptr->sym ) ) { /* in global list */ if ( temp3->relative == '?' ) /* not resolved yet. */ if ( ! addref ) return 0 ; else return sym_addtolocaloplist( symptr, termptr ) ; else { symptr->val = temp3->val ; /* resolve the symbol */ symptr->sym[0] = temp3->relative ; symptr->sym[1] = 1 ; /* set relative count to 1 */ return 0 ; } } if ( temp3 = sym_lookup_local( symptr->sym ) ) { symptr->val = temp3->val ; /* resolve the symbol*/ symptr->sym[0] = temp3->relative ; symptr->sym[1] = 1 ; /* set relative count to 1 */ return 0 ; } else if ( ! addref ) return 0 ; else return sym_addtolocaloplist( symptr, termptr ) ; } /* sym_add_operand_symbol */ /***************************************************************************** Function sym_addtolocaloplist This function will add a symbol reference to the local operand list. If the symbol already has referneces in the local operand list, it creates a new reference node only. If the symbol has no previous references, it creates the symbol operand node as well as its first reference node. Input : symptr : pointer to the symbol node. termptr : pointer to term list that contains the symbol node. Globals : sym_local_ref_head : updated when symbol has never been referenced before, and a new operand node was created. Returns : 0 : okay. 41: not enough memory to add to list. ****************************************************************************/ int sym_addtolocaloplist ( p_sym_type * symptr , am_term_type * termptr ) { sym_operand_type * temp ; sym_ref_type * temp2 ; for ( temp = sym_local_ref_head ; temp && strncmp(temp->symbol,symptr->sym, MAXSYMLEN ) ; temp = temp->next ); if ( temp ) if ( temp2 = ( sym_ref_type * ) malloc( sizeof(sym_ref_type)) ) { temp2->termptr = termptr ; temp2->next = temp->list ; temp->list = temp2 ; return 0 ; } else { e_message(0,41,NULL) ; /* not enough memory */ return 41 ; } else if ( temp = ( sym_operand_type * ) malloc (sizeof(sym_operand_type)) ) if ( temp2 = ( sym_ref_type * ) malloc( sizeof(sym_ref_type)) ) { strncpy( temp->symbol, symptr->sym, MAXSYMLEN ); temp->next = sym_local_ref_head ; temp->list = temp2 ; sym_local_ref_head = temp ; temp2->termptr = termptr ; temp2->next = NULL ; return 0 ; } else { free(temp); e_message(0,41,NULL); /* not enough memory */ return 41 ; } else { e_message(0,41,NULL); /* not enough memory */ return 41 ; } } /* sym_addtolocaloplist */