From: Richard Burgess <> Date: Thu, 9 Feb 1995 16:30:42 +0000 (+0000) Subject: autocommit for file dated 1995-02-09 16:30:42 X-Git-Url: https://pd.if.org/git/?p=mmurtl;a=commitdiff_plain;h=90a9e5577efeed0e668695d9a63e6a04fa986d6e autocommit for file dated 1995-02-09 16:30:42 --- diff --git a/msamples/cm32m/cm32.c b/msamples/cm32m/cm32.c new file mode 100644 index 0000000..b65ec9c --- /dev/null +++ b/msamples/cm32m/cm32.c @@ -0,0 +1,4756 @@ +/* + C Minus 32 (C-32) Compiler (DOS version) + + Derived from code Copyright (c) 1989, Dave Dunfield + Copyright (c) 1991, 1992, 1993, 1994, 1995 R.A. Burgess + Permission is required from the authors for + any commercial use of this source code. + + 32 bit assembly language generation specificly for the Intel 386/486 + for the DASM assembler. + + CM-32 integer support: + short = int = short int = 16 bits, + long = long int = 32 bits. + + 16 bit Intel addressing modes are NOT supported for code generation. + +*/ + +/* These Simple defines to keep down the number of keystrokes. Also + lessens that horrible burden of portability that C is supposed to + shield you from (right...) +*/ + +#define U32 unsigned long +#define S32 long +#define U16 unsigned int +#define S16 int +#define U8 unsigned char +#define S8 char + +#include +#include +#include +#include +#include "CM32.h" +#include "Tokens32.h" + +#include "\OSSource\MMemory.h" + +#define SCODEBUF 512000 /* 500Kb allocated buffer */ +U8 *pcodebuf; + +/* Input buffer & input pointer for preprocessed lines */ +char line_in[LINE_MAX], *input_ptr; + +/* Global value storage locations (results from lexical scanner) */ + +char gst[SYMBOL_SIZE+1]; /* Name if SYMBOL */ +U8 namesize; /* size of SYMBOL name */ +U32 gvalue; + +/* + if token = SYMBOL then gvalue is length (number of characters). + if token = NUMBER then gvalue is value of NUMBER. + if token = STRING then gvalue is index into literal pool to the string. +*/ + +/* Symbol table and associated variables */ + +char GPool[GBUFFSIZE]; /* Pool for symbol names (packed) */ +char LPool[LBUFFSIZE]; /* Pool for local symbol names (packed) */ + +struct sym { + U32 type; /* 32 symbol type bits */ + U32 itypedef; /* index in symtab for type definition, or zero */ + /* If strucmem, struct it belongs to */ + U32 oname; /* offset in pool to sym name */ + U32 argoffset; /* arg offset from EBP for locals */ + U32 strucoff; /* size of strucdef (also offset for next new member), */ + /* if strucmem, it's member's offset in structure */ + U32 dindex; /* rg for index to dim */ + }; /* or index to proto list for arg types */ + /* or holds labels for for local Goto */ + +struct sym symtab[MAX_SYMBOLS]; /* SYM TABLE... */ + +U32 oNextGName = 0; /* offset to next new name in global pool */ +U32 oNextLName = 0; /* offset to next new name in local pool */ + +U32 proto_list[MAX_PROTOS], /* list of arg types for functions */ + global_top = 0, + global_count = 0, + iproto_next = 1, /* index to next available proto args (0 illegal)*/ + icrntpro = 0, /* index to arg being tested */ + iarg = 0, + argtype = 0, + local_top = MAX_SYMBOLS, + arg_count, /* number of args so far */ + local_stack, + sptr, /* symbol ptr */ + fptr; /* ptr to function symbol we are in */ + + /* structure definition use and control variables */ + + S8 fInStruct = 0; /* true if currently defining struct members */ + U32 CrntStrucDef; /* iSym for Structure def we are using or working on */ + char structname[12] = "0StructDef"; + U8 NxtStrucNum = 0; + U32 memoffset; /* used to calculate members offset */ + U32 strucsize; /* used for size of struct in INC and DEC */ +/* Literal + Dimension pools and associated variables */ + +U32 dim_top = 0, + literal_top = 0, + dim_pool[DIM_SIZE]; /* each entry has nDims for an array */ + + +char literal_pool[LITER_BUFF]; + +/* Expression evaluation stack. The value field contents vary + depending on what the token is. If it's a Symbol, this is + the symbol table entry. If token is a number then value is + the actual value of the number. If it's a string, then + value is the length. + The offset is used to hold constant values for addresses + that are in the index register or on the stack. + When accessing the data pointed to, if this is NON-zero, + we use the [ESI+NUM] addressing mode (Base+Offset). */ + +struct expr { + U32 token; /* Number, Symbol, String, In-Accumulator, etc. */ + U32 value; /* varies depending on token */ + U32 type; /* type from symtab */ + U32 offset; /* Offset of pointer in structure for access */ + }; + +struct expr expstk[EXPR_DEPTH]; /* Expression Stack */ + +U32 expr_ptr = 0; + +/* Misc. global variables */ + +char if_flag = 0, + asm_flag = 0, + not_flag = 0, + prefix = 'L'; /* override with /P:x switch */ + +U32 break_stack[LOOP_DEPTH], + continue_stack[LOOP_DEPTH], + switch_stack[MAX_SWITCH*2], + exit_label = 0, + exit_used = 0, + in_function = 0, + loop_ptr = 0, + switch_ptr = 0, + define_top = 0, + sdefault = 0, + exit_flag = 0, + next_lab = 0, + line_number = 0, + begin_comment = 0, /* line that a comment starts on for errors */ + ungot_token = 0, + error_count = 0, + warn_count = 0; + + + /* input/output pointers and buffer for for preprocessor */ + +char buffer[LINE_MAX], + *buffin_ptr, + *buffout_ptr; + + /* macro definition: index, pool and top pointers */ + +U32 macro = 0; +char *define_index[MACROS], + define_pool[MAC_BUFF], + *define_ptr; + + /* macro parameter: index, pool and top pointers */ +unsigned parm; +char *parm_index[PARAMETERS], + parm_pool[PARM_BUFF], + *parm_ptr; + + /* include: line stack & count, file pointers, filename */ +long include = 0, + incl_line[INCL_DEPTH]; + +FILE *incl_fh[INCL_DEPTH], + *source_fh = 0, + *asm_fh = 0, + *code_fh = 0, + *list_fh = 0; + +char codename[40]; +char srcname[40]; +char asmname[40]; +char lstname[40]; + +/* file open flags */ + +char fLISTOpen = 0, + fTEMPOpen = 0, + fCODEOpen = 0, + fASMOpen = 0; + + /* misc. variables and flags */ +char comment_flag = -1, + fQuiet = 0, + fSource = 0, + fNoOpt = 0, + fOptS = 0, + fList = 0, + fGen = 0, + fWarnings = 0; + + /* inlcude path */ + +char *incdir = "\\CM32\\INCLUDE"; + +/* For Code Gen */ + +U32 global_width = 0, + asmlab = 0; + +char zero_flag, + stack_flag = 0; + +U8 databuf[4096]; +U32 pc = 0; /* code pointer in */ +U32 pco = 0; /* code pointer out */ +U32 pd = 0; + +#include "Optimize.h" +#include "Proto32.h" + +/****************************************** +* Determine if a character is alphabetic +* or is underscore. +*******************************************/ + +static char is_alpha(char chr) +{ + return (((chr|0x20) >= 'a') && ((chr|0x20) <= 'z')) + || (chr == '_'); +} + +/********************************************* +* Determine if a character is a numeric digit +**********************************************/ + +static char is_digit(char chr) +{ + return (chr >= '0') && (chr <= '9'); +} + +/****************************************** +* Test for valid character in a name.(MACRO) +*******************************************/ + +#define is_alnum(c) (is_alpha(c) || is_digit(c)) +/* +static char is_alnum(char c) +{ + return is_alpha(c) || is_digit(c); +} +*/ + +/**************** +* Copy a string +*****************/ + +static void copystring(char *dest, char *source) +{ + while(*dest++ = *source++); +} + + +/*************************** +* Test for string equality +****************************/ + +static char equal_string(char *str1, char *str2) +{ + do { + if(*str1 != *str2++) + return 0; } + while(*str1++); + return -1; +} + +/********************************************************* + Skip to next non-blank (space, tab, CR or LF) in buffer + returning the character we find. +***********************************************************/ + +static char skip_blanks(void) +{ + while((*buffin_ptr < 0x21) && (*buffin_ptr)) + ++buffin_ptr; + return *buffin_ptr; +} + +/*************************************** +* Test for more macro parameters +****************************************/ + +static long more_parms(void) +{ + register char c; + + if(((c = skip_blanks()) == ',') || (c == ')')) + ++buffin_ptr; + else + line_error("Invalid macro parameter"); + return c == ','; +} + +/************************************** +* Skip ahead through a comment +***************************************/ + +static void skip_comment(void) +{ + register U16 x; + register char c; + + x = 0; + for(;;) { + if(!(c = *buffin_ptr++)) { /* end of line_in */ + if(!fgets(buffin_ptr = buffer, LINE_MAX, source_fh)) { + *buffin_ptr = 0; + UTC_error(); + return; } + ++line_number; } + else { + if((x = (x << 8) + c) == (('*' << 8) + '/')) /* close */ + return; + if(x == (('/' << 8) + '*')) /* imbedded comment */ + skip_comment(); + } + } +} + +/***************************************************************** +* Copy a named symbol from the input buffer to the output buffer +******************************************************************/ + +static void copy_name(void) +{ + do + *buffout_ptr++ = *buffin_ptr++; + while(is_alnum(*buffin_ptr)); + *buffout_ptr = 0; +} + +/****************************************************************** +* Copy a quoted string from the input buffer to the output buffer +* with regard for "protected" characters. +*******************************************************************/ + +static void copy_string(void) +{ + register char delim; + + if(((delim = *buffin_ptr) == '"') || (delim == 0x27)) { + do { + if(!(*buffout_ptr++ = *buffin_ptr)) { /* premature end */ + line_error("Unterminated string"); + return; } + if(*buffin_ptr++ == '\\') /* protected char */ + *buffout_ptr++ = *buffin_ptr++; } + while(*buffin_ptr != delim); + *buffout_ptr++ = *buffin_ptr++; } +} + +/************************************************************** +* Lookup a word from the input stream to see if it is a macro +***************************************************************/ + +static U32 lookup_macro(char eflag) +{ + register long i; + register char *name; + + name = buffout_ptr; + copy_name(); + for(i = macro - 1; i >= 0; --i) /* look it up */ + if(!strcmp(name, define_index[i])) + return i; + if(eflag) /* not found */ + line_error("Undefined macro"); + return -1; +} + +/************************************************************** +* Resolve a word into a macro definition (if it is defined) +***************************************************************/ + +static void resolve_macro(void) +{ + char *mptr, *ptr, *old_ptr; + long i; + register char c; + + old_ptr = buffout_ptr; + if((i = lookup_macro(0)) != -1) { /* Substitution required */ + mptr = define_index[i]; + while(*mptr++); + parm = 0; + parm_ptr = parm_pool; + if(*mptr++) { /* parameterized macro */ + if(skip_blanks() == '(') { + ++buffin_ptr; + do { + parm_index[parm++] = parm_ptr; + while(*buffin_ptr && (*buffin_ptr != ',') && (*buffin_ptr != ')')) + *parm_ptr++ = *buffin_ptr++; + *parm_ptr++ = 0; } + while(more_parms()); } } + while(c = *mptr) { /* copy over definition */ + if(c & 0x80) { /* parameter substitution */ + if((i = c & 0x7f) < parm) { + for(ptr = parm_index[i]; *ptr; ++ptr) + *old_ptr++ = *ptr; } } + else + *old_ptr++ = *mptr; + ++mptr; + } + *(buffout_ptr = old_ptr) = 0; + } +} + +/**************************************** +* Test for a string in input stream +*****************************************/ + +static unsigned long match(char *ptr) +{ + register char *ptr1; + + ptr1 = buffin_ptr; + while(*ptr) + if(*ptr++ != *ptr1++) /* symbols do not match */ + return 0; + if(is_alnum(*ptr1)) /* symbol continues */ + return 0; + buffin_ptr = ptr1; + skip_blanks(); + return(1); +} + +/******************************************************* +* Compare all optimization table entries with the +* instructions in the peephole buffer. +* Return: 0 = No match +* n = Full match begining at peep_top +* and ending at entry 'n' +********************************************************/ + +static long compareT(char *ptable, long peep) +{ + int i; + char *ptr1, *ptr2, c; + + for(i=0; i < OSYMBOLS; ++i) + symbols[i][0] = 0; + + ptr1 = peep_buffer[peep]; + while(c = *ptable) { /* any chars left in entry? */ + if(c == '\n') { /* end of line in table entry */ + if(*ptr1) /* and no match... */ + return 0; + peep = (peep+1) % OBUF_SIZE; + if(peep == peep_next) + return 0; + ptr1 = peep_buffer[peep]; /* next buffer entry */ + } + else if (c == ' ') { /* space */ + if(!isspace(*ptr1)) + return 0; + while(isspace(*ptr1)) + ++ptr1; } + else if(c & 0x80) { /* symbol name */ + + c = *(ptable + 1); + + ptr2 = symbols[*ptable & 0x7f]; + if(*ptr2) { + while(*ptr1 && (*ptr1 != c)) + if(*ptr1++ != *ptr2++) + return 0; + if(*ptr2) + return 0; } + else { /* new symbol */ + while(*ptr1 && (*ptr1 != c)) + *ptr2++ = *ptr1++; + *ptr2 = 0; } } + + else if(c != *ptr1++) return 0; /* normal character */ + + ++ptable; + } + return (*ptr1) ? 0 : peep + 1; +} + + +/******************************************************** +* Exchange new code for old code in the peephole buffer. +* pnew points to the first char of the new code. +* old points to last entry of buffer code to be replaced. +*********************************************************/ + +static void exchange(unsigned long old, char *pnew) +{ + char *ptr1, *ptr2; + + peep_top = (old+(OBUF_SIZE-1)) % OBUF_SIZE; /* last entry of replacement */ + ptr2 = peep_buffer[peep_top]; + while(*pnew) { /* while still some new stuff left */ + if(*pnew & 0x80) { /* output a symbol */ + ptr1 = symbols[*pnew & 0x7f]; /* ptr1 points to a symbol */ + while(*ptr1) + *ptr2++ = *ptr1++; } + else if(*pnew == '\n') { /* end of a new entry */ + *ptr2 = 0; /* put null at end of line */ + peep_top = (peep_top+(OBUF_SIZE-1)) % OBUF_SIZE; + ptr2 = peep_buffer[peep_top]; } + else + *ptr2++ = *pnew; + ++pnew; } + *ptr2 = 0; +} + + +/*********************************************** + Simulates fgets from the HUGE code buffer +************************************************/ + +static char *fgetcode(char *pout, long maxout) +{ +char c, *s; +long n; + + s = pout; + n = 0; + + if (pco >= pc) /* end of valid buffer data */ + return(0); + + while ((n < maxout-1) && (pcodebuf[pco])) + { + c = pcodebuf[pco]; + *pout++ = pcodebuf[pco++]; + n++; + if (c == 0x0A) + { + *pout = 0; + return(s); + } + } + if (n) + return(s); + else + return(0); +} + + +/**************************************** +* Read a line into the peephole buffer +* from the tmpcodebuf +*****************************************/ + +static long read_line(void) +{ +char c, *sptr; + + if(fgetcode(peep_buffer[peep_next], OLINE_SIZE)) { + + /*strip CR/LF from line */ + sptr = &(peep_buffer[peep_next]); + + while (c = *sptr) { + if (c == '\r') *sptr = '\0'; + if (c == '\n') *sptr = '\0'; + sptr++; + } + + /* next peep_buf entry please... */ + peep_next = (peep_next+1) % OBUF_SIZE; + return 1; + } + return 0; +} + +/********************************************************* +* Write a line from the peephole buffer to the output file +**********************************************************/ +static void write_line(void) +{ + if (fGen) + fputs(peep_buffer[peep_top], code_fh); + else + fputs(peep_buffer[peep_top], asm_fh); + peep_top = (peep_top + 1) % OBUF_SIZE; + if (fGen) + fputs("\n", code_fh); + else + fputs("\n", asm_fh); +} + + +/********************************************************* +* Peephole Optimizer function for code segment +**********************************************************/ +static void optimize(void) +{ + long i, j; + char *ptr; + + if (!fQuiet) + fputs("CM32 V2.3M optimizer phase\r\n", stdout); + + /* initially fill every line in peephole buffer */ + + j = 0; + + for(;;) { + /* keep buffer full! */ + while ( ((peep_next+1) % OBUF_SIZE) != peep_top ) + { + if (!read_line()) + { + break; + } + } + /* walk thru the whole peep_table and see if we have + matches in the peep_buffer */ + + for(i=0; ptr = peep_table[i]; i += 2) { + + j = compareT(ptr, peep_top); + + if(j) { /* we have a match, exchange it */ + exchange(j, peep_table[i+1]); + break; /* break to fill buffer again */ + } + } + if(!j) write_line(); /* no matches, flush this line */ + + if (peep_top == peep_next) + return; /* We are done! */ + } +} + + +/******************************************** + Read a line & perform pre-processing. if_flag + is used to indicate the nesting of ifs. It + is incremented for each if found. The high + bit is set if we are actually IN an active + macro. +*********************************************/ + +static long readline(void) +{ +U32 i; +char c, ch, fgotone; + +for(;;) { + if(!fgets(buffin_ptr = buffer, LINE_MAX, source_fh)) + { + if(include) + { + fclose(source_fh); + line_number = incl_line[--include]; + source_fh = incl_fh[include]; + continue; + } + return 0; + } /* no more lines... */ + + ++line_number; + buffout_ptr = line_in; + + fgotone = 0; + + if ((ch = skip_blanks()) == '#') + { + + if(match("#asm")) + { /* if inline assembly */ + asm_flag = -1; + fgotone = -1; + } + + else if(match("#endasm")) + { /* end of assembly */ + asm_flag = 0; + fgotone = -1; + } + + else if(match("#ifdef")) + { /* if macro defined */ + fgotone = -1; + if(if_flag) + ++if_flag; /* one deeper */ + else if(lookup_macro(0) == -1) + { + if_flag = 0x80; + } + } + + else if(match("#ifndef")) + { /* if macro not defined */ + fgotone = -1; + if(if_flag) + ++if_flag; + else if(lookup_macro(0) != -1) + { + if_flag = 0x80; + } + } + + else if(match("#else")) + { /* reverse condition */ + fgotone = -1; + if(!(if_flag & 0x7f)) /* not nested? */ + if_flag ^= 0x80; + } /* XOR high bit */ + + else if(match("#endif")) + { /* end conditional */ + fgotone = -1; + if(if_flag & 0x7f) /* if nested, reduce one */ + --if_flag; + else + if_flag = 0; + } /* else it's over */ + + else if(match("#pragma")) + { /* later... */ + fgotone = -1; + } + } + + /* If we got here, we have found # and it's not + a conditional, so it must be a new macro. + */ + + if ((!fgotone) && (!if_flag)) + { + if (ch == '#') + { + if(match("#define")) { /* define a new macro */ + if(macro >= MACROS) { + fatal_error("Too many macro definitions"); + exit(-1); } + buffout_ptr = define_index[macro++] = define_ptr; + if(!is_alpha(*buffin_ptr)) { + line_error("Invalid macro name"); + continue; } + copy_name(); /* get macro name */ + define_ptr = buffout_ptr; + *define_ptr++ = 0; + parm = 0; + parm_ptr = parm_pool; + if(*buffin_ptr == '(') { /* parameterized macro */ + *define_ptr++ = 1; + ++buffin_ptr; + do { + if(parm >= PARAMETERS) { + line_error("Too many macro parameters"); + break; } + parm_index[parm++] = buffout_ptr = parm_ptr; + skip_blanks(); + copy_name(); + parm_ptr = buffout_ptr+1; } + while(more_parms()); } + else + *define_ptr++ = 0; + skip_blanks(); + while(c = *buffin_ptr) { + buffout_ptr = define_ptr; + if(is_alpha(c)) { + resolve_macro(); + for(i=0; i < parm; ++i) { + if(!strcmp(define_ptr, parm_index[i])) { + *define_ptr++ = i + 0x80; + buffout_ptr = define_ptr; + break; } } + define_ptr = buffout_ptr; } + else if((c == '"') || (c == 0x27)) { + copy_string(); + define_ptr = buffout_ptr; } + else { + if((*++buffin_ptr == '*') && (c == '/')) { + ++buffin_ptr; + begin_comment = line_number; + skip_comment(); } + else + *define_ptr++ = c; } + /* skip_blanks(); */ + } + *define_ptr++ = 0; } + + else if(match("#undef")) + { /* undefine a macro */ + if((i = lookup_macro(-1)) != -1) + { + if(i == (macro - 1)) /* last one, simple delete */ + define_ptr = define_index[i]; + else + { /* not last, reclaim space */ + define_ptr -= (parm = (buffin_ptr = define_index[i+1]) - + (parm_ptr = define_index[i])); + while(parm_ptr < define_ptr) + *parm_ptr++ = *buffin_ptr++; + while(i < macro) + { /* adjust index list */ + define_index[i] = define_index[i+1] - parm; + ++i; + } + } + --macro; + } + } + else if(match("#include")) + { /* include a file */ + if(include >= INCL_DEPTH) + fatal_error("Too many include files"); + if((c = skip_blanks()) == '<') + { /* incdir definition */ + for(parm_ptr = incdir; *parm_ptr; ++parm_ptr) + *buffout_ptr++ = *parm_ptr; + *buffout_ptr++ = '\\'; /*** OS dependent!!! ***/ + c = '>'; + } + else if(c != '"') + { /* current directory */ + line_error("Invalid include file name"); + continue; + } + while(*++buffin_ptr && (*buffin_ptr != c)) + *buffout_ptr++ = *buffin_ptr; + *buffout_ptr = 0; + incl_fh[include] = source_fh; + incl_line[include] = line_number; + if(source_fh = fopen(line_in, "r")) + { + line_number = 0; + ++include; + } + else + { + line_error("Cannot open include file"); + source_fh = incl_fh[include]; + } + } + else + line_error("Unknown preprocessor directive"); + + } + else + { /* default, perform pre-processing */ + + if(asm_flag) /* inline assembly */ + do_asm(buffer); + else + while(c = *buffin_ptr) + { + if(is_alpha(c)) /* symbol, could be macro */ + resolve_macro(); + else if((c == '"') || (c == 0x27)) /* quoted string */ + copy_string(); + else + { + if((*++buffin_ptr == '*') && (c == '/') && comment_flag) + { /* comment */ + ++buffin_ptr; + begin_comment = line_number; /* save begin line*/ + skip_comment(); + } + else /* nothing special, copy it */ + *buffout_ptr++ = c; + } + } + if (fSource) + { + code_chr(';'); + code_str(buffer); + } + input_ptr=line_in; /* point to start of new line */ + *buffout_ptr = 0; /* null at end of line */ + return -1; + } + } /* !fgotone && !if_flag */ +} /*for*/ +} + + + + +/*********************************************************** +* Test to see if the last statement compiled was an "exit" +* statement, and if so, generate a jump to the appriopriate +* label. This prevents generation of a spurious jump when a +* "return" statement is used at the end of a function. +************************************************************/ + +static U32 test_exit(void) +{ + if(exit_flag) { + jump(exit_flag, -1); + exit_used = -1; + return(exit_flag = 0); + } + return -1; +} + +/********************************************* +* Output an warning message with quoted text +**********************************************/ + +static void t_warn(char *msg, char *txt) +{ +int i; + ++warn_count; + if (!fWarnings) return; + + incl_line[include] = line_number; + for(i=0; i <= include; ++i) { + put_num(incl_line[i], list_fh); + fputc(':', list_fh); + } + fputc(' ', list_fh); + fputs("Warning: ", list_fh); + fputs(msg, list_fh); + fputc(' ', list_fh); + fputc(0x27, list_fh); /* single quote */ + fputs(txt, list_fh); + fputc(0x27, list_fh); /* single quote */ + fputc('\r', list_fh); + fputc('\n', list_fh); +} + +/********************************************* +* Output an error message with quoted text +**********************************************/ + +static void t_error(char *msg, char *txt) +{ + char emsg[50], *ptr; + + ptr = emsg; + while(*msg) + *ptr++ = *msg++; + *ptr++ = ':'; + *ptr++ = ' '; + *ptr++ = 0x27; /* single quote */ + while(*txt) + *ptr++ = *txt++; + *ptr++ = 0x27; /* single quote */ + *ptr = 0; + line_error(emsg); +} + + +/*********************************************************** +* Report an error involving a freshly parsed symbol name +************************************************************/ + +static void symbol_error(char *msg) +{ + t_error(msg, gst); +} + + +/*********************************************************** +* Report a syntax error +************************************************************/ + +static void syntax_error(void) +{ + line_error("Syntax error"); +} + +/*********************************************************** +* Report incompatable types +************************************************************/ + +static void type_error(void) +{ + line_error("Types mismatch"); +} + +/*********************************************************** +* Report an error in indirection +************************************************************/ + +static void index_error(void) +{ + line_error("Illegal indirection"); +} + + +/**************************************************** +* Test the next token in the input stream, put it +* back if its not the one we are looking for. +****************************************************/ + +static char test_token(U32 token) +{ + U32 token1; + + if((token1 = get_token()) == token) + return -1; + unget_token(token1); + return 0; +} + + +/************************************************************** +* Check for a certain token occuring next in the input stream. +* If it is not found, report an error. +***************************************************************/ + +static void expect(U32 token) +{ + if(!test_token(token)) + t_error("Expected", tokens[token]); +} + +/*************************************** + Report a Unterminated Comment error +****************************************/ + +static void UTC_error(void) +{ + U32 i; + + incl_line[include] = line_number; + for(i=0; i <= include; ++i) { + put_num(incl_line[i], list_fh); + fputc(':', list_fh); + } + fputc(' ', list_fh); + fputs("Unterminated comment from line: ", list_fh); + put_num(begin_comment, list_fh); + fputc('\r', list_fh); + fputc('\n', list_fh); + + if(++error_count == MAX_ERRORS) + fatal_error("Too many errors"); +} + +/*************************************** +* Report a compile error +****************************************/ + +static void line_error(char *message) +{ + U32 i; + + incl_line[include] = line_number; + for(i=0; i <= include; ++i) { + put_num(incl_line[i], list_fh); + fputc(':', list_fh); + } + fputc(' ', list_fh); + fputs(message, list_fh); + fputc('\r', list_fh); + fputc('\n', list_fh); + + if(++error_count == MAX_ERRORS) + fatal_error("Too many errors"); +} + +/******************************************* +* Report a non-recoverable compile error +********************************************/ + +static void fatal_error(char *string) +{ + line_error(string); + + /* put this out even if fQuiet */ + fputs("Fatal error, compilation aborted\r\n", stdout); + + if (fList) { + fputs("Fatal error, compilation aborted\r\n", list_fh); + if (fLISTOpen) + fclose(list_fh); + } + + /* close files */ + + if (fASMOpen) fclose(asm_fh); + exit(-1); /* We are done ! */ + +} + +/***************************************************** +* Check that a loop is active & setup exit condition +******************************************************/ + +static void check_loop(U32 stack[]) +{ + expect(SEMI); + if(loop_ptr) + exit_flag = stack[loop_ptr-1]; + else + line_error("No active loop"); +} + +/**************************************************** +* Check that a switch is active & allocate label +*****************************************************/ + +static U32 check_switch(void) +{ + if(!sdefault) + line_error("No active switch"); + gen_label(++next_lab); + return next_lab; +} + +/******************************************************************* +* Compile a jump only if last statement compiled was not an "exit" +* statement ("return", "break", or "continue"). This prevents the +* generation of an unaccessable jump following these statements. +********************************************************************/ + +static void test_jump(U32 label) +{ + if(test_exit()) + jump(label, -1); +} + + +/******************************************** +* Compile a conditional jump and +* set up 'Z' flag if necessary. +*********************************************/ + +static void cond_jump(char cond, U32 label, char ljmp) +{ + if(zero_flag) + { + out_inst("AND EAX,EAX"); + zero_flag = 0; + } + + jump_if(cond ^ not_flag, label, ljmp); + not_flag = 0; +} + + +/********************************************************** +* Get a number in a number base for a maximum # of digits +* (digits = 0 means no limit) +***********************************************************/ + +static U32 get_number(U32 base, U32 digits) +{ + U32 value; + char c; + + value = 0; + do { + if(is_digit(c = *input_ptr)) /* convert numeric digits */ + c -= '0'; + else if(c >= 'a') /* convert lower case alphas */ + c -= ('a' - 10); + else if(c >= 'A') /* convert upper case alphas */ + c -= ('A' - 10); + else + break; + if(c >= base) /* outside of base */ + break; + value = (value * base) + c; /* include in total */ + ++input_ptr; } + while(--digits); /* enforce maximum digits */ + return value; +} + + +/***************************************************************** +* End of file has been encountered, dump the literal pool, and +* generate definitions for any external or uninitialized global +* variables. +******************************************************************/ + +static void clean_up(void) +{ + U32 type, size, i, j; + + if(in_function) + fatal_error("Unterminated function"); + +/* Generate all externals referenced from CSEG if NOT for DASM */ + + for(sptr = 0; sptr < global_top; ++sptr) + { + if((type = symtab[sptr].type) & (EXTERNAL)) + if (type & REFERENCE) + gen_ext_data_DASM(sptr); + } + +/* dump literal pool */ + + gen_literal(literal_pool, literal_top); + +/* Generate all global variables that are not initialized */ + + for(sptr = 0; sptr < global_top; ++sptr) { + type = symtab[sptr].type; + if(!(type & (FUNCTION | INITED | EXTERNAL | TYPDEF))) { + + if (type & (POINTER | DWORD)) size = 4; + else if (type & WORD) size = 2; + else if (type & BYTE) size = 1; + else if (type & STRUCT) { + size = symtab[sptr].strucoff; /* contains size */ + } + else size = 1; /* should be an error maybe?? */ + + if(type & ARRAY) { /* calculate size of array */ + i = symtab[sptr].dindex; /* i = index to dimpool */ + j = dim_pool[i++]; /* j = nDims */ + while(j--) size *= dim_pool[i++]; + } + gen_global(sptr, size); + } + } + + if (pd) + { + fwrite(databuf, pd, 1, asm_fh); + pd = 0; + } + + if (!error_count) + { /* No optimize if errors */ + + if (fNoOpt) { + + while (fgetcode(buffer, LINE_MAX)) + { + if (fGen) + fputs(buffer, code_fh); + else + fputs(buffer, asm_fh); + } + } + else { + optimize(); + } + } + fclose(asm_fh); + if (fLISTOpen) + fclose(list_fh); + if (fCODEOpen) + fclose(code_fh); + + if(!fQuiet) { + put_num(error_count, stdout); + fputs(" errors\r\n", stdout); + put_num(warn_count, stdout); + fputs(" warnings\r\n", stdout); + } + + exit(0); /* We are done ! */ +} + + +/***************************************** +* Read a character from the input file +******************************************/ + +static char read_char(void) +{ + char c; + + while(!(c = *input_ptr++)) { /* end of this line */ + if (!readline()) { + clean_up(); + exit(error_count); + } + } + return c; +} + +/*********************************************************** +* Allow a single token to be returned to the input stream +************************************************************/ + +static void unget_token(U32 token) +{ + ungot_token = token; +} + + +/************************************************ +* Read special character (with translations) +*************************************************/ + +static U32 read_special(char delim) +{ + S32 c; + + if((c = read_char()) == delim) + return 0xff00; + if(c == '\\') + switch(c = read_char()) { + case 'n': /* newline */ + c = 0x0a; + break; + case 'r': /* return */ + c = 0x0d; + break; + case 't': /* tab */ + c = 0x09; + break; + case 'f' : /* formfeed */ + c = 0x0c; + break; + case 'b': /* backspace */ + c = 0x08; + break; + case 'v': /* vertical tab */ + c = 0x0b; + break; + case 'x' : /* hex value */ + c = get_number(16, 2); + break; + default: + if(is_digit(c)) { /* octal value */ + --input_ptr; + c = get_number(8, 3); + } + } + return c & 0xff; +} + +/************************************************************************* +* Get a token from the input stream, and return it as a simple value +* (indicating type). If it a special token type (NUMBER, STRING, SYMBOL), +* global variables "gvalue" and "gst" are set to appriopriate values. +**************************************************************************/ + +static U32 get_token(void) +{ + U32 i; + char *ptr, *last_pos, chr; + +/* if a token has been ungot, re-get it */ +/* (gvalue & gst) will not have changed */ + if(ungot_token) { + i = ungot_token; + ungot_token = 0; + return i; + } + +/* skip any leading whilespace */ + do { + chr = read_char(); + } + while((chr == ' ') || (chr == '\t') || + (chr == '\n') || (chr == '\r')); + + --input_ptr; + + +/* lookup token in token table */ + last_pos = input_ptr; /* remember where we were */ + + if (itoken[*input_ptr] != 0) { + /* start at first match char */ + for(i=itoken[*input_ptr]; ptr = tokens[i]; ++i) { + if (*input_ptr != *ptr) break; + while((chr = *input_ptr) && (*ptr == chr)) { + ++ptr; + ++input_ptr; } + if(!*ptr) { /* we found a token */ + if(is_alpha(*(ptr-1)) && is_alpha(*input_ptr)) + continue; /* not this token */ + return i; } + input_ptr = last_pos; /* reset pointer */ + } + } +/* we didn't find a token, check out special cases */ + input_ptr = last_pos; + + if((chr = *input_ptr) == '"') { /* string value */ + ++input_ptr; + gvalue = literal_top; /* set to index to string */ + do { + if(literal_top >= LITER_BUFF) + fatal_error("String space exausted"); + literal_pool[literal_top++] = i = read_special('"'); + } + while(!(i & 0xff00)); /* 0xff00 returned from read_special */ + return STRING; } + + /* Quoted values (e.g., '\n') are read into gvalue. + ANSI says that multibyte character constants are + implementation dependent. SOOO, In our case we + shift multiple bytes to the left. '\n\r' will + make gvalue equal 0x0D0A even though the \n (0x0A) is + first. + */ + + if(chr == 0x27) + { /* quoted value */ + ++input_ptr; + while( !((i = read_special(0x27)) & 0xff00) ) + gvalue = i & 0xff; /* strip notify bits */ + return NUMBER; + } + + + if(is_digit(chr)) { /* numeric constant */ + if(chr == '0') { + ++input_ptr; + if((*input_ptr == 'x') || (*input_ptr == 'X')) { + ++input_ptr; + gvalue = get_number(16, 0); /* hex */ + } + else + gvalue = get_number(8, 0); /* octal */ + } + else gvalue = get_number(10, 0); /* decimal */ + + /* Look for Unsigned and Long terminal characters */ + + if((*input_ptr == 'U') || (*input_ptr == 'u')) { + ++input_ptr; + if((*input_ptr == 'L') || (*input_ptr == 'l')) ++input_ptr; + } + else if((*input_ptr == 'L') || (*input_ptr == 'l')) ++input_ptr; + + return NUMBER; + } + + if(is_alpha(chr)) { /* symbol name */ + gvalue = 0; + while(is_alnum(chr = *input_ptr)) { + if(gvalue < SYMBOL_SIZE) + gst[gvalue++] = chr; + ++input_ptr; } + gst[gvalue] = 0; + namesize = gvalue; + return SYMBOL; } + +/* not a token or special value */ + ++input_ptr; /* skip offending character */ + return -1; /* report "unknown" token type */ +} + + +/***************************************************** +* Locate a symbol in the local symbol table +******************************************************/ + +static U32 lookup_local(void) +{ + U16 i,j; + + i = MAX_SYMBOLS-1; + while(i > local_top-1) { + j = symtab[i].oname; + if(equal_string(gst, &LPool[j])) + return symtab[sptr=i].type |= REFERENCE; + i--; + } + return 0; +} + + +/************************************************* + Locate a symbol in the global symbol table. +**************************************************/ + +static U32 lookup_global(void) +{ + U16 i, j; + + for(i=0; i < global_top; i++) { + j = symtab[i].oname; + if(equal_string(gst, &GPool[j])) + return symtab[sptr=i].type |= REFERENCE; + } + return 0; +} + +/************************************************** + Locate a structure member in either symbol table +***************************************************/ + +static U32 lookup_member(U32 CrntStruc) +{ + U16 i, j, CrntStrucDef; + + /* structure member names were hidden in the tables + by inserting a '0' as the first char. Now we + must make the current symbol name match + so we can find it in the tables. It will follow + the structure definition entry for CrntStruc. + */ + + for (i=SYMBOL_SIZE; i > 0; i--) /* fix member name */ + gst[i] = gst[i-1]; + gst[0] = '0'; /* make it start with digit */ + ++namesize; + + CrntStrucDef = symtab[CrntStruc].itypedef; + + if (symtab[CrntStrucDef].type & GLOBAL) { + + for(i=CrntStrucDef+1; i < global_top; i++) { + if (!(symtab[i].type & STRUCMEM)) + break; + j = symtab[i].oname; + if(equal_string(gst, &GPool[j])) + return symtab[sptr=i].type |= REFERENCE; + } + } + else { /* must be local */ + + for(i=CrntStrucDef-1; i > local_top-1; i--) { + if (!(symtab[i].type & STRUCMEM)) + break; + j = symtab[i].oname; + if(equal_string(gst, &LPool[j])) + return symtab[sptr=i].type |= REFERENCE; + } + } + return 0; /* didn't find it! */ +} + +/*************************************************************** + Enter a symbol in the symbol table with specified name & type. + Leaves global sptr with the index of the symbol just added. +****************************************************************/ + +static void define_symbol(U32 type, U32 dim_index) +{ + U32 index; + U16 j; + + if(in_function) { /* within a function, use local */ + if (type&PROTO) { /* give it a false name to satisfy symbol table */ + gst[0] = '_'; /* 2 underscores */ + gst[1] = '-'; + gst[2] = arg_count + 65; /* A-Z */ + gst[3] = '\0'; /* null terminate */ + namesize = 3; + } + else { + if(lookup_local()) { + symbol_error("Duplicate local or arg"); + return; + } + } + + sptr = --local_top; + if(type & ARGUMENT) { + + type &= ~PROTO; /* turn off proto warning */ + + /* if a prototype arg already exists, make sure it's the same */ + if (icrntpro) { + if (proto_list[icrntpro++] != type) { + j=symtab[fptr].oname; + t_warn("Arg not same type as prototype in ", &GPool[j]); + } + } + else proto_list[iproto_next++] = type; + if(iproto_next>MAX_PROTOS) + fatal_error("Prototype table full"); + } + else + index = local_stack; + + if(global_top > local_top) + fatal_error("Symbol table full"); + if((oNextLName+SYMBOL_SIZE) > LBUFFSIZE) + fatal_error("Local symbol name pool full"); + symtab[sptr].oname=oNextLName; + copystring(&LPool[oNextLName], gst); + oNextLName += namesize; + oNextLName++; /* for null */ + symtab[sptr].type = type; + symtab[sptr].argoffset = index; + symtab[sptr].dindex = dim_index; + + } + else { /* outside of function, use global */ + type |= GLOBAL; + if(index = lookup_global()) { /* symbol already exists */ + if(index & (PROTO|FUNCTION)) { /* re-definition */ + if((index | (INITED|REFERENCE|EXTERNAL|PROTO)) != + (type | (INITED|REFERENCE|EXTERNAL|PROTO))) + symbol_error("Inconsistant re-declaration"); + symtab[sptr].type = type; + return; + } + else if (type & STRUCMEM) + {} /* no error */ + else if ((type & STRUCDEF) && + (equal_string(&structname[0], &GPool[symtab[sptr].oname]))) + {} /* no error */ + else { + symbol_error("Duplicate global"); + return; } + } + sptr = global_top++; + index = global_count++; + if(global_top > local_top) + fatal_error("Symbol table full"); + if((oNextGName+SYMBOL_SIZE) > GBUFFSIZE) + fatal_error("Global symbol name pool full"); + symtab[sptr].oname = oNextGName; + copystring(&GPool[oNextGName], gst); + oNextGName += namesize; + oNextGName++; /* for null */ + symtab[sptr].type = type; + symtab[sptr].argoffset = index; + symtab[sptr].dindex = dim_index; + } +} + + +/****************************************** +* Push a value on the expression stack +*******************************************/ + +static void push(U32 token, U32 value, U32 type, U32 offset) +{ + if(expr_ptr >= EXPR_DEPTH) + fatal_error("Expression stack overflow"); + + expstk[expr_ptr].token = token; + expstk[expr_ptr].value = value; + expstk[expr_ptr].type = type; + expstk[expr_ptr].offset = offset; + expr_ptr++; +} + +/********************************************* +* Pop a value from the expression stack +**********************************************/ + +static void pop(U32 *token, U32 *value, U32 *type, U32 *offset) +{ + if(!expr_ptr) + fatal_error("Expression stack underflow"); + + expr_ptr--; + *token = expstk[expr_ptr].token; + *value = expstk[expr_ptr].value; + *type = expstk[expr_ptr].type; + *offset = expstk[expr_ptr].offset; +} + + +/******************************************** +* Get a constant value (NUMBER or STRING) +* which can be evaluated at compile time. +*********************************************/ + +static void get_constant(U32 *token, U32 *value) +{ + U32 type, offset; + + expr_ptr = 0; + unget_token(do_oper(SEMI)); /* do_oper gets the token... */ + + pop(token, value, &type, &offset); /* then we pop it into token */ + + if((*token != NUMBER) && (*token != STRING)) + line_error("Constant expression required"); +} + +/******************************** +* Define a variable +*********************************/ + +static void define_var(U32 type) +{ + U32 token, stype, lasttoken, value, index, size, i, j, ocbcnt; + U32 sDim1, nDims, iDim, eTotal; + char eflag, nflag; + + if(in_function > 1) + line_error("Declaration must preceed code"); + +/* calculate base variable size - store in j for later use if array */ + + if (type&BYTE) size = 1; + else if (type&WORD) size = 2; + else if (type&DWORD) size = 4; + else if (type&STRUCT) + size = symtab[CrntStrucDef].strucoff; + else + line_error("Type specifier missing"); + + if (type&(ARGUMENT | POINTER)) size = 4; /* pointers are 32 bit offsets */ + j = size; + +/* If fInStruct then this is a structure member definition. + NOT a structure member! */ + + if (fInStruct) { + type |= (STRUCMEM|TYPDEF); /* make it a member definition */ + for (i=SYMBOL_SIZE; i > 0; i--) /* fix member name */ + gst[i] = gst[i-1]; + gst[0] = '0'; /* make it start with digit */ + ++namesize; + } + +/* evaluate any array indexes */ + + iDim = dim_top; + nflag = 0; + nDims = 0; + while(test_token(OSB)) { /* array definition */ + ++nDims; + ++dim_top; + if(test_token(CSB)) { /* null definition */ + if ((nflag) || (nDims > 1)) + line_error("Null only allowed in first index"); + --nflag; + size *= dim_pool[dim_top] = 1; /* dummy up unbounded array */ + continue; } + get_constant(&token, &value); + if(token != NUMBER) + line_error("Numeric constant required"); + size *= dim_pool[dim_top] = value; + expect(CSB); + } + + if(nDims) { /* defining an array */ + type |= ARRAY; + dim_pool[iDim] = nDims; + if(++dim_top > DIM_SIZE) + fatal_error("Dimension table full"); + } + + if(test_token(ASSIGN)) /* initialized variable */ + type |= INITED; + + local_stack += size; /* Keep track of offset of local vars */ + + define_symbol(type, iDim); /* Create the symbol table entry */ + + if (type&(STRUCMEM|TYPDEF)) { + symtab[sptr].itypedef = CrntStrucDef; + symtab[sptr].strucoff = symtab[CrntStrucDef].strucoff; + symtab[CrntStrucDef].strucoff += size; /* add to the total */ + } + + if (type&(STRUCT)) { + symtab[sptr].itypedef = CrntStrucDef; + symtab[sptr].strucoff = symtab[CrntStrucDef].strucoff; + } + +/* + Initialization of arrays has changed with ANSI. Nested braces and + trailing commas are now allowed. We make sure the braces balance. +*/ + eflag = -1; /* Expexting initializer */ + sDim1 = 0; /* tracks nDimensions for unbounded arrays [] */ + eTotal = 0; /* Total elements initialized so far */ + + if(type & INITED) { /* force immediate allocation */ + if ((in_function) || (fInStruct)) + line_error("Illegal initialization"); + data_global(sptr); /* generate label in DSeg */ + ocbcnt = 0; /* number of open brackets */ + index = 0; /* tracks index for current dimensions */ + do { + switch (token=get_token()) { + case OCB: + if ((nflag) && (ocbcnt == 1)) /* count for unbounded */ + ++sDim1; + ocbcnt++; + if (ocbcnt > nDims) + line_error("Too many open braces"); + lasttoken = OCB; + eflag = -1; + break; + case CCB: + if (ocbcnt) --ocbcnt; + else + line_error("Unbalanced braces"); + if ((nDims > 1) && (ocbcnt)) { + while (index < dim_pool[dim_top-1]) { + init_static(NUMBER, 0, j); + ++index; + } + } + eTotal+=index; /* total inited so far */ + index = 0; /* tracks index for current dimensions */ + eflag = 0; /* no constant expected now! */ + lasttoken = CCB; + break; /* we need a comma first/ocb first */ + case COMMA: + /* secial case of char[x][y] = {"abc","def","ghi"}; */ + if ((nDims > 1) && (ocbcnt==1) && + (lasttoken==STRING) && !(type & POINTER)) { + while (index < dim_pool[dim_top-1]) { + init_static(NUMBER, 0, j); + ++index; + } + eTotal+=index; /* total inited so far */ + index = 0; + } + if (lasttoken==CCB) eflag = 0; + else eflag = -1; + lasttoken = COMMA; + break; + case STRING: + /* special case of char[x] = "xx"; or char[] = "xx"; */ + if ((!ocbcnt) && (nDims==1)) + eflag = -1; + case NUMBER: + case SUB: + case COM: + case NOT: + unget_token(token); /* put it back */ + if (eflag) { /* we are expecting a constant */ + if ((nflag) && (ocbcnt == 1)) + ++sDim1; + get_constant(&token, &value); + if((token == STRING) && !(type & POINTER)) { + do { + init_static(NUMBER, literal_pool[value], j); + ++index;} + while(++value < literal_top); + literal_top = gvalue; + + /* special case of char[]="xx" or char[x]="xx"; */ + if ((!ocbcnt) && (nDims==1)) { + eTotal = index; + sDim1 = index; + } + } + else { + init_static(token, value, j); + if (!nDims) + ++eTotal; /* tracked by index if array */ + ++index; + } + lasttoken=token; + } + else line_error("Improper bracketed initialization"); + break; + case SYMBOL: + lasttoken=token; + if (eflag) { /* we are expecting an initializer */ + if (type&POINTER) { + if(stype=lookup_global()) { + if (stype&(EXTERNAL|FUNCTION)) + symbol_error("Invalid init type"); + else { + init_static(SYMBOL, sptr, j); + index += j; + } + } + else symbol_error("Undefined"); + } + else line_error("Must be pointer array"); + } + else line_error("Improper bracketed initialization"); + break; + default: + line_error("Improper symbol in initialization"); + break; + } + } + while(((ocbcnt) || (lasttoken==COMMA)) && (token!=SEMI)); + if(ocbcnt) expect(CCB); /* make sure brackets balance!! */ + + if(nflag) /* fixup null definition */ + dim_pool[iDim+1] = sDim1; + + /* new we make sure all the array elements were initialized + to ensure we have allocated Dseg for them. + */ + + i=1; + while (nDims) i*= dim_pool[iDim+nDims--]; /* i has total elements */ + + while (eTotal < i) { /* eTotal is total inited */ + init_static(NUMBER, 0, j); + ++eTotal; + } + + if (eTotal > i) + line_error("Too many initial values"); + + end_static(); + + } /* if (INITED) */ + +} + +/***************************************************** +* Check that we are within a function definition +******************************************************/ + +static void check_func(void) +{ + if(in_function) { + if(in_function < 2) { /* first invocation */ + in_function = 2; + enter_func(fptr, local_stack); } } + else + line_error("Incorrect declaration"); +} + +/********************************************************** +* Declare a symbol (function or variable) with modifiers. + If we get here we have found a legal type specifier. + Definitions other than structures are quite simple. We + loop through eating the variables behind it adding them + to the symbol table. + Structures are handled differently because you can define + a structure with a TAG without actually allocating space. + This is actually a type definition. + In fact, you can define the structure with an optional + tag, then place the variable names right behind it. + This checks to see if we have a tag name first. If the tag + name is present, we then check to see if it's a defined + structure already, in which case we expect a new symbol + name will follow. If the tag is not already defined than + we expect {} with a structure definition in between! +***********************************************************/ + +static void declare(U32 token, U32 type) +{ + fInStruct = 0; + for(;;) { + switch(token) { + case CHAR: + type &= ~WORD; /* cancel WORD */ + type |= BYTE; + break; + case INT: + if (!(type&DWORD)) type |= WORD; + break; + case SHORT: + type |= WORD; + break; + case LONG: + type &= ~WORD; /* cancel WORD */ + type |= DWORD; + break; + case UNSIGN: + type |= WORD; /* cancelled for char or long */ + type |= UNSIGNED; + break; + case SIGNED: + type |= WORD; /* this will be cancelled if it's long */ + type &= ~UNSIGNED; + break; + case STAT: + type |= STATIC; + break; + case STRUC: + type |= STRUCDEF; + break; + case CONST: + type |= CONSTANT; + break; + case EXTERN: + type |= EXTERNAL; + break; + case REGIS: + type |= REGISTER; + break; + case INTR: + type |= ISR; /* ISR modified for functions */ + break; + case VOIDD: + type |= VOID; + break; + case FARR: + type |= FAR; + break; + case STAR: /* pointer reference */ + do + ++type; + while(test_token(STAR)); + + /* allow for prototype arg pointers with no symbol name */ + + if((type&ARGUMENT) && (test_token(COMMA))) { + define_var(type|=PROTO); /* tell 'em it may be symboless */ + return; + } + else + if(!test_token(SYMBOL)) syntax_error(); + case SYMBOL: /* we have a symbol name */ + /* If STRUCDEF, MUST be a Struct Tag Name (new or existing) */ + if(type & STRUCDEF) { /* This symbol follows "struct" */ + if (lookup_global()) { /* Existing tag ?? */ + if (symtab[sptr].type & STRUCDEF) { + CrntStrucDef = sptr; /* tag we just found */ + /* might be a pointer to a structure */ + if (test_token(STAR)) + do + ++type; + while(test_token(STAR)); + /* now expect a new structure variable name */ + if(!test_token(SYMBOL)) { + line_error("struct variable expected"); + return; + } + else { + type &= ~STRUCDEF; /* struct variable w/tag */ + type |= STRUCT; + } + } + else + line_error("struct tag name expected"); + } + + /* we didn't find the tag so it must be a new one! */ + + else { /* So add the strucdef */ + define_symbol(type|TYPDEF, 0); + CrntStrucDef = sptr; /* symbol we just found */ + if(!(test_token(OCB))) /* expecting { */ + line_error("Structure { expected"); + else { + fInStruct = 1; /* we are in it now... */ + type = 0; + break; + } + } + } + if(type&ARGUMENT) { + define_var(type); + return; + } + if(test_token(ORB)) + define_func(type); /* function definition */ + else + define_var(type); /* variable definition */ + if(test_token(COMMA)) + break; + + test_token(SEMI); /* eat the semicolon if there */ + + if (fInStruct) { /* still defining struct members */ + type = 0; + break; + } + else + return; + case OCB: /* '{' only allowed for immediate struct defs */ + if(type & STRUCDEF) { /* Immediate struct definition */ + copystring(gst, structname); + gvalue = 11; + define_symbol(type|TYPDEF, 0); + CrntStrucDef = sptr; /* symbol we just added */ + fInStruct = 1; /* we are in it now... */ + type = 0; + break; + } + else + syntax_error(); + break; + case CCB: + if (!fInStruct) { + syntax_error(); + return; + } + else { + type &= ~STRUCDEF; /* struct variable */ + type |= STRUCT; + fInStruct = 0; + } + if (test_token(SEMI)) /* if semicolon, eat it and return */ + return; + break; + case CRB: + unget_token(token); /* fall thru to COMMA */ + case COMMA: + if(type&ARGUMENT) { + define_var(type|=PROTO); + return; + } + break; + default: + syntax_error(); + + } + type &= ~(POINTER | ARRAY); /* clear indirection and rg of last var */ + token = get_token(); + } +} + +/************************************************************** +Define a function + +Notes on prototype handling: + +We have an array called proto_list and an index to the list called +iproto_next. For any function (external or global), we keep track +of the argument types in the proto list. We do this by assuming that the +first time a function is identified, we define it as if it was a real +function. If it turns out to be a proto, we just throw away the +local arguments and mark it as proto. If we find a function that is +being defined and it's args don't match the defined types, emit a warning +or error (if type is not convertable). +In any case, this means we now know what type of argument +to expect for any function that is called. We can do the proper +type manipulations and flag errors when found. +This also means you must prototype all external function, and +that all of them MUST have all arguments defined!!! +As per ANSI - the proto MUST contain the type of each arg, and +may or may not provide a name for each arg. + +For functions that are defined with variable parameter lists (unknown +numbers of args and type), we add a single identifier in the Proto list +that indicates this. The arg definition for variable parameters is +an elipse (...). +All args in a function with variable args are accessed differently than +those with fixed parameters. Normally the EBP register accesses the +args with a fixed offset. When you have variable args (and becuase we +push args from left to right) we won't know where the fixed args are or +where the variable args begin. To find out where they are and access them +we push the number of variable args as the last arg on the stack. +From this value we can determine where the variable args begin and the fixed +args leave off. We access the fixed args with [EBP+EDI+offset] +to access the args. This is because the fucntion "va_arg" will set +EDI up with the proper context to access the variable required. See +stdarg.c for more information. + +****************************************************************/ + +static void define_func(U32 type) +{ + U32 token, dim_save, t, flastarg, i, stackpop; + + icrntpro = 0; + flastarg = 0; + arg_count = 0; + oNextLName = 0; + + /* do not allow functions or protos within functions */ + + if(in_function) { + line_error("Illegally nested function or prototype"); + return; + } + + if(t = lookup_global()) { /* symbol already exists */ + if (t & PROTO) { + icrntpro = symtab[sptr].dindex; /* icrntpro points to arg types */ + } + } + else { + define_symbol(type | FUNCTION | GLOBAL, 0); + symtab[sptr].dindex = iproto_next; + } + + fptr = sptr; + +/* accept variable declarations for local arguments */ +/* These must be inside the parens -- ANSI style */ + + local_top = MAX_SYMBOLS; + dim_save = dim_top; + in_function = 1; /* indicate inside a function */ + + do { + switch(token = get_token()) { /* define local arguments */ + case SHORT: + case LONG: + case INT: + case SIGNED: + case UNSIGN: + case CHAR: + case FAR: + case CONST: + case STRUC: + declare(token, ARGUMENT); + arg_count += 1; + break; + case COMMA: break; + case ELIPSE: + proto_list[iproto_next++] = VOID; + flastarg = -1; + symtab[fptr].type |= VARARGS; + break; + case VOIDD: + if (arg_count) syntax_error(); /* fall through to default */ + default: + if (!flastarg) { + proto_list[iproto_next++] = VOID; + if(iproto_next>MAX_PROTOS) + fatal_error("Prototype table full"); + flastarg = -1; + if (token==CRB) unget_token(token); + } + break; + } + } while (!flastarg); + + token = get_token(); + if(token != CRB) + syntax_error(); + + if(test_token(SEMI)) { /* a prototype function !! */ + symtab[fptr].type |= PROTO; + in_function = 0; + exit_label = 0; + exit_flag = 0; + exit_used = 0; + dim_top = dim_save; + if (symtab[fptr].type & EXTERNAL) + gen_extern_DASM(fptr); + } + else { + symtab[fptr].type &= ~PROTO; + symtab[fptr].type |= FUNCTION; + local_stack = 0; + exit_label = 0; + exit_flag = 0; + exit_used = 0; + stackpop = 0; + + /* Loop through the args putting in correct stack offsets + NOTE: For running 32 bit code in a 16 bit environment, the + value to be added to stackpop is 6 for each item on the stack. + For a 32 bit environment its 8 for each. + + +-----+-----+ xx + |Last param | The last parameter pushed is here + +-----+-----+ 06/8/0C + | n Bytes | Return Address (2, 4 or 8) + +-----+-----+ 04 + | 4 Bytes | Previous Frame Pointer + +-----+-----+ 00 + + The Return Address is either 2, 4 or 8 bytes depending on + the environment and whether the call was Near or Far. + 16 Bit segments - n = 2 for Near, 4 for Far + 32 Bit segments - n = 4 for Near, 8 for Far + + */ + + for(i=arg_count; i>0; i--) { + if (symtab[fptr].type & FAR) + symtab[MAX_SYMBOLS-i].argoffset = stackpop+12; /* 12 for 32 far */ + else + symtab[MAX_SYMBOLS-i].argoffset = stackpop+8; /* 8 for MMURTL */ + stackpop += 4; + } + statement(token=get_token()); + check_func(); /* ensure enter gets written in null func */ + if((exit_label) && (exit_used)) + gen_label(exit_label); + if (symtab[fptr].type & VARARGS) + end_func(0); + else + end_func(stackpop); + in_function = 0; + exit_label = 0; + exit_flag = 0; + exit_used = 0; + dim_top = dim_save; + + /* Error reporting when end of func is reached and certain + conditions exist */ + + while(local_top < MAX_SYMBOLS) { + if((token = symtab[local_top].type) & EXTERNAL) + t_error("Unresolved", &LPool[symtab[local_top].oname]); + if(!(token & REFERENCE)) + t_warn("Unreferenced", &LPool[symtab[local_top].oname]); + ++local_top; } + return; + } +} + +/*********************************************************************** + Write an assembler string to access an operand value. + Certain types of operands may require type inducers suxh as "BYTE PTR." +************************************************************************/ +static void write_oper(U32 token, U32 value, U32 type, U32 offset) +{ + + switch(token) { + case INEAX: + code_str("EAX"); + break; + case NUMBER: + code_num(value); + break; + case STRING: + code_str("OFFSET "); + code_chr(prefix); + code_str("_lit+"); + code_num(value); + break; + case SYMBOL: + if(type & GLOBAL) { + code_chr('_'); /* prefix with _ */ + code_str(&GPool[symtab[value].oname]); + break; } + if(type & ARGUMENT) + { + if (symtab[fptr].type & VARARGS) + { + if (type&(DWORD|POINTER)) + code_str("DWORD PTR [EBP+EDI+"); + else if (type & WORD) + code_str("WORD PTR [EBP+EDI+"); + else + code_str("BYTE PTR [EBP+EDI+"); + code_num(symtab[value].argoffset); + code_chr(']'); + } + else + { + if (type&(DWORD|POINTER)) + code_str("DWORD PTR [EBP+"); + else if (type & WORD) + code_str("WORD PTR [EBP+"); + else + code_str("BYTE PTR [EBP+"); + code_num(symtab[value].argoffset); + code_chr(']'); + } + } + else + { + if (type&(DWORD|POINTER)) + code_str("DWORD PTR [EBP-"); + else if (type & WORD) + code_str("WORD PTR [EBP-"); + else + code_str("BYTE PTR [EBP-"); + code_num(symtab[value].argoffset); + code_chr(']'); + } + break; + case INECX: + code_str("ECX"); + break; + case INEDX: + code_str("EDX"); + break; + case PESI: + case PEDX: + case PECX: /* pointer in other reg - indirect access */ + case ISTACK_TOP: + if (type&(DWORD|POINTER)) + code_str("DWORD PTR "); + else if (type & WORD) + code_str("WORD PTR "); + else + code_str("BYTE PTR "); + + if (offset) + { + if (token==PESI) + code_str("[ESI+"); + else if (token==PECX) + code_str("[ECX+"); + else if (token==PEDX) + code_str("[EDX+"); + else + code_str("[EBX+"); + code_num(offset); + code_chr(']'); + } + else + { + if (token==PESI) + code_str("[ESI]"); + else if (token==PEDX) + code_str("[EDX]"); + else if (token==PECX) + code_str("[ECX]"); + else + code_str("[EBX]"); + } + break; + case STACK_TOP: + code_str("EBX"); + break; + + case ION_STACK: /* shouldn't happen */ + case ON_STACK: + default: /* Unknown (error) */ + code_num(token); + code_str(" ERROR in write_oper\n"); + } +} + + +/************************************************** + Places operand in code string and sends instruction + out to code segment. +**************************************************/ + +static void GenCodeOper(char *ptr, U32 token, U32 value, U32 type, U32 offset) +{ + /* interpret the output string & insert the operand */ + + code_chr('\t'); + while(*ptr) { + if(*ptr == '|') + write_oper(token, value, type, offset); + else + code_chr(*ptr); + ++ptr; + } + code_chr('\n'); +} + +/************************************************************* + Examine the expression stack and see if any active token + is in EAX. If so, place it on the stack. + The stack top is really the EBX register. If we find + an active item in the EBX register, we must place + it on the real processor stack first and change it's + token to show where it is. +**************************************************************/ + +static void StackEAX(void) +{ + S32 i, j; + + for(i=0; i < expr_ptr; ++i) { + if (expstk[i].token == INEAX) /* Found it */ + { + for(j=0; j < expr_ptr; ++j) + { + if ((expstk[j].token == STACK_TOP) || + (expstk[j].token == ISTACK_TOP)) + { + out_inst("PUSH EBX"); + if (expstk[j].token == STACK_TOP) + expstk[j].token = ON_STACK; + else + expstk[j].token = ION_STACK; + break; + } + } + test_not(); + out_inst("MOV EBX,EAX"); + expstk[i].token = STACK_TOP; + } + } +} + +/********************************************************** + If the token we need is actually on the processor stack, + we must must pop it out so we can use it. To do this, + we pop it into EDX. EDX is only used for multiply and + divide operations otherwise. +***********************************************************/ +static U32 CheckStack(U32 token) +{ + if (token==ION_STACK) + { + out_inst("POP EDX"); + token = PEDX; + } + else if (token==ON_STACK) + { + out_inst("POP EDX"); + token = INEDX; + } + return(token); +} + +/************************************************************** + Get a parameter into the EAX register. + The value/register is always sign or zero extended to 32 bits! +***************************************************************/ + +static void LoadEAX(U32 token, U32 value, U32 type, U32 offset) +{ + if(type & FUNCTION) + type_error(); + + if(token == INEAX) { /* Already there */ + test_not(); + return; + } + + token = CheckStack(token); /* If it's on the processor stack, get it into EDX */ + + StackEAX(); /* stack EAX if needed */ + not_flag = 0; + + if((token == NUMBER) && (!value)) { /* 0 Value */ + test_not(); + code_str("\tXOR EAX,EAX\n"); + return; + } + + if ((type&(DWORD|POINTER)) || + (token == NUMBER) || + (token == INEDX) || + (token == STACK_TOP) || + (token == INECX)) + + GenCodeOper("MOV EAX,|", token, value, type, offset); + else if (type & WORD) + { + if (type & UNSIGNED) + GenCodeOper("MOVZX EAX,|", token, value, type, offset); + else + GenCodeOper("MOVSX EAX,|", token, value, type, offset); + } + else + { + if (type & UNSIGNED) + { + code_str("\tXOR EAX,EAX\n"); + GenCodeOper("MOV AL,|", token, value, type, offset); + } + else + GenCodeOper("MOVSX EAX,|", token, value, type, offset); + } + + zero_flag = -1; +} + +/************************************************************** + Get a parameter into the ECX register. + The value/register is always sign or zero extended to 32 bits! +***************************************************************/ + +static void LoadECX(U32 token, U32 value, U32 type, U32 offset) +{ + if(type & FUNCTION) + type_error(); + + if(token == INECX) + { /* Already there */ + return; + } + + token = CheckStack(token); /* If it's on the processor stack, get it into EDX */ + + if ((token == NUMBER) && (!value)) + { /* 0 Value */ + code_str("\tXOR ECX,ECX\n"); + return; + } + + if ((type&(DWORD|POINTER)) || + (token == NUMBER) || + (token == INEDX) || + (token == STACK_TOP) || + (token == INEAX)) + + GenCodeOper("MOV ECX,|", token, value, type, offset); + else if (type & WORD) { + if (type & UNSIGNED) + GenCodeOper("MOVZX ECX,|", token, value, type, offset); + else + GenCodeOper("MOVSX ECX,|", token, value, type, offset); + } + else + { + if (type & UNSIGNED) + { + code_str("\tXOR ECX,ECX\n"); + GenCodeOper("MOV CL,|", token, value, type, offset); + } + else + GenCodeOper("MOVSX ECX,|", token, value, type, offset); + } +} + + +/**************************************************************** +* Evaluate a sub expression & handle COMMA operator. This must +* be done as a special case, because the function performed by +* COMMA differs with the context of the expression, and can not +* therefore be handled as a general operator. +*****************************************************************/ + +static void sub_eval(U32 term) +{ +U32 token; + + for(;;) { + if((token = do_oper(SEMI)) != COMMA) { + unget_token(token); + expect(term); + return; + } + pop(&token, &token, &token, &token); /* throw it away */ + } +} + + +/******************************************************** +* Evaluate a full expression at the highest level, and +* load the result into the accumulator if necessary. +*********************************************************/ + +static void eval(U32 term, char flag) +{ +U32 token, value, type, offset; + + expr_ptr = 0; + not_flag = 0; + sub_eval(term); + + pop(&token, &value, &type, &offset); + if((token != INEAX) || flag) + LoadEAX(token, value, type, offset); +} + +/************************************************ +* Write an instruction with text formatting + to the code tmp file +*************************************************/ + +static void out_inst(char *ptr) +{ + code_chr('\t'); + code_str(ptr); + code_chr('\n'); +} + + +/************************************************************* + Examine the expression stack and see if any active token + is in the EBX register which acts as the stack top for + fast access. If so, put it on the real stack because + we must preserve it through a call. +**************************************************************/ + +static void StackTop(void) +{ + S32 j; + + for(j=0; j < expr_ptr; ++j) + { + if ((expstk[j].token == STACK_TOP) || + (expstk[j].token == ISTACK_TOP)) + { + out_inst("PUSH EBX"); + if (expstk[j].token == STACK_TOP) + expstk[j].token = ON_STACK; + else + expstk[j].token = ION_STACK; + break; + } + } +} + + +/************************************************************* + Examine the expression stack and see if any active token + is in the Index Reg. If so, place it on the stack. + The stack top is really the EBX register. If we find + an active item in the EBX register, we must place + it on the real processor stack first and change it's + token to show where it is. +**************************************************************/ + +static void StackESI(void) +{ + S32 i, j; + + for(i=0; i < expr_ptr; ++i) { + if (expstk[i].token == PESI) /* Found it */ + { + for(j=0; j < expr_ptr; ++j) + { + if ((expstk[j].token == STACK_TOP) || + (expstk[j].token == ISTACK_TOP)) + { + out_inst("PUSH EBX"); + if (expstk[j].token == STACK_TOP) + expstk[j].token = ON_STACK; + else + expstk[j].token = ION_STACK; + break; + } + } + out_inst("MOV EBX,ESI"); + expstk[i].token = ISTACK_TOP; + } + } +} + + +/***************************************** +* Load index address for an array +******************************************/ + +static void load_index(U32 t, U32 v, U32 tt, U32 o) +{ + StackESI(); + if((tt & ARGUMENT) || !(tt & ARRAY)) /* pointer or argument */ + index_ptr(t, v, tt, o); + else /* standard array */ + index_adr(t, v, tt, o); +} + + +/************************************************************************* +* Evaluate a unary operation, if possible, evaluate constant expressions +* into another constant. Produce code to perform operation if necessary. +**************************************************************************/ + +static void do_unary(U32 oper) +{ + U32 token, value, type, offset; + char flag; + + pop(&token, &value, &type, &offset); + flag = 0; + /* Evaluate any operations that can be performed at compile time */ + if(token == NUMBER) + { + flag = -1; + switch(oper) + { + case SUB : /* unary minus */ + value = -value; + break; + case COM: /* ones complement */ + value = ~value; + break; + case NOT: /* logical complement */ + value = !value; + break; + default: + flag = 0; + } + } + + /* Generate code to perform operation */ + if(!flag) + { + switch(oper) + { + case SUB: /* unary minus */ + GenCodeOper("NEG |", token, value, type, offset); + break; + case COM: /* ones complement */ + GenCodeOper("NOT |", token, value, type, offset); + break; + case NOT: /* logical complement */ + LoadEAX(token, value, type, offset); + token = INEAX; + not_flag = TRUE; + break; + case INC: /* '++' increment token & load */ + if (ispStruct(type, value)) + GenCodeOper("ADD |,strucsize", token, value, type, offset); + else if (isp32(type)) + GenCodeOper("ADD |,4", token, value, type, offset); + else if (isp16(type)) + GenCodeOper("ADD |,2", token, value, type, offset); + else + GenCodeOper("INC |", token, value, type, offset); + break; + case DEC: /* '--' decrement & store */ + if (ispStruct(type, value)) + GenCodeOper("SUB |,strucsize", token, value, type, offset); + else if (isp32(type)) + GenCodeOper("SUB |,4", token, value, type, offset); + else if (isp16(type)) + GenCodeOper("SUB |,2", token, value, type, offset); + else + GenCodeOper("DEC |", token, value, type, offset); + break; + default: + syntax_error(); + } + } + push(token, value, type, offset); +} + + +/****************************************************** + This evaluates array indeces for get_value. +*******************************************************/ + +static S8 eval_index(U32 t, U32 v, U32 tp, U32 ofs, U32 *tpRet) +{ + S32 ndim; + U32 tp1, dptr, iSym, vsize, ofs1; + char fMultiDim; + + fMultiDim = FALSE; /* true when processing second or subsequent dims */ + + if(tp & ARRAY) { /* array, get # dimensions */ + dptr = symtab[v].dindex; + ndim = dim_pool[dptr++]; + } + else /* pointer, fake # dims as 1 */ + dptr = ndim = 1; + + iSym = v; /* save index into symtab */ + + push(t, v, tp, ofs); /* save symbol we are indexing on exp stack */ + + do { /* calculate index */ + + /* this gets the size of variable into vsize */ + + v = tp & (POINTER | ARRAY); + if ((v==ARRAY) || (v < 2)) { + if (tp & BYTE) vsize = 1; + else if (tp & WORD) vsize = 2; + else if (tp & DWORD) vsize = 4; + else if (tp & STRUCT) + vsize = symtab[iSym].strucoff; /* strucoff = size of struc*/ + } + else vsize = 4; + --ndim; + if(tp & ARRAY) /* array reference */ + { + t = ++dptr; + if(!(v = ndim)) /* all indices given, load pointer */ + tp &= ~ARRAY; /* Not an array ref anymore, ptr instead */ + while(v--) + vsize *= dim_pool[t++]; + } + else + { + if(tp & POINTER) + { /* pointer reference */ + --tp; /* drop defer level by one */ + if(fMultiDim) + { /* array of pointers */ + pop(&t, &v, &tp1, &ofs1); + LoadEAX(t, v, tp1, ofs1); + pop(&t, &v, &tp1, &ofs1); + load_index(t, v, tp1, ofs1); + out_inst("ADD ESI,EAX"); + push(t = PESI, v, tp, ofs1); + fMultiDim = 0; + } + } + else /* invalid indexing */ + index_error(); + } + sub_eval(CSB); /* GET the value inside the [] */ + + /* Multiply [] by size of index */ + + if(vsize != 1) { /* optimize away size of 1 */ + push(NUMBER, vsize, DWORD, 0); + do_lr2op(STAR); /* multiply by size */ + } + if(fMultiDim) do_lr2op(ADD); /* add to last index if there */ + fMultiDim = TRUE; /* if there is another... */ + } + while(test_token(OSB)); + + *tpRet = tp; + return fMultiDim; +} + +/****************************************************** + Gets the next token and perform any processing + required to evaluate it. This includes structure + members. +*******************************************************/ + +static void get_value(void) +{ + S32 ndim, vcnt; + U32 i, j, size, token, t, v, tp, tp1, ofs; + char fMultiDim, fvarargs; + + switch(token = get_token()) { + case NUMBER: /* a constant number */ + t = NUMBER; /* constant */ + v = gvalue; /* value of number */ + tp = DWORD; /* all constants are DWORDS */ + ofs = 0; /* no constant offset */ + break; + case STRING: /* a literal string */ + t = STRING; /* will be found in lit pool */ + v = gvalue; /* length of string */ + tp = BYTE | 1; /* 1 is level of indirection */ + ofs = 0; /* offset of zero */ + break; + case SYMBOL: /* symbol value */ + if(!(lookup_local() || lookup_global())) { /* not defined */ + if(test_token(ORB)) { /* function, but no proto! */ + symbol_error("Function not prototyped"); + } + else /* variable, report error */ + symbol_error("Undefined symbol"); + } + t = SYMBOL; + v = sptr; /* symtab entry */ + tp = symtab[v].type; + ofs = 0; /* offset of zero */ + break; + case STAR: /* pointer dereference */ + get_value(); + pop(&t, &v, &tp, &ofs); + StackESI(); + index_ptr(t, v, tp, ofs); + t = PESI; + if(tp & POINTER) + --tp; + else + index_error(); + break; + case AND: /* address of */ + get_value(); + pop(&t, &v, &tp, &ofs); + if(t == SYMBOL) + { + StackEAX(); + code_str((tp & GLOBAL) ? "\tMOV EAX,OFFSET " : "\tLEA EAX,"); + write_oper(t, v, tp, ofs); + code_chr('\n'); + not_flag=0; + tp = (tp + 1) & ~FUNCTION; /* tp + 1 ups the ptr ref count */ + t = INEAX; + } + else if (t == PESI) + { + StackEAX(); + not_flag=0; + out_inst("MOV EAX,ESI"); + tp = (tp + 1) & ~FUNCTION; /* tp + 1 ups the ptr ref count */ + t = INEAX; + } + else if ((t == INEAX) && (tp & POINTER)) + { /* do nothing... it's there. */ } + else + line_error("Invalid '&' operation"); + break; + case ORB: /* sub-expression */ + sub_eval(CRB); + pop(&t, &v, &tp, &ofs); + break; + case SIZEOF: /* sizeof */ + if(test_token(ORB)) { + get_value(); /* look for a symbol */ + pop(&t, &v, &tp, &ofs); + if (t == SYMBOL) { + if (tp & POINTER) size = 4; + else if (tp & BYTE) size = 1; + else if (tp & WORD) size = 2; + else if (tp & DWORD) size = 4; + else if (tp & STRUCT) + size = symtab[v].strucoff; /* strucoff = size */ + else size = 4; + if ((tp & ARRAY) && (!(tp & POINTER))) { /* array ref */ + i = symtab[v].dindex; /* i = index to dimpool */ + j = dim_pool[i++]; /* j = nDims */ + while(j--) size *= dim_pool[i++]; + } + t = NUMBER; + tp = DWORD; /* all constants are DWORDS */ + v = size; + push(t, v, tp, 0); /* PUSH THE "Value" ON THE EXP STACK */ + expect(CRB); + return; + } + else + line_error("Symbol expected"); + } + else + line_error("'(' expected"); + break; + default: /* anything else (operators) */ + get_value(); /* look for a value */ + do_unary(token); + return; + } + +/* Function calls - Open Round Brackett (ORB) */ + + if(test_token(ORB)) + { + iarg = symtab[v].dindex; /* index to prototyped args! */ + push(t, v, tp, ofs); + StackEAX(); + StackESI(); + if (tp & VARARGS) fvarargs = -1; + else fvarargs = 0; + vcnt = ndim = 0; + StackTop(); + if(!test_token(CRB)) + { /* evaluate function operands */ + do { + argtype = proto_list[iarg]; /* current arg type */ + if(!(argtype&VOID)) iarg++; /* if not end of proto args, get next*/ + token = do_oper(SEMI); /* handle operation on arg */ + pop(&t, &v, &tp, &ofs); /* get token arg from expstack */ + LoadEAX(t, v, tp, ofs); + if ((t != PESI) && (ofs) && (tp&POINTER)) + { /* offset must be added */ + code_str("\tADD EAX,"); + code_num(ofs); + code_chr('\n'); + } + out_inst("PUSH EAX"); + + ++ndim; + if ((!fvarargs) || (fvarargs && (argtype==VOID))) { + ++vcnt; + } + } + while(token == COMMA); + if(token != CRB) + syntax_error(); + } + iarg = 0; + pop(&t, &v, &tp, &ofs); /* get function back off the expr stack */ + + /* if the function we are about to call had variable parameters + then put the count of bytes that were pushed into EDI as the + code in a vararg function uses it to access the fixed args. + Also, we leave ndim (total count of bytes push) with it's + value so that the "call" function knows he must remove the + data from the stack and the "end_func" function knows not + to use the RET XX instruction. + */ + + if (fvarargs) { + code_str("\tMOV EDI, "); + code_num(vcnt*4); + code_chr('\n'); + } + else ndim = 0; + + call(t, v, tp, ofs, ndim); + + t = INEAX; /* set up to leave the return value in ACC */ + tp &= ~FUNCTION; + } + + + +/* Indexing operations - Open Square Brackett (OSB) */ + + fMultiDim = 0; + if(test_token(OSB)) { + fMultiDim = eval_index(t, v, tp, ofs, &tp); + + /* get index value token pushed by eval_index */ + + pop(&token, &v, &t, &ofs); + if ((token==NUMBER) & (!v)) + { /* index is 0 */ + pop(&t, &v, &tp1, &ofs); /* Get var base */ + if ((tp1 & (POINTER|ARRAY)) && (t!=PESI)) + load_index(t, v, tp1, ofs); /* Load base into Index reg */ + } + else + { + LoadEAX(token, v, t, ofs); /* load it into ACC */ + pop(&t, &v, &tp1, &ofs); /* Get var base */ + if (tp1 & (POINTER|ARRAY) && (t!=PESI)) + load_index(t, v, tp1, ofs); /* Load base into Index reg */ + out_inst("ADD ESI,EAX"); + } + t = PESI; /* Let em know ESI pts to item */ + } + +/* Convert any [UNINDEXED] array references to address values. + This is done later to structures and struct members. +*/ + + if((tp & ARRAY) && (!(tp & (STRUCT|STRUCMEM)))) { + tp = (tp + 1) & ~ARRAY; /* tp+1 ups the ptr ref count */ + if(!(tp & ARGUMENT)) + { + if(!fMultiDim) + { + StackEAX(); /* save what's in EAX and ESI */ + StackESI(); + index_adr(t, v, tp, ofs); /* load address of rg into ESI */ + } + StackEAX(); /* save what's in EAX if needed */ + not_flag = 0; + out_inst("MOV EAX,ESI"); + t = INEAX; + } + } + +/* handle the dereference operators . and -> for + structures and pointers to structures if present */ + + + if(test_token(DEREF)) { + if ((tp & STRUCT) && ((tp & POINTER) || (t==PESI)) ) { + if (t == SYMBOL) { /* may already be PESI */ + StackEAX(); /* save what's in EAX */ + StackESI(); /* stack ESI */ + index_ptr(t, v, tp, ofs); /* pointer base into ESI */ + t = PESI; /* tell em it's indirect now */ + } + if (test_token(SYMBOL)) { + if (lookup_member(v)) { + memoffset = symtab[sptr].strucoff; + tp = symtab[sptr].type; + v = sptr; + ofs = memoffset; /* NEW */ + + if (tp & ARRAY) + tp = (tp + 1) & ~ARRAY; /* tp+1 ups the ptr ref count */ + t = PESI; /* tell em it's indirect now */ + } + else + line_error("Not a structure member"); + } + else + line_error("Structure member expected"); + } + else + line_error("Pointer to Struct expected"); + } + + if (test_token(DOT)) { + if ((tp & STRUCT) && (!(tp & POINTER))) { + if (t == SYMBOL) { /* may already be PESI */ + StackEAX(); /* save what's in EAX */ + StackESI(); /* stack ESI */ + index_adr(t, v, tp, ofs); /* ptr to base of struct into ESI */ + t = PESI; + } + if (test_token(SYMBOL)) { + if (lookup_member(v)) { + memoffset = symtab[sptr].strucoff; + tp = symtab[sptr].type; /* member type */ + v = sptr; /* symtab entry of member */ + ofs = memoffset; /* NEW */ + + t = PESI; /* tell em it's indirect now */ + + } + else + line_error("Structure member expected"); + } + else + line_error("Structure member expected"); + } + else + line_error("Invalid structure operation"); + } + +/* Indexing operations for STRUCTMEMS- Open Square Brackett (OSB) */ + + fMultiDim = 0; + if(test_token(OSB)) { + fMultiDim = eval_index(t, v, tp, ofs, &tp); + pop(&token, &v, &t, &ofs); /* get index value token just pushed */ + if ((token==NUMBER) & (!v)) + { /* index is 0 */ + pop(&t, &v, &tp1, &ofs); /* Get var base */ + if (tp1 & (POINTER|ARRAY) && (t!=PESI)) + { + load_index(t, v, tp1, ofs); /* Load base into Index reg */ + if ((t==ISTACK_TOP) && + (symtab[v].type & ARRAY)) + t=STACK_TOP; + } + } + else { + LoadEAX(token, v, t, ofs); /* load it into ACC */ + pop(&t, &v, &tp1, &ofs); /* Get var base */ + if (tp1 & (POINTER|ARRAY) && (t!=PESI)) { + if ((t==ISTACK_TOP) && + (symtab[v].type & ARRAY)) + t=STACK_TOP; + load_index(t, v, tp1, ofs); /* Load base into Index reg */ + } + out_inst("ADD ESI,EAX"); + } + t = PESI; /* Let em know ESI pts to item */ + } + + +/* Convert any [UNINDEXED] array references to address values + for struct members that are arrays. +*/ + + if((tp & ARRAY) && (tp & (STRUCMEM))) { + tp = (tp + 1) & ~ARRAY; /* tp+1 ups the ptr ref count */ + if(!(tp & ARGUMENT)) + { + if(!fMultiDim) + { + StackEAX(); /* stack EAX */ + StackESI(); /* stack ESI */ + index_adr(t, v, tp, ofs); /* load address of rg into ESI */ + } + StackEAX(); /* stack EAX if needed */ + not_flag = 0; + out_inst("MOV EAX,ESI"); + t = INEAX; + } + } + +/* handle any post operators (++ and --) if present */ + + if(test_token(INC)) { /* post '++' */ + if (tp & POINTER) + { + load_index(t, v, tp, ofs); + } + else + LoadEAX(t, v, tp, ofs); + if (ispStruct(tp, v)) + GenCodeOper("ADD |,strucsize", t, v, tp, ofs); + else if (isp32(tp)) + GenCodeOper("ADD |,4", t, v, tp, ofs); + else if (isp16(tp)) + GenCodeOper("ADD |,2", t, v, tp, ofs); + else + GenCodeOper("INC |", t, v, tp, ofs); + if (tp & POINTER) + { + t = PESI; + } + else + t = INEAX; + } + + else if(test_token(DEC)) + { /* post '--' */ + if (tp & POINTER) + { + load_index(t, v, tp, ofs); + } + else + LoadEAX(t, v, tp, ofs); + if (ispStruct(tp, v)) + GenCodeOper("SUB |,strucsize", t, v, tp, ofs); + else if (isp32(tp)) + GenCodeOper("SUB |,4", t, v, tp, ofs); + else if (isp16(tp)) + GenCodeOper("SUB |,2", t, v, tp, ofs); + else + GenCodeOper("DEC |", t, v, tp, ofs); + if (tp & POINTER) + { + t = PESI; + } + else + t = INEAX; + } + push(t, v, tp, ofs); /* FINALLY PUSH THE "Value" ON THE EXP STACK */ +} + +/**************************************************** + Combine two types (For binary operations) + This raises the type to the lowest common type, or + in the case of a strucr-member operation, the type + is the type become the member's type. +*****************************************************/ + +static U32 combine(U32 type1, U32 type2) +{ + U32 new_type; + +/* preserve width and indirection if pointer operation is involved */ +/* if neither is pointer no harm is done with this code */ + + if ((type1 & POINTER) >= (type2 & POINTER)) + new_type = type1 & POINTER; + else + new_type = type2 & POINTER; + +/* raise to lowest common type */ + + if ((type1 & DWORD) || (type2 & DWORD)) new_type |= DWORD; + else if ((type1 & WORD) || (type2 & WORD)) new_type |= WORD; + else new_type |= BYTE; + +/* ANSI says: If combining unsigned and signed types and signed var + is large enough to hold all values of unsigned then combined type + remains SIGNED, else the type becomes UNSIGNED! +*/ + + if (!((type1 & type2) & UNSIGNED)) /* both signed */ + return new_type; + + if ((type1 & UNSIGNED) && (type2 & UNSIGNED)) { /* both unsigned */ + new_type |=UNSIGNED; + return new_type; + } + + /* ONLY one is signed... */ + + if (((type1 & SIZEMASK) >= (type2 & SIZEMASK)) && (type2 & UNSIGNED)) + new_type |= UNSIGNED; + + else + if (((type1 & SIZEMASK) <= (type2 & SIZEMASK)) && (type1 & UNSIGNED)) + new_type |= UNSIGNED; + + return new_type; + +} + +/********************************************************** +* Perform an operation & all higher priority operations. +***********************************************************/ + +static U32 do_oper(U32 token) +{ + U32 token1, t, v, tp, tp1, ofs, ofs1, exit_lab; + + /* Handle "special" binary operators which involve jumps */ + if((token == DAND) || (token == DOR) || (token == QUEST)) + { + pop(&t, &v, &tp, &ofs); + StackEAX(); /* stack EAX */ + StackESI(); /* stack ESI */ + if(t != INEAX) + LoadEAX(t, v, tp, ofs); + exit_lab = ++next_lab; + if(token == QUEST) + { /* conditional expression */ + cond_jump(FALSE, token1 = ++next_lab, 0); + sub_eval(COLON); + pop(&t, &v, &tp, &ofs); + LoadEAX(t, v, tp, ofs); + jump(exit_lab, 0); + gen_label(token1); + } + else + { /* && and || */ + test_not(); + if(token == DAND) + cond_jump(FALSE, exit_lab, -1); + else + cond_jump(TRUE, exit_lab, -1); + } + token1 = do_oper(SEMI); + pop(&t, &v, &tp1, &ofs1); + LoadEAX(t, v, tp1, ofs1); + gen_label(exit_lab); + push(INEAX, v, combine(tp, tp1), ofs1); + return token1; + } + + get_value(); /* stack the value (number or whatever) */ + +/* Handle operator precedence and grouping. optype is 2 if operator + separates two operands and grouping is L-R. It's 3 if R-L */ + + token1 = get_token(); /* Look at next operator */ + while((optype[token1] > 1) && + (priority[token1] >= priority[token])) + { + /* if they are the same priority AND grouping == L-R */ + + if((priority[token1] == priority[token]) && + (optype[token] == 2)) + { + do_lr2op(token); + return do_oper(token1); + } + + token1 = do_oper(token1); + } + +/* Perform the operation */ + if(token!=SEMI) + do_lr2op(token); + + return token1; +} + + +/************************************************************************* + Evaluates two operands for operators with L to R grouping. + if possible, evaluate constant expressions into another constant. + Produce code to perform operation if necessary. + The two operands are on the top of the expression stack and are + loaded into token and token1. The item at the top of the stack is the + second operand. This means that if it were "A minus B", A would go into + token and B would go into token1. +**************************************************************************/ + +static void do_lr2op(U32 oper) +{ + U32 token, value, type, token1, value1, type1, offset, offset1; + U32 atoken, avalue, atype, temp, ctype; + U32 uflag, swap, order, wheretok; + + pop(&token1, &value1, &type1, &offset1); + pop(&token, &value, &type, &offset); + + uflag = swap = order = atoken = 0; + + /* Constant numbers assume the type of the other operand */ + if(token == NUMBER) + { + swap = 1; + type = type1; + } + if(token1 == NUMBER) + { + swap = 0; + type1 = type; + } + + ctype = combine(type, type1); + + /* Do any operations that can be performed at compile time */ + + if((token == NUMBER) && (token1 == NUMBER) && + (oper != DAND) && (oper != DOR)) { + switch(oper) { + case ADD : + value += value1; + break; + case SUB : + value -= value1; + break; + case STAR: + value *= value1; + break; + case DIV: + value /= value1; + break; + case MOD: + value %= value1; + break; + case AND: + value &= value1; + break; + case OR: + value |= value1; + break; + case XOR: + value ^= value1; + break; + case SHL: + value <<= value1; + break; + case SHR: + value >>= value1; + break; + case EQ: + value = value == value1; + break; + case NE: + value = value != value1; + break; + case LT: + value = value < value1; + case LE: + value = value <= value1; + break; + case GT: + value = value > value1; + break; + case GE: + value = value >= value1; + break; + default: + syntax_error(); + } + } + else + { + /* Generate code to perform the operation */ + avalue = value; + atype = type; + wheretok = INEAX; /* default place for result */ + + /* Use unsigned operations if needed */ + if((type | type1) & (POINTER | UNSIGNED)) + uflag = 1; + + /* Try and re-arrange for partial results already in ACC */ + if(token1 == INEAX) + swap = 1; + + /* This first switch sets up self assignments and + changes the operand to the real operation to be + performed + */ + + switch(oper) + { + case DAND: /* logical AND and OR are done elsewhere */ + case DOR: + push(token1, value1, type1, offset1); + return; + case ADDE: + atoken = token; /* nonzero atoken indicates self assignment*/ + oper = ADD; + break; + case SUBE: + atoken = token; + oper = SUB; + break; + case STARE: + atoken = token; + oper = STAR; + break; + case DIVE: + oper = DIV; + atoken = token; + break; + case MODE: + oper = MOD; + atoken = token; + break; + case SHLE: + oper = SHL; + atoken = token; + break; + case SHRE: + oper = SHR; + atoken = token; + break; + case ANDE: + oper = AND; + atoken = token; + break; + case ORE: + oper = OR; + atoken = token; + break; + case XORE: + oper = XOR; + atoken = token; + break; + } + + /* this next switch looks for operations that MUST + use special registers (such as EAX or EDX). + Or must be in a special order for a particular + operation. + */ + switch(oper) + { + case SUB: + case DIV: + case MOD: + case SHL: + case SHR: + order = 1; + break; + case LT: + if (swap) { + if (uflag) + oper = UGT; + else + oper = GT; + } + else if (uflag) + oper = ULT; + break; + case LE: + if (swap) { + if (uflag) + oper = UGE; + else + oper = GE; + } + else if (uflag) + oper = ULE; + break; + case GT: + if (swap) { + if (uflag) + oper = ULT; + else + oper = LT; + } + else if (uflag) + oper = UGT; + break; + case GE: + if (swap) { + if (uflag) + oper = ULE; + else + oper = LE; + } + else if (uflag) + oper = UGE; + break; + } + + /* Now we can use the flags to set up a swap of operands if + indicated so long as ordering was not important (order flag). + Some operations require the use of EAX. + */ + + if((token1 == INEAX) && order) + { /* out of order - use ECX */ + test_not(); + out_inst("MOV ECX,EAX"); + token1 = INECX; + } + + if(swap && !order) + { + temp = token; token = token1; token1 = temp; + temp = value; value = value1; value1 = temp; + temp = type; type = type1; type1 = temp; + temp = offset; offset = offset1; offset1 = temp; + } + + if(type1 & FUNCTION) + type_error(); + + if (oper != ASSIGN) /* simple assignment is optimized case */ + { + LoadEAX(token, value, type, offset); /* insure its loaded */ + } + + /* If it's on the processor stack, get it into EDX */ + token1 = CheckStack(token1); + + /* Now we perform the operation */ + + switch(oper) + { + case ASSIGN: + /* optimize away an assigment */ + if ((token1 == token) && (value == value1)) + { + wheretok = token; + break; + } + if (swap) /* token to store is already in EAX */ + { + store(token1, value1, type1, offset1); + } + else /* token1 may not be loaded already */ + { + LoadEAX(token1, value1, type1, offset1); + store(token, value, type, offset); + } + break; + case ADD: + if (type1&(DWORD|POINTER)) + GenCodeOper("ADD EAX,|", token1, value1, type1, offset1); + else + { + LoadECX(token1, value1, type1, offset1); + out_inst("ADD EAX,ECX"); + } + zero_flag = 0; + break; + case SUB: + if (type1&(DWORD|POINTER)) + GenCodeOper("SUB EAX,|", token1, value1, type1, offset1); + else + { + LoadECX(token1, value1, type1, offset1); + out_inst("SUB EAX,ECX"); + } + zero_flag = 0; + break; + case STAR: + LoadECX(token1, value1, type1, offset1); + if (ctype & UNSIGNED) + out_inst("MUL ECX"); + else + out_inst("IMUL ECX"); + zero_flag = 1; + break; + case DIV: + case MOD: + LoadECX(token1, value1, type1, offset1); + if (ctype & UNSIGNED) + { + out_inst("XOR EDX,EDX"); + out_inst("DIV ECX"); + } + else + { + out_inst("CDQ"); + out_inst("IDIV ECX"); + } + zero_flag = -1; + if (oper == MOD) + out_inst("MOV EAX,EDX"); + break; + case AND: + if (type1 & (DWORD|POINTER)) + GenCodeOper("AND EAX,|", token1, value1, type1, offset1); + else + { + LoadECX(token1, value1, type1, offset1); + out_inst("AND EAX,ECX"); + } + zero_flag = 0; + break; + case OR: + if (type1 & (DWORD|POINTER)) + GenCodeOper("OR EAX,|", token1, value1, type1, offset1); + else + { + LoadECX(token1, value1, type1, offset1); + out_inst("OR EAX,ECX"); + } + zero_flag = 0; + break; + case XOR: + if (type1 & (DWORD|POINTER)) + GenCodeOper("XOR EAX,|", token1, value1, type1, offset1); + else + { + LoadECX(token1, value1, type1, offset1); + out_inst("XOR EAX,ECX"); + } + zero_flag = 0; + break; + case SHL: + LoadECX(token1, value1, type1, offset1); + out_inst("SHL EAX,CL"); + if(ctype & WORD) + out_inst("AND EAX,0FFFFh"); + else if (ctype & BYTE) + out_inst("AND EAX,0FFh"); + zero_flag = 0; + break; + case SHR: + LoadECX(token1, value1, type1, offset1); + if (ctype & (DWORD | POINTER)) + out_inst("SHR EAX,CL"); + else if (ctype & WORD) + out_inst("SHR AX,CL"); + else + out_inst("SHR AL,CL"); + zero_flag = 0; + break; + case EQ: /* compare & test */ + case NE: + case LT: + case LE: + case GT: + case GE: + case ULT: + case ULE: + case UGT: + case UGE: + if (type1&(DWORD|POINTER)) + GenCodeOper("CMP EAX,|", token1, value1, type1, offset1); + else + { + LoadECX(token1, value1, type1, offset1); + out_inst("CMP EAX,ECX"); + } + switch(oper) + { + case EQ: out_inst("SETE AL"); break; + case NE: out_inst("SETNE AL"); break; + case LT: out_inst("SETL AL"); break; + case LE: out_inst("SETLE AL"); break; + case GT: out_inst("SETG AL"); break; + case GE: out_inst("SETGE AL"); break; + case ULT: out_inst("SETB AL"); break; + case ULE: out_inst("SETBE AL"); break; + case UGT: out_inst("SETA AL"); break; + case UGE: out_inst("SETAE AL"); break; + } + out_inst("AND AL,AL"); + zero_flag = 0; + break; + default: + syntax_error(); + } + + /* for self assigns, replace value */ + if(atoken) + store(atoken, avalue, atype, offset); + + token = wheretok; + } + + push(token, value, ctype, offset); +} + + + +/*************************************************** +* Test for a logically negated value, and update +* the accumulator if one is being used. +****************************************************/ + +static void test_not(void) +{ + if(not_flag) + { + out_inst("AND EAX,EAX"); + out_inst("SETZ AL"); + out_inst("AND AL,AL"); + not_flag = 0; + zero_flag = 0; + } +} + + +/******************************************** +* Store accumulator value +*********************************************/ + +static void store(U32 token, U32 value, U32 type, U32 offset) +{ + + token = CheckStack(token); /* get stack operand to EDX if needed */ + + switch(token) { + case SYMBOL: + case PESI: + case PECX: + case PEDX: + case ISTACK_TOP: + token = CheckStack(token); /* get stack operand to EDX if needed */ + if(type & (DWORD | POINTER)) + GenCodeOper("MOV |, EAX", token, value, type, offset); + else if (type & WORD) + GenCodeOper("MOV |, AX", token, value, type, offset); + else + GenCodeOper("MOV |, AL", token, value, type, offset); + zero_flag = -1; + break; + default: + line_error("Non-assignable"); + } +} + + +/********************************************* + Determine if a type is a pointer to struct +**********************************************/ + +static U32 ispStruct(U32 type, U32 value) +{ + if((type & POINTER) > 1) /* pointer to pointer */ + return 0; + if ((type & STRUCT) && (type & POINTER)) { + strucsize = symtab[value].strucoff; + return 1; + } + return 0; /* not our pointer */ +} + +/********************************************* + Determine if a type is a pointer to 32 bits +**********************************************/ + +static U32 isp32(U32 type) +{ + if(type & (POINTER-1)) /* pointer to pointer */ + return 1; + if(type & POINTER) /* first level pointer */ + return (type & DWORD); + return 0; /* not a pointer */ +} + +/*********************************************** + Determine if a type is a pointer to 16 bits +************************************************/ +static U32 isp16(U32 type) +{ + if(type & POINTER) /* first level pointer */ + return (type & WORD); + return 0; /* not a pointer */ +} + +/* + * Output a string to the assembler followed by newline. + */ +static void do_asm(char *ptr) +{ + code_str(ptr); +} + +/*************************** + Initialize static storage +****************************/ + +static void init_static(U32 token, U32 value, char word) +{ + char *ptr, *ptr1, *nptr; + U32 i; + + ptr = ""; + + if(global_width) /* continuing definition */ + ptr = ","; + else { /* new definition */ + if (word==1) ptr = " DB "; + else if (word==2) ptr = " DW "; + else if (word==4) ptr = " DD "; + } + + if(token == SYMBOL) { /* Symbol - MUST BE GLOBAL - NOT EXTERNAL */ + ptr1 = "OFFSET "; + global_width += 7; } + else if(token == STRING) { /* literal pool entry */ + ptr1 = "OFFSET L_lit+"; + *(ptr1+7) = prefix; + global_width += 23; } + else if(token == LABEL) { /* instruction label */ + ptr1 = "L_"; + *ptr1 = prefix; + global_width += 4; } + else { /* constant value */ + ptr1 = ""; + global_width += 6; } + + if (*ptr) data_str(ptr); + if (*ptr1) data_str(ptr1); + if (token!=SYMBOL) + data_num(value); + else { + nptr=&GPool[symtab[value].oname]; + data_str(nptr); + i=0; while(*nptr++) i+=1; + global_width += i; + } + + if(global_width > 60) { + global_width = 0; + data_chr('\n'); + } +} + +/******************************** +* End static storage definition +*********************************/ +static void end_static(void) +{ + if(global_width) { + data_chr('\n'); + global_width = 0; + } +} + +/******************************************************************* +* Define a global non-static variable +********************************************************************/ + +static void gen_global(U32 symbol, U32 size) +{ + data_global(symbol); + if(symtab[symbol].type & (POINTER | DWORD)) { + if (size==4) + data_str(" DD 0h\n"); + else { + data_str(" DD "); + data_num(size/4); + data_str(" DUP(0)\n"); + } + } + else + if(symtab[symbol].type & WORD) { + if (size==2) + data_str(" DW 0h\n"); + else { + data_str(" DW "); + data_num(size/2); + data_str(" DUP(0)\n"); + } + } + else + if(symtab[symbol].type & (BYTE|STRUCT)) { + if (size==1) + data_str(" DB 0h\n"); + else { + data_str(" DB "); + data_num(size); + data_str(" DUP(0)\n"); + } + } +} + +/**************************************************** + Define an external label for DASM near or FAR call + Print EXTRN _Symname: NEAR in CSeg if NEAR function. + Print EXTRN _Symname FWORD in DSeg if FAR function. +*****************************************************/ + +static void gen_extern_DASM (U32 symbol) +{ + U32 type; + + type = symtab[symbol].type; + + if (type & FAR) { + data_str("EXTRN "); + data_chr('_'); + data_str(&GPool[symtab[symbol].oname]); + data_str(" FWORD"); + data_chr('\n'); + } + else if (type & FUNCTION) + { + code_str("EXTRN "); + code_chr('_'); + code_str(&GPool[symtab[symbol].oname]); + code_chr(':'); + code_str(" NEAR"); + code_chr('\n'); + } + +} + +/**************************************************** + Define an external label for DASM data references. + Print EXTRN _Symname DB (DW or DD) if NOT function. +*****************************************************/ + +static void gen_ext_data_DASM (U32 symbol) +{ + U32 type; + + type = symtab[symbol].type; + + if (!(type & FUNCTION)) + { + data_str("EXTRN "); + data_chr('_'); + data_str(&GPool[symtab[symbol].oname]); + if (type & (POINTER|DWORD)) + data_str(" DD"); + else if (type & WORD) + data_str(" DW"); + else + data_str(" DB"); + data_chr('\n'); + } +} + +/******************************************************* +* Enter function & allocate local variable stack space +********************************************************/ + +static void enter_func(U32 symbol, U32 size) +{ + code_global(symbol); + code_str(":\n"); + if (symtab[fptr].type & ISR) + { + out_inst("PUSHAD"); + } + else { + out_inst("PUSH EBP"); + out_inst("MOV EBP,ESP"); + if(size) { + code_str("\tSUB ESP,"); + code_num(size); + code_chr('\n'); } + } +} + +/************************************************ +* Clean up the stack & end function definition +*************************************************/ + +static void end_func(U32 stackpop) +{ + if (symtab[fptr].type & ISR) + { + out_inst("POPAD"); + out_inst("IRETD"); + } + else { + if(local_stack) + out_inst("MOV ESP,EBP"); + out_inst("POP EBP"); + if (stackpop) { + if (symtab[fptr].type & FAR) code_str("\tRETF "); + else code_str("\tRETN "); + code_num(stackpop); + code_chr('\n'); + } + else if (symtab[fptr].type & FAR) out_inst("\tRETF"); + else out_inst("RETN"); + } +} + +/********************************** + Define a compiler generated label +***********************************/ + +static void gen_label(U32 label) +{ + code_chr(prefix); + code_chr('_'); + code_num(label); + code_str(":\n"); +} + +/*********************** + Define literal pool +************************/ +static void gen_literal(unsigned char *ptr, U32 size) +{ + U32 i; + + if(size) { + i = 0; + data_chr(prefix); + data_str("_lit"); + while(i < size) { + data_str((i % 16) ? "," : " DB "); + data_num(*ptr++); + if(!(++i % 16)) + data_chr('\n'); } + if(i % 16) + data_chr('\n'); } +} + +/*********************************** +* Call a function by name +************************************/ +static void call(U32 token, U32 value, U32 type, U32 offset, U32 clean) +{ + + token = CheckStack(token); + if (type & FAR) + { + code_str("\tCALL FWORD PTR "); + } + else code_str("\tCALL "); + if(token == NUMBER) + code_num(value); + else + write_oper(token, value, type, offset); + code_chr('\n'); + + if(clean) + { /* clean up stack following function call */ + clean *=4; /* each stack operand is 4 bytes */ + code_str("\tADD ESP,"); + code_num(clean); + code_chr('\n'); + } + + zero_flag = -1; + +} + +/********************************** +* Unconditional jump to label +***********************************/ + +static void jump(U32 label, char ljmp) +{ + code_str(ljmp ? "\tJMP " : "\tJMP SHORT "); + code_chr(prefix); + code_chr('_'); + code_num(label); + code_chr('\n'); +} + +/**************************************** +* Conditional jump to label +*****************************************/ +static void jump_if(char cond, U32 label, char ljmp) + /* condition TRUE or FALSE, destination, long jump required */ +{ + if (ljmp) code_str(cond ? "\tJNZ " : "\tJZ "); + else code_str(cond ? "\tJNZ SHORT " : "\tJZ SHORT "); + code_chr(prefix); + code_chr('_'); + code_num(label); + code_chr('\n'); +} + +/*********************************************** + JUMP to the begining of the switch jump table +************************************************/ +static void do_switch(U32 label) +{ + code_str("\tJMP "); + code_chr(prefix); + code_chr('_'); + code_num(label); + code_chr('\n'); +} + +/************************************************* +* Build switch compare/jump table. +* d is the count of switch jump table entries. +**************************************************/ + +static void build_switch(U32 d) +{ + while(switch_ptr > d) { + code_str("\tCMP EAX,"); + code_num(switch_stack[--switch_ptr]); + code_chr('\n'); + code_str("\tJE "); + code_chr(prefix); + code_chr('_'); + code_num(switch_stack[--switch_ptr]); + code_chr('\n'); + } +} + + +/******************************************** + Load index register with a pointer value +*********************************************/ + +static void index_ptr(U32 token, U32 value, U32 type, U32 offset) +{ + if(token == INEAX) + { + out_inst("MOV ESI,EAX"); + } + else if (token==PESI) + return; + else + { + token = CheckStack(token); + code_str("\tMOV ESI,"); + write_oper(token, value, type, offset); + code_str("\n"); + } +} + +/************************************************ + Load index register with the address of a symbol +*************************************************/ + +static void index_adr(U32 token, U32 value, U32 type, U32 offset) +{ + if (token != PESI) + { + code_str((type & GLOBAL) ? "\tMOV ESI,OFFSET " : "\tLEA ESI,"); + write_oper(token, value, type, offset); + code_str("\n"); + } +} + +/********************************** +* Output a global code symbol name +***********************************/ + +static void code_global(U32 symbol) +{ +char *ptr; + ptr = &GPool[symtab[symbol].oname]; + if(!(symtab[symbol].type & STATIC)) + { + code_str("PUBLIC "); + } + code_chr('_'); + code_str(ptr); +} + +/********************************** +* Output a global data symbol name +***********************************/ + +static void data_global(U32 symbol) +{ +char *ptr; + ptr = &GPool[symtab[symbol].oname]; + if(!(symtab[symbol].type & STATIC)) + { + data_str("PUBLIC "); + } + data_chr('_'); + data_str(ptr); +} + + +/********************************************** + CODE FILE OUTPUT ROUTINES +***********************************************/ + +/*************************************** + Write a char to the code tmp file +***************************************/ + +void code_chr(char chr) +{ + pcodebuf[pc++] = chr; + if (pc >= 512000-1) + { + printf("Code buffer overflow... (512000 bytes)\r\n"); + exit(1); + } +} + +/**************************************** + Write a string to the code tmp file +*****************************************/ + +void code_str(char *ptr) +{ + while(*ptr) + code_chr(*ptr++); +} + +/******************************** +* Write a number to the code buf +*********************************/ + +void code_num(U32 value) +{ + char stack[10]; + U32 i; + + if(value & 0x80000000) { + code_chr('-'); + value = -value; } + + i = 0; + do + stack[i++] = (value % 10) + '0'; + while(value /= 10); + + while(i) + code_chr(stack[--i]); +} + +/********************************************** + ASM FILE OUTPUT ROUTINES (INITIAL DATA SEG) +***********************************************/ + +/* Write a char to the asm file (all data) */ + +void data_chr(char chr) +{ + databuf[pd++] = chr; + if (pd > 4095) + { + fwrite(databuf, 4096, 1, asm_fh); + pd = 0; + } +} + +/* Write a string to the asm file (all data) */ + +void data_str(char *ptr) +{ + while(*ptr) + data_chr(*ptr++); +} + +/******************************** +* Write a number to the data buf +*********************************/ + +void data_num(U32 value) +{ + char stack[10]; + U32 i; + + if(value & 0x80000000) { + data_chr('-'); + value = -value; } + + i = 0; + do + stack[i++] = (value % 10) + '0'; + while(value /= 10); + + while(i) + data_chr(stack[--i]); +} + + +/******************************** +* Write a number to file +*********************************/ + +void put_num(U32 value, FILE *outfile) +{ + char stack[10]; + U32 i; + + if(value & 0x80000000) { + fputc('-', outfile); + value = -value; } + + i = 0; + do + stack[i++] = (value % 10) + '0'; + while(value /= 10); + + while(i) + fputc(stack[--i], outfile); +} + +/*************************************** +* Process a language statement +****************************************/ + +static void statement(U32 token) +{ + U32 a, b, c, d; + + test_exit(); /* generate any preceeding exit */ + + switch(token) + { /* act upon the token */ + case SEMI: /* ';' - null statement */ + check_func(); + return; + case OCB: /* '{' - begin a block */ + while((token = get_token()) != CCB) + statement(token); + break; + case INT: /* 16/32 integer variable declaration */ + case CHAR: /* 8 bit variable declaration */ + case SHORT: /* 16 bit variable declaration */ + case LONG: /* 32 bit variable declaration */ + case SIGNED: /* signed variable declaration (the default anyway)*/ + case UNSIGN: /* unsigned variable declaration */ + case STAT: /* static modifier */ + case STRUC: /* structure declaration */ + case EXTERN: /* external modifier */ + case REGIS: /* register modifier */ + case INTR: /* interrupt modifier for functions */ + case VOIDD: /* void for function, and empty args */ + declare(token, 0); + break; + case IF: + check_func(); + expect(ORB); + eval(CRB, 0); + cond_jump(FALSE, a = ++next_lab, -1); + statement(get_token()); + if(test_token(ELSE)) { + test_jump(b = ++next_lab); + gen_label(a); + a = b; + statement(get_token()); } + test_exit(); + gen_label(a); + break; + case WHILE: + check_func(); + gen_label(continue_stack[loop_ptr] = a = ++next_lab); + break_stack[loop_ptr++] = b = ++next_lab; + expect(ORB); + eval(CRB, 0); + cond_jump(FALSE, b, -1); + statement(get_token()); + test_jump(a); + gen_label(b); + --loop_ptr; + break; + case DO: + check_func(); + gen_label(a = ++next_lab); + continue_stack[loop_ptr] = b = ++next_lab; + break_stack[loop_ptr++] = c = ++next_lab; + statement(get_token()); + gen_label(b); + expect(WHILE); + eval(SEMI, 0); + cond_jump(TRUE, a, -1); + gen_label(c); + --loop_ptr; + break; + case FOR: + check_func(); + expect(ORB); + if(!test_token(SEMI)) /* initial */ + eval(SEMI, -1); + gen_label(d = a = ++next_lab); + break_stack[loop_ptr] = b = ++next_lab; + if(!test_token(SEMI)) { /* test */ + eval(SEMI, 0); + cond_jump(FALSE, b, -1); } + if(!test_token(CRB)) + { /* end */ + jump(c = ++next_lab, 0); + gen_label(d = ++next_lab); + eval(CRB, -1); + jump(a, 0); + gen_label(c); + } + continue_stack[loop_ptr++] = d; + statement(get_token()); + test_jump(d); + gen_label(b); /* exit */ + --loop_ptr; + break; + case SWITCH: + check_func(); + a = sdefault; + break_stack[loop_ptr++] = sdefault = b = ++next_lab; + expect(ORB); + eval(CRB, -1); + do_switch(c = ++next_lab); + d = switch_ptr; + statement(get_token()); + test_jump(b); + gen_label(c); + build_switch(d); + if (sdefault!=a) jump(sdefault, -1); + gen_label(b); + --loop_ptr; + sdefault = a; + break; + case CASE: + a = check_switch(); + get_constant(&b, &c); + switch_stack[switch_ptr++] = a; /* RAB reversed a & c */ + switch_stack[switch_ptr++] = c; + if(switch_ptr >= (MAX_SWITCH*2)) + fatal_error("Too many active cases"); + expect(COLON); + break; + case DEFAULT: + sdefault = check_switch(); + expect(COLON); + break; + case RETURN: + check_func(); + if(!test_token(SEMI)) + { + eval(SEMI, -1); + } + exit_flag = exit_label ? exit_label : (exit_label = ++next_lab); + break; + case BREAK: + check_loop(break_stack); + break; + case CONTIN: + check_loop(continue_stack); + break; + case GOTO: + check_func(); + if(get_token() != SYMBOL) { + syntax_error(); + break; } + if(a = lookup_local()) { + if(!(a & GLABEL)) + type_error(); } + else + define_symbol(REFERENCE|GLABEL, ++next_lab); + jump(symtab[sptr].dindex, -1); + break; + case SYMBOL: /* a symbol table entry */ + if(!in_function) { /* global definition */ + declare(token, 0); + break; } + if(*input_ptr == ':') { /* label definition */ + check_func(); + ++input_ptr; + if(lookup_local()) + symtab[sptr].type &= ~EXTERNAL; + else + define_symbol(GLABEL, ++next_lab); /* sets sptr */ + gen_label(symtab[sptr].dindex); + break; + } + default: /* expression evaluation */ + check_func(); + unget_token(token); + eval(SEMI, -1); + } +} + + +/********************************************************* +* Main compile loop, process statements until end of file +**********************************************************/ + +static void compile(void) +{ + define_ptr = define_pool; + *(input_ptr = line_in) = 0; + if (!fGen) { + code_str("\n\n.CODE\n"); + data_str("\n.DATA\n"); + } + for(;;) + statement(get_token()); +} + +/**************************************************** +* Initialize I/O & execute compiler MAIN MAIN MAIN +*****************************************************/ + +void main(S32 argc, char *argv[]) +{ + U32 i, erc; + char *ptr, *pname; + +/* first, get a BIG code buffer!! */ + + erc = AllocPage(SCODEBUF/4096, &pcodebuf); + if (erc) + printf("Not enough memory to allocate %d bytes.\r\n", SCODEBUF); + + /* extern far U32 DeAllocPage(U8 *pOrigMem, U32 nPages); */ + +/* first process any filenames and command line options */ + + list_fh = stdout; /* default the list file */ + + for(i=1; i < argc; ++i) { /* start at arg 1 */ + ptr = argv[i]; + if (*ptr == '/') { + ptr++; + switch(*ptr) { + case 'S' : /* quiet mode */ + case 's' : + fQuiet = 1; + break; + case 'L' : /* List file ON */ + case 'l' : + fList = 1; + break; + case 'E' : /* Embedded source mode */ + case 'e' : + fSource = 1; + break; + case 'G' : /* Generate separate modules */ + case 'g' : + fGen = 1; + break; + case 'N' : /* NO optimize */ + case 'n' : + fNoOpt = 1; + break; + case 'O' : /* Optimize for speed */ + case 'o' : + fOptS = 1; + break; + case 'W' : /* Warnings ON */ + case 'w' : + fWarnings = 1; + break; + case 'P' : /* Prefix label character */ + case 'p' : + ptr++; + if (!is_alpha(*ptr)) + fatal_error("Invalid label prefix character"); + prefix = *ptr; + break; + default: + fatal_error("Invalid switch"); + break; + } + } + else { + if(!source_fh) { + copystring(srcname, argv[i]); + source_fh = fopen(argv[i], "r"); + } + else if(!asm_fh) { + copystring(asmname, argv[i]); + if(!(asm_fh = fopen(argv[i], "w"))) { + fputs("Error: Can't open ASM file\n", stdout); + exit(-1); + } + } + else + fatal_error("Too many parameters"); + } + } + +/* Input file not explicitly named errors out */ + if(!source_fh) { /* default to standard input */ + fputs("C Minus 32 Compiler, Version 2.3M\r\n", stdout); + fputs("Usage: SourceFile [AsmFile] /S /E /G /L /W /Px\r\n", stdout); + fputs("/S Suppress screen output (e.g., herald)\r\n", stdout); + fputs("/E Embed source in ASM output\r\n", stdout); + fputs("/G Generate separate Code & Data files\r\n", stdout); + fputs("/L List file generated for errors\r\n", stdout); + fputs("/N No optimization\r\n", stdout); + fputs("/O Optimize for speed.\r\n", stdout); + fputs("/W Warnings ON\r\n", stdout); + fputs("/Px Label prefix character (x=Label char)\r\n\n", stdout); + fputs("Error: Source filename required\r\n", stdout); + exit(-1); + } + +/* Output file not explicitly named defaults to Source.asm */ + + if(!asm_fh) + { /* default asm name to SourceName.asm */ + copystring(asmname, srcname); + pname=asmname; + while ((*pname != '.') && (*pname!= '\0')) pname++; + + if (fGen) + { + *pname++ = '.'; + *pname++ = 'D'; + *pname++ = 'A'; + *pname++ = 'S'; + } + else + { + *pname++ = '.'; + *pname++ = 'A'; + *pname++ = 'S'; + *pname++ = 'M'; + } + *pname = '\0'; + if(!(asm_fh = fopen(asmname, "w"))) + { + fputs("Error: Can't open ASM file\r\n", stdout); + exit(-1); + } + } + fASMOpen = TRUE; + +/* If -L then List file named Source.LST is generated. */ + + if (fList) + { + copystring(lstname, srcname); + pname=lstname; + while ((*pname != '.') && (*pname!= '\0')) pname++; + *pname++ = '.'; + *pname++ = 'L'; + *pname++ = 'S'; + *pname++ = 'T'; + *pname = '\0'; + if(!(list_fh = fopen(lstname, "w"))) + fatal_error("Cannot open LIST file"); + else fLISTOpen = TRUE; + } + + /* open code segment tmp file or Gen file */ + + if (fGen) + { + copystring(codename, srcname); + pname=codename; + while ((*pname != '.') && (*pname!= '\0')) pname++; + *pname++ = '.'; + *pname++ = 'C'; + *pname++ = 'A'; + *pname++ = 'S'; + *pname = '\0'; + if(!(code_fh = fopen(codename, "w"))) + fatal_error("Cannot open Code file"); + else fCODEOpen = TRUE; + } + + if(!fQuiet) + fputs("C Minus 32 Compiler, Version 2.3M\r\n", stdout); + + compile(); +}