/* ** Small-C Compiler -- Part 1 -- Top End. ** Copyright 1982, 1983, 1985, 1988 J. E. Hendrix ** Copyright 1998 H T Walheim ** All rights reserved. */ #include #include "notice.h" #include "cc.h" /* ** miscellaneous storage */ int nogo, /* disable goto statements? */ noloc, /* disable block locals? */ opindex, /* index to matched operator */ opsize, /* size of operator in characters */ swactive, /* inside a switch? */ swdefault,/* default label #, else 0 */ *swnext, /* address of next entry */ *swend, /* address of last entry */ *stage, /* staging buffer address */ *wq, /* while queue */ argcs, /* static argc */ *argvs, /* static argv */ *wqptr, /* ptr to next entry */ litptr, /* ptr to next entry */ macptr, /* macro buffer index */ pptr, /* ptr to parsing buffer */ ch, /* current character of input line */ nch, /* next character of input line */ declared, /* # of local bytes to declare, -1 when declared */ iflevel, /* #if... nest level */ skiplevel,/* level at which #if... skipping started */ nxtlab, /* next avail label # */ litlab, /* label # assigned to literal pool */ csp, /* compiler relative stk ptr */ argstk, /* function arg sp */ argtop, /* highest formal argument offset */ ncmp, /* # open compound statements */ errflag, /* true after 1st error in statement */ eof, /* true on final input eof */ output, /* fd for output file */ files, /* true if file list specified on cmd line */ filearg = 0, /* cur file arg index */ input = EOF, /* fd for input file */ input2 = EOF, /* fd for "#include" file */ usexpr = YES, /* true if value of expression is used */ ccode = YES, /* true while parsing C code */ *snext, /* next addr in stage */ *stail, /* last addr of data in stage */ *slast, /* last addr in stage */ listfp, /* file pointer to list device */ lastst, /* last parsed statement type */ oldseg; /* current segment (0, DATASEG, CODESEG) */ char optimize, /* optimize output of staging buffer? */ alarm, /* audible alarm on errors? */ monitor, /* monitor function headers? */ pause, /* pause for operator on errors? */ *symtab, /* symbol table */ *litq, /* literal pool */ *macn, /* macro name buffer */ *macq, /* macro string buffer */ *pline, /* parsing buffer */ *mline, /* macro buffer */ *line, /* ptr to pline or mline */ *lptr, /* ptr to current character in "line" */ *glbptr, /* global symbol table */ *locptr, /* next local symbol table entry */ quote[2] = {'"'}, /* literal string for '"' */ *cptr, /* work ptrs to any char buffer */ *cptr2, *cptr3, msname[NAMESIZE], /* macro symbol name */ ssname[NAMESIZE]; /* static symbol name */ int op[16] = { /* p-codes of signed binary operators */ OR12, /* level5 */ XOR12, /* level6 */ AND12, /* level7 */ EQ12, NE12, /* level8 */ LE12, GE12, LT12, GT12, /* level9 */ ASR12, ASL12, /* level10 */ ADD12, SUB12, /* level11 */ MUL12, DIV12, MOD12 /* level12 */ }; int op2[16] = { /* p-codes of unsigned binary operators */ OR12, /* level5 */ XOR12, /* level6 */ AND12, /* level7 */ EQ12, NE12, /* level8 */ LE12u, GE12u, LT12u, GT12u, /* level9 */ ASR12, ASL12, /* level10 */ ADD12, SUB12, /* level11 */ MUL12u, DIV12u, MOD12u /* level12 */ }; /* ** execution begins here */ main(argc, argv) int argc, *argv; { fputs(VERSION, stdout); fputs(CRIGHT1, stdout); fputs(CRIGHT2, stdout); argcs = argc; argvs = argv; swnext = calloc(SWTABSZ, 1); swend = swnext+(SWTABSZ-SWSIZ); stage = calloc(STAGESIZE, 2*INTSIZE); wqptr = wq = calloc(WQTABSZ, INTSIZE); litq = calloc(LITABSZ, 1); macn = calloc(MACNSIZE, 1); macq = calloc(MACQSIZE, 1); pline = calloc(LINESIZE, 1); mline = calloc(LINESIZE, 1); slast = stage+(STAGESIZE*2*INTSIZE); symtab = calloc((NUMLOCS*SYMAVG + NUMGLBS*SYMMAX), 1); locptr = STARTLOC; glbptr = STARTGLB; ask(); /* get user options */ openfile(); /* and initial input file */ preprocess(); /* fetch first line */ header(); /* intro code */ setcodes(); /* initialize code pointer array */ parse(); /* process ALL input */ trailer(); /* follow-up code */ fclose(output); /* explicitly close output */ } /******************** high level parsing *******************/ /* ** process all input text ** ** At this level, only static declarations, ** defines, includes and function ** definitions are legal... */ parse() { while (eof == 0) { if (amatch("extern", 6)) dodeclare(EXTERNAL); else if(dodeclare(STATIC)) ; else if( match("#asm")) doasm(); else if( match("#include")) doinclude(); else if( match("#define")) dodefine(); else dofunction(); blanks(); /* force eof if pending */ } } /* ** test for global declarations */ dodeclare(class) int class; { if (amatch("char", 4)) declglb(CHR, class); else if(amatch("unsigned", 8)) { if (amatch("char", 4)) declglb(UCHR, class); else {amatch("int", 3); declglb(UINT, class);} } else if(amatch("int", 3) || class == EXTERNAL) declglb(INT, class); else return 0; ns(); return 1; } /* ** declare a static variable */ declglb(type, class) int type, class; { int id, dim; while(1) { if(endst()) return; /* do line */ if(match("*")) { id = POINTER; dim = 0; } else { id = VARIABLE; dim = 1; } if(symname(ssname) == 0) illname(); if(findglb(ssname)) multidef(ssname); if(id == VARIABLE) { if (match("(")) { id = FUNCTION; need(")"); } else if(match("[")) { id = ARRAY; dim = needsub(); } } if (class == EXTERNAL) external(ssname, type >> 2, id); else if (id != FUNCTION) initials(type >> 2, id, dim); if(id == POINTER) addsym(ssname, id, type, PTRSIZE, 0, &glbptr, class); else addsym(ssname, id, type, dim * (type >> 2), 0, &glbptr, class); if(match(",") == 0) return; } } /* ** initialize global objects */ initials(size, ident, dim) int size, ident, dim; { int savedim; litptr = 0; if(dim == 0) dim = -1; /* *... or ...[] */ savedim = dim; /* public(ident); */ if(match("=")) { if(match("{")) { while(dim) { init(size, ident, &dim); if(match(",") == 0) break; } need("}"); } else init(size, ident, &dim); } if(savedim == -1 && dim == -1) { if(ident == ARRAY) error("need array size"); stowlit(0, size = PTRSIZE); } /* FASM */ public(ident); if(litptr>0) dumplits(size); else if(dim>0) {/*In FASM: ": TIMES D 0" */ fputc(':',output); } dumpzero(size, dim); /* only if dim > 0 */ /* FASM */ } /* ** evaluate one initializer */ init(size, ident, dim) int size, ident, *dim; { int value; if(string(&value)) { if(ident == VARIABLE || size != 1) error("must assign to char pointer or char array"); *dim -= (litptr - value); if(ident == POINTER) point(); } else if(constexpr(&value)) { if(ident == POINTER) error("cannot assign to pointer"); stowlit(value, size); *dim -= 1; } } /* ** get required array size */ needsub() { int val; if(match("]")) return 0; /* null size */ if(constexpr(&val) == 0) val = 1; if(val < 0) { error("negative size illegal"); val = -val; } need("]"); /* force single dimension */ return val; /* and return size */ } /* ** open an include file */ doinclude() { int i; char str[30]; blanks(); /* skip over to name */ if(*lptr == '"' || *lptr == '<') ++lptr; i = 0; while(lptr[i] && lptr[i] != '"' && lptr[i] != '>' && lptr[i] != '\n') { str[i] = lptr[i]; ++i; } str[i] = NULL; if((input2 = fopen(str,"r")) == NULL) { input2 = EOF; error("open failure on include file"); } kill(); /* make next read come from new file (if open) */ } /* ** define a macro symbol */ dodefine() { int k; if(symname(msname) == 0) { illname(); kill(); return; } /* puts (msname); puts (" is #defined\n"); */ k = 0; if(search(msname, macn, NAMESIZE+2, MACNEND, MACNBR, 0) == 0) { if(cptr2 = cptr) while(*cptr2++ = msname[k++]) ; else { error("macro name table full"); return; } } putint(macptr, cptr+NAMESIZE, 2 /*INTSIZE*/); while(white()) gch(); while(putmac(gch())); if(macptr >= MACMAX) { error("macro string queue full"); exit(ERRCODE); } } putmac(c) char c; { macq[macptr] = c; if(macptr < MACMAX) ++macptr; return c; } /* ** begin a function ** ** called from "parse" and tries to make a function ** out of the following text */ dofunction() { char *ptr; nogo = /* enable goto statements */ noloc = /* enable block-local declarations */ lastst = /* no statement yet */ litptr = 0; /* clear lit pool */ litlab = getlabel(); /* label next lit pool */ locptr = STARTLOC; /* clear local variables */ if(match("void")) blanks(); /* skip "void" & locate header */ if(monitor) lout(line, stderr); if(symname(ssname) == 0) { error("illegal function or declaration"); errflag = 0; kill(); /* invalidate line */ return; } if(ptr = findglb(ssname)) { /* already in symbol table? */ if(ptr[CLASS] == AUTOEXT) ptr[CLASS] = STATIC; else multidef(ssname); } else addsym(ssname, FUNCTION, INT, 0, 0, &glbptr, STATIC); public(FUNCTION); argstk = 0; /* init arg count */ if(match("(") == 0) error("no open paren"); while(match(")") == 0) { /* then count args */ if(symname(ssname)) { if(findloc(ssname)) multidef(ssname); else { addsym(ssname, 0, 0, 0, argstk, &locptr, AUTOMATIC); argstk += INTSIZE; } } else { error("illegal argument name"); skip(); } blanks(); if(streq(lptr,")") == 0 && match(",") == 0) error("no comma"); if(endst()) break; } csp = 0; /* preset stack ptr */ argtop = argstk+INTSIZE; /* account for the pushed BP */ while(argstk) { if (amatch("char", 4)) {doargs(CHR); ns();} else if(amatch("int", 3)) {doargs(INT); ns();} else if(amatch("unsigned", 8)) { if (amatch("char", 4)) {doargs(UCHR); ns();} else {amatch("int", 3); doargs(UINT); ns();} } else {error("wrong number of arguments"); break;} } gen(ENTER, 0); statement(); if(lastst != STRETURN && lastst != STGOTO) gen(RETURN, 0); if(litptr) { toseg(DATASEG); gen(REFm, litlab); dumplits(1); /* dump literals */ } } /* ** declare argument types */ doargs(type) int type; { int id, sz; char c, *ptr; while(1) { if(argstk == 0) return; /* no arguments */ if(decl(type, POINTER, &id, &sz)) { if(ptr = findloc(ssname)) { ptr[IDENT] = id; ptr[TYPE] = type; putint(sz, ptr+SIZE, INTSIZE); putint(argtop-getint(ptr+OFFSET, INTSIZE), ptr+OFFSET, INTSIZE); } else error("not an argument"); } argstk = argstk - INTSIZE; /* cnt down */ if(endst()) return; if(match(",") == 0) error("no comma"); } } /* ** parse next local or argument declaration */ decl(type, aid, id, sz) int type, aid, *id, *sz; { int n, p; int mod; if(match("(")) p = 1; else p = 0; if(match("*")) {*id = POINTER; *sz = PTRSIZE;} else {*id = VARIABLE; *sz = type >> 2;} if((n = symname(ssname)) == 0) illname(); if(p && match(")")) ; if(match("(")) { if(!p || *id != POINTER) error("try (*...)()"); need(")"); } else if(*id == VARIABLE && match("[")) { *id = aid; if((*sz *= needsub()) == 0) { if(aid == ARRAY) error("need array size"); *sz = PTRSIZE; /* size of pointer argument */ } } mod = *sz % ALIGN; if (mod) { *sz = *sz + (ALIGN-mod); } return n; } /******************** start 2nd level parsing *******************/ /* ** statement parser */ statement() { if(ch == 0 && eof) return; else if(amatch("char", 4)) {declloc(CHR); ns();} else if(amatch("int", 3)) {declloc(INT); ns();} else if(amatch("unsigned", 8)) { if (amatch("char", 4)) {declloc(UCHR); ns();} else {amatch("int", 3); declloc(UINT); ns();} } else { if(declared >= 0) { if(ncmp > 1) nogo = declared; /* disable goto */ gen(ADDSP, csp - declared); declared = -1; } if(match("{")) compound(); else if(amatch("if", 2)) {doif(); lastst = STIF;} else if(amatch("while", 5)) {dowhile(); lastst = STWHILE;} else if(amatch("do", 2)) {dodo(); lastst = STDO;} else if(amatch("for", 3)) {dofor(); lastst = STFOR;} else if(amatch("switch", 6)) {doswitch(); lastst = STSWITCH;} else if(amatch("case", 4)) {docase(); lastst = STCASE;} else if(amatch("default", 7)) {dodefault(); lastst = STDEF;} else if(amatch("goto", 4)) {dogoto(); lastst = STGOTO;} else if(dolabel()) lastst = STLABEL; else if(amatch("return", 6)) {doreturn(); ns(); lastst = STRETURN;} else if(amatch("break", 5)) {dobreak(); ns(); lastst = STBREAK;} else if(amatch("continue", 8)) {docont(); ns(); lastst = STCONT;} else if(match(";")) errflag = 0; else if(match("#asm")) {doasm(); lastst = STASM;} else {doexpr(NO); ns(); lastst = STEXPR;} } return lastst; } /* ** declare local variables */ declloc(type) int type; { int id, sz; if(swactive) error("not allowed in switch"); if(noloc) error("not allowed with goto"); if(declared < 0) error("must declare first in block"); while(1) { if(endst()) return; decl(type, ARRAY, &id, &sz); declared += sz; addsym(ssname, id, type, sz, csp - declared, &locptr, AUTOMATIC); if(match(",") == 0) return; } } compound() { int savcsp; char *savloc; savcsp = csp; savloc = locptr; declared = 0; /* may now declare local variables */ ++ncmp; /* new level open */ while (match("}") == 0) if(eof) { error("no final }"); break; } else statement(); /* do one */ if(--ncmp /* close current level */ && lastst != STRETURN && lastst != STGOTO) gen(ADDSP, savcsp); /* delete local variable space */ cptr = savloc; /* retain labels */ while(cptr < locptr) { cptr2 = nextsym(cptr); if(cptr[IDENT] == LABEL) { while(cptr < cptr2) *savloc++ = *cptr++; } else cptr = cptr2; } locptr = savloc; /* delete local symbols */ declared = -1; /* may not declare variables */ } doif() { int flab1, flab2; test(flab1 = getlabel(), YES); /* get expr, and branch false */ statement(); /* if true, do a statement */ if(amatch("else", 4) == 0) { /* if...else ? */ /* simple "if"...print false label */ gen(LABm, flab1); return; /* and exit */ } flab2 = getlabel(); if(lastst != STRETURN && lastst != STGOTO) gen(JMPm, flab2); gen(LABm, flab1); /* print false label */ statement(); /* and do "else" clause */ gen(LABm, flab2); /* print true label */ } dowhile() { int wq[4]; /* allocate local queue */ addwhile(wq); /* add entry to queue for "break" */ gen(LABm, wq[WQLOOP]); /* loop label */ test(wq[WQEXIT], YES); /* see if true */ statement(); /* if so, do a statement */ gen(JMPm, wq[WQLOOP]); /* loop to label */ gen(LABm, wq[WQEXIT]); /* exit label */ delwhile(); /* delete queue entry */ } dodo() { int wq[4]; addwhile(wq); gen(LABm, wq[WQLOOP]); statement(); need("while"); test(wq[WQEXIT], YES); gen(JMPm, wq[WQLOOP]); gen(LABm, wq[WQEXIT]); delwhile(); ns(); } dofor() { int wq[4], lab1, lab2; addwhile(wq); lab1 = getlabel(); lab2 = getlabel(); need("("); if(match(";") == 0) { doexpr(NO); /* expr 1 */ ns(); } gen(LABm, lab1); if(match(";") == 0) { test(wq[WQEXIT], NO); /* expr 2 */ ns(); } gen(JMPm, lab2); gen(LABm, wq[WQLOOP]); if(match(")") == 0) { doexpr(NO); /* expr 3 */ need(")"); } gen(JMPm, lab1); gen(LABm, lab2); statement(); gen(JMPm, wq[WQLOOP]); gen(LABm, wq[WQEXIT]); delwhile(); } doswitch() { int wq[4], endlab, swact, swdef, *swnex, *swptr; swact = swactive; swdef = swdefault; swnex = swptr = swnext; addwhile(wq); *(wqptr + WQLOOP - WQSIZ) = 0; need("("); doexpr(YES); /* evaluate switch expression */ need(")"); swdefault = 0; swactive = 1; gen(JMPm, endlab = getlabel()); statement(); /* cases, etc. */ gen(JMPm, wq[WQEXIT]); gen(LABm, endlab); gen(SWITCH, 0); /* match cases */ while(swptr < swnext) { gen(NEARm, *swptr++); #ifdef INT32 gen(DWORDn, *swptr++); /* case value */ #else gen(WORDn, *swptr++); /* case value */ #endif } #ifdef INT32 gen(DWORDn, 0); #else gen(WORDn, 0); #endif if(swdefault) gen(JMPm, swdefault); gen(LABm, wq[WQEXIT]); delwhile(); swnext = swnex; swdefault = swdef; swactive = swact; } docase() { if(swactive == 0) error("not in switch"); if(swnext > swend) { error("too many cases"); return; } gen(LABm, *swnext++ = getlabel()); constexpr(swnext++); need(":"); } dodefault() { if(swactive) { if(swdefault) error("multiple defaults"); } else error("not in switch"); need(":"); gen(LABm, swdefault = getlabel()); } dogoto() { if(nogo > 0) error("not allowed with block-locals"); else noloc = 1; if(symname(ssname)) gen(JMPm, addlabel(NO)); else error("bad label"); ns(); } dolabel() { char *savelptr; blanks(); savelptr = lptr; if(symname(ssname)) { if(gch() == ':') { gen(LABm, addlabel(YES)); return 1; } else bump(savelptr-lptr); } return 0; } addlabel(def) int def; { if(cptr = findloc(ssname)) { if(cptr[IDENT] != LABEL) error("not a label"); else if(def) { if(cptr[TYPE]) error("duplicate label"); else cptr[TYPE] = YES; } } else cptr = addsym(ssname, LABEL, def, 0, getlabel(), &locptr, LABEL); return (getint(cptr+OFFSET, INTSIZE)); } doreturn() { int savcsp; if(endst() == 0) doexpr(YES); savcsp = csp; gen(RETURN, 0); csp = savcsp; } dobreak() { int *ptr; if((ptr = readwhile(wqptr)) == 0) return; gen(ADDSP, ptr[WQSP]); gen(JMPm, ptr[WQEXIT]); } docont() { int *ptr; ptr = wqptr; while (1) { if((ptr = readwhile(ptr)) == 0) return; if(ptr[WQLOOP]) break; } gen(ADDSP, ptr[WQSP]); gen(JMPm, ptr[WQLOOP]); } doasm() { ccode = 0; /* mark mode as "asm" */ while (1) { inline(); if(match("#endasm")) break; if(eof)break; fputs(line, output); } kill(); ccode = 1; } doexpr(use) int use; { int constant, val; int *before, *start; usexpr = use; /* tell isfree() whether expr value is used */ while(1) { setstage(&before, &start); expression(&constant, &val); clearstage(before, start); if(ch != ',') break; bump(1); } usexpr = YES; /* return to normal value */ } /******************** miscellaneous functions *******************/ /* ** get run options */ ask() { int i; int j; i = listfp = nxtlab = 0; output = stdout; #ifdef LATER optimize = YES; // Not working for 32 bit int's yer #else optimize = NO; #endif alarm = monitor = pause = NO; line = mline; while(getarg(++i, line, LINESIZE, argcs, argvs) != EOF) { if(line[0] != '-' && line[0] != '/') continue; if(toupper(line[1]) == 'L' // List && isdigit(line[2]) && line[3] <= ' ') { listfp = line[2]-'0'; continue; } if(toupper(line[1]) == 'N' // No optimize && toupper(line[2]) == 'O' && line[3] <= ' ') { optimize = NO; continue; } if(toupper(line[1]) == 'D') { j = 0; ch = line[j+2]; lptr = line + j+2; /* while (line[j+2] != ' ') { if (j < (NAMEMAX-1)) { msname[j] = line[j+1]; ++j; } else { break; } } msname[j] = '\0'; */ dodefine (); continue; } if(line[2] <= ' ') { if(toupper(line[1]) == 'A') {alarm = YES; continue;} if(toupper(line[1]) == 'M') {monitor = YES; continue;} if(toupper(line[1]) == 'P') {pause = YES; continue;} } fputs("usage: cc [file]... [-m] [-a] [-p] [-l#] [-no] [-d]\n", stderr); fputs(" -m monitor\n", stderr); fputs(" -a alarm\n", stderr); fputs(" -p pause\n", stderr); fputs(" -l# list\n", stderr); fputs(" -no no optimize\n", stderr); fputs(" -d pre-#define id\n", stderr); exit(ERRCODE); } } /* ** input and output file opens */ openfile() { /* entire function revised */ char outfn[15]; int i, j, ext; input = EOF; while(getarg(++filearg, pline, LINESIZE, argcs, argvs) != EOF) { if(pline[0] == '-' || pline[0] == '/') continue; ext = NO; i = -1; j = 0; while(pline[++i]) { if(pline[i] == '.') { ext = YES; break; } if(j < 10) outfn[j++] = pline[i]; } if(!ext) strcpy(pline + i, ".C"); input = mustopen(pline, "r"); #ifdef _MSC_VER if(!files) { strcpy(outfn + j, ".ASM"); output = mustopen(outfn, "w"); } #else if(!files /* && iscons(stdout)*/) { strcpy(outfn + j, ".ASM"); output = mustopen(outfn, "w"); } #endif files = YES; kill(); return; } if(files++) eof = YES; else input = stdin; kill(); } /* ** open a file with error checking */ mustopen(fn, mode) char *fn, *mode; { int fd; if(fd = fopen(fn, mode)) return fd; fputs("open error on ", stderr); lout(fn, stderr); exit(ERRCODE); }