Category : Files from Magazines
Archive   : DDJ0990.ZIP
Filename : IVES.ASC

 
Output of file : IVES.ASC contained in archive : DDJ0990.ZIP
_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 */






  3 Responses to “Category : Files from Magazines
Archive   : DDJ0990.ZIP
Filename : IVES.ASC

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/