/* as.c */ #include "as.h" #include #include typedef unsigned char byte; union { int i; float f; struct { byte a,b,c,d; } b; } converter; typedef struct S { char *name; int value, defnd; struct S *next; } symbolentry; symbolentry *hashtable[1000]; FILE *source, *fout; char *filename; int errors; int line_number; char line[100]; int location_counter; int total_size; symbolentry *lookup(char *name) { int i,h; symbolentry *s; h=4521; for (i=0; name[i]!=0; i+=1) h=h*21+name[i]; if (h<0) h=-h; h=h%1000; s=hashtable[h]; while (s!=NULL && strcmp(s->name,name)!=0) s=s->next; if (s==NULL) { s=malloc(sizeof(symbolentry)); s->name=malloc(strlen(name)+1); strcpy(s->name,name); s->value=0; s->defnd=0; s->next=hashtable[h]; hashtable[h]=s; } return (s); } void define(char *n, int v) { symbolentry *s; s=lookup(n); s->value=v; s->defnd=1; } void init_all(void) { int i; for (i=0; i<1000; i+=1) hashtable[i]=NULL; define("BADP",(OPCODE 0)); define("LOAD",(OPCODE 1 +OPERAND)); define("STOR",(OPCODE 2 +OPERAND)); define("LOADN",(OPCODE 3 +OPERAND)); define("LOADF",(OPCODE 4 +OPERAND)); define("POP",(OPCODE 5)); define("STI",(OPCODE 6)); define("LDI",(OPCODE 7)); define("DUP",(OPCODE 8)); define("ADD",(OPCODE 9)); define("ADDF",(OPCODE 10)); define("SUB",(OPCODE 11)); define("SUBF",(OPCODE 12)); define("MUL",(OPCODE 13)); define("MULF",(OPCODE 14)); define("DIV",(OPCODE 15)); define("DIVF",(OPCODE 16)); define("MOD",(OPCODE 17)); define("FLOAT",(OPCODE 18)); define("FIX",(OPCODE 19)); define("NOT",(OPCODE 20)); define("AND",(OPCODE 21)); define("OR",(OPCODE 22)); define("EQL",(OPCODE 23)); define("EQLF",(OPCODE 24)); define("NEQ",(OPCODE 25)); define("NEQF",(OPCODE 26)); define("LSS",(OPCODE 27)); define("LSSF",(OPCODE 28)); define("GTR",(OPCODE 29)); define("GTRF",(OPCODE 30)); define("LEQ",(OPCODE 31)); define("LEQF",(OPCODE 32)); define("GEQ",(OPCODE 33)); define("GEQF",(OPCODE 34)); define("INCSP",(OPCODE 35 +OPERAND)); define("DECSP",(OPCODE 36 +OPERAND)); define("INCFP",(OPCODE 37 +OPERAND)); define("DECFP",(OPCODE 38 +OPERAND)); define("JUMP",(OPCODE 39 +OPERAND)); define("PJZ",(OPCODE 40 +OPERAND)); define("POPJMP",(OPCODE 41)); define("POPFP",(OPCODE 42)); define("LDFP",(OPCODE 43)); define("INIT",(OPCODE 44)); define("STOP",(OPCODE 45)); define("BADN",(OPCODE 127)); define("PRINTINT",1); define("PRINTFLOAT",2); define("PRINTSTRING",3); define("READINT",4); define("READFLOAT",5); errors=0; total_size=0; source=fopen(filename,"r"); if (source==NULL) { fprintf(stderr,"can't open \"%s\"\n",filename); exit(1); } } void init_pass(void) { line_number=0; if (errors!=0) exit(1); fseek(source,0,SEEK_SET); location_counter=FIRST_ADDRESS; } void split(char *in, char **label, char **opcode, char **operand) { int next; char *out; char *start[4]; next=0; *label=NULL; start[0]=NULL; start[1]=NULL; while (next<3) { while (*in==' ' || *in=='\t') in+=1; if (*in=='\n' || *in=='\r' || *in=='/' || *in==0) break; start[next]=in; next+=1; out=in; while (*in>' ' && *in!='/' && *in!=':') { if (*in>='a' && *in<='z') *out=*in-'a'+'A'; else *out=*in; out+=1; in+=1; if (next==2) while (*in==' ' || *in=='\t') in+=1; } if (*in==':') { next=0; if (*start[0]!=':') *label=start[0]; start[0]=NULL; } if (*in=='/') { *out=0; break; } if (*in!=0) in+=1; *out=0; } *opcode=start[0]; *operand=start[1]; } void error(char *msg, char *detail) { fprintf(stderr,"Line %d of %s: ", line_number, filename); fprintf(stderr, msg, detail); fprintf(stderr,"\n"); errors+=1; } void first_pass(void) { char *label, *opcode, *operand, *in; symbolentry *s; init_pass(); while (1) { if (errors>5) { fprintf(stderr,"Too many errors, I give up\n"); return; } in=fgets(line,99,source); if (in==NULL) break; line_number+=1; split(line,&label,&opcode,&operand); if (label!=NULL) { s=lookup(label); if (s->defnd) error("label %s is already defined", label); else { s->defnd=1; s->value=location_counter; } } if (opcode==NULL) continue; if (strcmp(opcode,"BYTES")==0) { int i=0; if (operand==NULL) { error("BYTES requires a number", NULL); continue; } if (sscanf(operand,"%d",&i)!=1) error("bad number %s after BYTES", operand); location_counter+=i; continue; } if (strcmp(opcode,"BYTE")==0) { location_counter+=1; continue; } s=lookup(opcode); if (!s->defnd) error("unknown name %s as opcode", opcode); else location_counter+=1; if (operand==NULL) { if (s->defnd && s->value&OPERAND!=0) error("%s instruction requires an operand", opcode); } else { if (s->defnd && s->value&OPERAND==0) error("instruction %s does not take an operand", opcode); location_counter+=4; } } } void second_pass(void) { char *label, *opcode, *operand, *in; int opcode_value; symbolentry *s; init_pass(); s=lookup("MAIN"); if (!s->defnd) error("MAIN has not been defined", NULL); converter.i=s->value; fputc(converter.b.a, fout); fputc(converter.b.b, fout); fputc(converter.b.c, fout); fputc(converter.b.d, fout); converter.i=location_counter; fputc(converter.b.a, fout); fputc(converter.b.b, fout); fputc(converter.b.c, fout); fputc(converter.b.d, fout); while (1) { if (errors>5) { fprintf(stderr,"Too many errors, I give up\n"); return; } in=fgets(line,99,source); if (in==NULL) break; line_number+=1; split(line,&label,&opcode,&operand); if (label!=NULL) { s=lookup(label); if (!s->defnd) error("use of undefined label %s", label); else if (s->value!=location_counter) error("problem with assembly: label %s has moved", label); } if (opcode==NULL) continue; if (strcmp(opcode,"BYTES")==0) { int i; if (operand==NULL) { error("BYTES requires a number", NULL); continue; } sscanf(operand,"%d",&i); while (i>0) { fputc(0, fout); i-=1; location_counter+=1; } continue; } if (strcmp(opcode,"BYTE")==0) { int i,k; if (operand==NULL) { error("BYTE requires a number", NULL); continue; } i=0; k=sscanf(operand,"%d",&i); location_counter+=1; fputc(i, fout); if (k!=1) error("improper BYTE operand %s",operand); continue; } s=lookup(opcode); opcode_value=s->value&~OPERAND; if (operand==NULL) { fputc(opcode_value, fout); location_counter+=1; } else { if (operand[0]=='+' || operand[0]=='-' || operand[0]=='.' || operand[0]>='0' && operand[0]<='9') { int i, flt=0; opcode_value|=CONSTANT_OPERAND; for (i=0; operand[i]!=0; i+=1) if (operand[i]=='.' || operand[i]=='E' || operand[i]=='e') { flt=1; break; } if (flt) { i=sscanf(operand,"%f",&converter.f); if (i!=1) error("incorrect floating point operand %s", operand); } else { i=sscanf(operand,"%d",&converter.i); if (i!=1) error("incorrect integer operand %s", operand); } } else if (operand[0]=='F' && operand[1]=='P' && (operand[2]=='+' || operand[2]=='-')) { int i=sscanf(operand+2,"%d",&converter.i); if (i!=1) error("improper offset %s after FP in operand", operand+2); opcode_value|=FP_RELATIVE_OPERAND; } else if (operand[0]=='@' && operand[1]=='F' && operand[2]=='P' && (operand[3]=='+' || operand[3]=='-')) { int i; if (opcode_value!=LOAD&~OPERAND) error("@FP+offset operand only valid with LOAD instruction", NULL); i=sscanf(operand+3,"%d",&converter.i); if (i!=1) error("improper offset %s after @FP in operand", operand+3); opcode_value=(LOADN&~OPERAND)|FP_RELATIVE_OPERAND; } else if (operand[0]=='@') { if (opcode_value!=LOAD&~OPERAND) error("@label operand only valid with LOAD instruction", NULL); s=lookup(operand+1); if (!s->defnd) error("operand %s has not been defined", operand); opcode_value=LOADN|CONSTANT_OPERAND; converter.i=s->value; } else { opcode_value|=CONSTANT_OPERAND; s=lookup(operand); if (!s->defnd) error("operand %s has not been defined", operand); converter.i=s->value; } fputc(opcode_value, fout); fputc(converter.b.a, fout); fputc(converter.b.b, fout); fputc(converter.b.c, fout); fputc(converter.b.d, fout); location_counter+=5; } } } void main(int argc, char *argv[]) { if (argc<2) filename="a.asm"; else filename=argv[1]; init_all(); first_pass(); total_size=location_counter; fout=fopen("a.exe", "w"); if (fout==NULL) { fprintf(stderr,"Can't create a.exe\n"); exit(1); } second_pass(); if (total_size!=location_counter) error("Assembly problem - size has changed", NULL); fclose(fout); fclose(source); }