Source code for a macro assembler
This appendix gives the complete source code for the macro assembler for the single-accumulator machine discussed in Chapter 7.
----- assemble.cpp ------------------------------------------------------------- // Macro assembler/interpreter for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #include "mc.h" #include "as.h" #define version "Macro Assembler 1.0" #define usage "Usage: ASSEMBLE source [listing]\n" void main(int argc, char *argv[]) { bool errors; char reply; char sourcename[256], listname[256]; // check on correct parameter usage if (argc == 1) { printf(usage); exit(1); } strcpy(sourcename, argv[1]); if (argc > 2) strcpy(listname, argv[2]); else appendextension(sourcename, ".lst", listname); MC *Machine = new(MC); AS *Assembler = new AS(sourcename, listname, version, Machine); Assembler->assemble(errors); if (errors) { printf("\nAssembly failed\n"); } else { printf("\nAssembly successful\n"); while (true) { printf("\nInterpret? (y/n) "); do { scanf("%c", &reply); } while (toupper(reply) != 'N' && toupper(reply) != 'Y'); if (toupper(reply) == 'N') break; scanf("%*[^\n]"); getchar(); Machine->interpret(); } } delete Machine; delete Assembler; } ----- misc.h ------------------------------------------------------------------ // Various common items for macro assemblers for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #ifndef MISC_H #define MISC_H #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <ctype.h> #include <limits.h> #define boolean int #define bool int #define true 1 #define false 0 #define TRUE 1 #define FALSE 0 #define maxint INT_MAX #if __MSDOS__ || MSDOS || WIN32 || __WIN32__ # define pathsep '\\' #else # define pathsep '/' #endif static void appendextension (char *oldstr, char *ext, char *newstr) // Changes filename in oldstr from PRIMARY.xxx to PRIMARY.ext in newstr { int i; char old[256]; strcpy(old, oldstr); i = strlen(old); while ((i > 0) && (old[i-1] != '.') && (old[i-1] != pathsep)) i--; if ((i > 0) && (old[i-1] == '.')) old[i-1] = 0; if (ext[0] == '.') sprintf(newstr,"%s%s", old, ext); else sprintf(newstr, "%s.%s", old, ext); } #define ASM_alength 8 // maximum length of mnemonics, labels #define ASM_slength 35 // maximum length of comment and other strings typedef char ASM_alfa[ASM_alength + 1]; typedef char ASM_strings[ASM_slength + 1]; #include "set.h" enum ASM_errors { ASM_invalidcode, ASM_undefinedlabel, ASM_invalidaddress, ASM_unlabelled, ASM_hasaddress, ASM_noaddress, ASM_excessfields, ASM_mismatched, ASM_nonalpha, ASM_badlabel, ASM_invalidchar, ASM_invalidquote, ASM_overflow }; typedef Set<ASM_overflow> ASM_errorset; #endif /* MISC_H */ ----- set.h ------------------------------------------------------------------- // Simple set operations #ifndef SET_H #define SET_H template <int maxElem> class Set { // { 0 .. maxElem } public: Set() // Construct { } { clear(); } Set(int e1) // Construct { e1 } { clear(); incl(e1); } Set(int e1, int e2) // Construct { e1, e2 } { clear(); incl(e1); incl(e2); } Set(int e1, int e2, int e3) // Construct { e1, e2, e3 } { clear(); incl(e1); incl(e2); incl(e3); } Set(int n, int e1[]) // Construct { e[0] .. e[n-1] } { clear(); for (int i = 0; i < n; i++) incl(e1[i]); } void incl(int e) // Include e { if (e >= 0 && e <= maxElem) bits[wrd(e)] |= bitmask(e); } void excl(int e) // Exclude e { if (e >= 0 && e <= maxElem) bits[wrd(e)] &= ~bitmask(e); } int memb(int e) // Test membership for e { if (e >= 0 && e <= maxElem) return((bits[wrd(e)] & bitmask(e)) != 0); else return 0; } int isempty(void) // Test for empty set { for (int i = 0; i < length; i++) if (bits[i]) return 0; return 1; } Set operator + (const Set &s) // Union with s { Set<maxElem> r; for (int i = 0; i < length; i++) r.bits[i] = bits[i] | s.bits[i]; return r; } Set operator * (const Set &s) // Intersection with s { Set<maxElem> r; for (int i = 0; i < length; i++) r.bits[i] = bits[i] & s.bits[i]; return r; } Set operator - (const Set &s) // Difference with s { Set<maxElem> r; for (int i = 0; i < length; i++) r.bits[i] = bits[i] & ~s.bits[i]; return r; } Set operator / (const Set &s) // Symmetric difference with s { Set<maxElem> r; for (int i = 0; i < length; i++) r.bits[i] = bits[i] ^ s.bits[i]; return r; } private: unsigned char bits[(maxElem + 8) / 8]; int length; int wrd(int i) { return(i / 8); } int bitmask(int i) { return(1 << (i % 8)); } void clear() { length = (maxElem + 8) / 8; for (int i = 0; i < length; i++) bits[i] = 0; } }; #endif /* SET_H */ ----- sh.s -------------------------------------------------------------------- // Source handler for assemblers for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #ifndef SH_H #define SH_H #include "misc.h" const int linemax = 129; // limit on source line length class SH { public: FILE *lst; // listing file char ch; // latest character read void nextch(void); // Returns ch as the next character on current source line, reading a new // line where necessary. ch is returned as NUL if src is exhausted bool endline(void) { return (charpos == linelength); } // Returns true when end of current line has been reached bool startline(void) { return (charpos == 1); } // Returns true if current ch is the first on a line void writehex(int i, int n) { fprintf(lst, "%02X%*c", i, n-2, ' '); } // Writes (byte valued) i to lst file as hex pair, left-justified in n spaces void writetext(char *s, int n) { fprintf(lst, "%-*s", n, s); } // Writes s to lst file left-justified in n spaces SH(); // Default constructor SH(char *sourcename, char *listname, char *version); // Initializes source handler, and displays version information on lst file. // Opens src and lst files using given names ~SH(); // Closes src and lst files private: FILE *src; // source file int charpos; // character pointer int linelength; // line length char line[linemax + 1]; // last line read }; #endif /*SH_H*/ ----- sh.cpp ------------------------------------------------------------------ // Source handler for assemblers for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #include "sh.h" void SH::nextch(void) { if (ch == '\0') return; // input exhausted if (charpos == linelength) // new line needed { linelength = 0; charpos = 0; ch = getc(src); while (ch != '\n' && !feof(src)) { if (linelength < linemax) { line[linelength] = ch; linelength++; } ch = getc(src); } if (feof(src)) line[linelength] = '\0'; // mark end with an explicit nul else line[linelength] = ' '; // mark end with an explicit space linelength++; } ch = line[charpos]; charpos++; // pass back unique character } SH::SH(char *sourcename, char *listname, char *version) { src = fopen(sourcename, "r"); if (src == NULL) { printf("Could not open input file\n"); exit(1); } lst = fopen(listname, "w"); if (lst == NULL) { printf("Could not open listing file\n"); lst = stdout; } fprintf(lst, "%s\n\n", version); ch = ' '; charpos = 0; linelength = 0; } SH::SH() { src = NULL; lst = NULL; ch = ' '; charpos = 0; linelength = 0; } SH::~SH() { if (src) fclose(src); src = NULL; if (lst) fclose(lst); lst = NULL; } ----- la.h -------------------------------------------------------------------- // Lexical analyzer for macro assemblers for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #ifndef LA_H #define LA_H #include "misc.h" #include "sh.h" enum LA_symtypes { LA_unknown, LA_eofsym, LA_eolsym, LA_idsym, LA_numsym, LA_comsym, LA_commasym, LA_plussym, LA_minussym, LA_starsym }; struct LA_symbols { bool islabel; // if in first column LA_symtypes sym; // class ASM_strings str; // lexeme int num; // value if numeric }; class LA { public: void getsym(LA_symbols &SYM, ASM_errorset &errors); // Returns the next symbol on current source line. // Adds to set of errors if necessary and returns SYM.sym = unknown // if no valid symbol can be recognized LA(SH *S); // Associates scanner with source handler S and initializes scanning private: SH *Srce; void getword(LA_symbols &SYM); void getnumber(LA_symbols &SYM, ASM_errorset &errors); void getcomment(LA_symbols &SYM); void getquotedchar(LA_symbols &SYM, char quote, ASM_errorset &errors); }; #endif /*LA_H*/ ----- la.cpp ------------------------------------------------------------------ // Lexical analyzer for assemblers for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #include "la.h" void LA::getword(LA_symbols &SYM) // Assemble identifier or opcode, in UPPERCASE for consistency { int length = 0; while (isalnum(Srce->ch)) { if (length < ASM_slength) { SYM.str[length] = toupper(Srce->ch); length++; } Srce->nextch(); } SYM.str[length] = '\0'; } void LA::getnumber(LA_symbols &SYM, ASM_errorset &errors) // Assemble number and store its identifier in UPPERCASE for consistency { int length = 0; while (isdigit(Srce->ch)) { SYM.num = SYM.num * 10 + Srce->ch - '0'; if (SYM.num > 255) errors.incl(ASM_overflow); SYM.num %= 256; if (length < ASM_slength) { SYM.str[length] = toupper(Srce->ch); length++; } Srce->nextch(); } SYM.str[length] = '\0'; } void LA::getcomment(LA_symbols &SYM) // Assemble comment { int length = 0; while (!Srce->endline()) { if (length < ASM_slength) { SYM.str[length] = Srce->ch; length++; } Srce->nextch(); } SYM.str[length] = '\0'; } void LA::getquotedchar(LA_symbols &SYM, char quote, ASM_errorset &errors) // Assemble single character address token { SYM.str[0] = quote; Srce->nextch(); SYM.num = Srce->ch; SYM.str[1] = Srce->ch; if (!Srce->endline()) Srce->nextch(); SYM.str[2] = Srce->ch; SYM.str[3] = '\0'; if (Srce->ch != quote) errors.incl(ASM_invalidquote); if (!Srce->endline()) Srce->nextch(); } void LA::getsym(LA_symbols &SYM, ASM_errorset &errors) { SYM.num = 0; SYM.str[0] = '\0'; // empty string while (Srce->ch == ' ' && !Srce->endline()) Srce->nextch(); SYM.islabel = (Srce->startline() && Srce->ch != ' ' && Srce->ch != ';' && Srce->ch != '\0'); if (SYM.islabel && !isalpha(Srce->ch)) errors.incl(ASM_badlabel); if (Srce->ch == '\0') { SYM.sym = LA_eofsym; return; } if (Srce->endline()) { SYM.sym = LA_eolsym; Srce->nextch(); return; } if (isalpha(Srce->ch)) { SYM.sym = LA_idsym; getword(SYM); } else if (isdigit(Srce->ch)) { SYM.sym = LA_numsym; getnumber(SYM, errors); } else switch (Srce->ch) { case ';': SYM.sym = LA_comsym; getcomment(SYM); break; case ',': SYM.sym = LA_commasym; strcpy(SYM.str, ","); Srce->nextch(); break; case '+': SYM.sym = LA_plussym; strcpy(SYM.str, "+"); Srce->nextch(); break; case '-': SYM.sym = LA_minussym; strcpy(SYM.str, "-"); Srce->nextch(); break; case '*': SYM.sym = LA_starsym; strcpy(SYM.str, "*"); Srce->nextch(); break; case '\'': case '"': SYM.sym = LA_numsym; getquotedchar(SYM, Srce->ch, errors); break; default: SYM.sym = LA_unknown; getcomment(SYM); errors.incl(ASM_invalidchar); break; } } LA::LA(SH* S) { Srce = S; Srce->nextch(); } ----- sa.h -------------------------------------------------------------------- // Syntax analyzer for macro assemblers for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #ifndef SA_H #define SA_H #include "misc.h" #include "la.h" const int SA_maxterms = 16; enum SA_termkinds { SA_absent, SA_numeric, SA_alphameric, SA_comma, SA_plus, SA_minus, SA_star }; struct SA_terms { SA_termkinds kind; int number; // value if known ASM_alfa name; // character representation }; struct SA_addresses { char length; // number of fields SA_terms term[SA_maxterms - 1]; }; struct SA_unpackedlines { // source text, unpacked into fields bool labelled; ASM_alfa labfield, mnemonic; SA_addresses address; ASM_strings comment; ASM_errorset errors; }; class SA { public: void parse(SA_unpackedlines &srcline); // Analyzes the next source line into constituent fields SA(LA *L); // Associates syntax analyzer with its lexical analyzer L private: LA *Lex; LA_symbols SYM; void GetSym(ASM_errorset &errors); void getaddress(SA_unpackedlines &srcline); }; #endif /*SA_H*/ ----- sa.cpp ------------------------------------------------------------------ // Syntax analyzer for macro assemblers for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #include "sa.h" #include "set.h" typedef Set<LA_starsym> symset; void SA::GetSym(ASM_errorset &errors) { Lex->getsym(SYM, errors); } void SA::getaddress(SA_unpackedlines &srcline) // Unpack the addressfield of line into srcline { symset allowed(LA_idsym, LA_numsym, LA_starsym); symset possible = allowed + symset(LA_commasym, LA_plussym, LA_minussym); srcline.address.length = 0; while (possible.memb(SYM.sym)) { if (!allowed.memb(SYM.sym)) srcline.errors.incl(ASM_invalidaddress); if (srcline.address.length < SA_maxterms - 1) srcline.address.length++; else srcline.errors.incl(ASM_excessfields); sprintf(srcline.address.term[srcline.address.length - 1].name, "%.*s", ASM_alength, SYM.str); srcline.address.term[srcline.address.length - 1].number = SYM.num; switch (SYM.sym) { case LA_numsym: srcline.address.term[srcline.address.length - 1].kind = SA_numeric; break; case LA_idsym: srcline.address.term[srcline.address.length - 1].kind = SA_alphameric; break; case LA_plussym: srcline.address.term[srcline.address.length - 1].kind = SA_plus; break; case LA_minussym: srcline.address.term[srcline.address.length - 1].kind = SA_minus; break; case LA_starsym: srcline.address.term[srcline.address.length - 1].kind = SA_star; break; case LA_commasym: srcline.address.term[srcline.address.length - 1].kind = SA_comma; break; } allowed = possible - allowed; GetSym(srcline.errors); // check trailing comment, parameters } if (!(srcline.address.length & 1)) srcline.errors.incl(ASM_invalidaddress); } void SA::parse(SA_unpackedlines &srcline) { symset startaddress(LA_idsym, LA_numsym, LA_starsym); srcline.labfield[0] = '\0'; strcpy(srcline.mnemonic, " "); srcline.comment[0] = '\0'; srcline.errors = ASM_errorset(); srcline.address.term[0].kind = SA_absent; srcline.address.term[0].number = 0; srcline.address.term[0].name[0] = '\0'; srcline.address.length = 0; GetSym(srcline.errors); // first on line - opcode or label ? if (SYM.sym == LA_eofsym) { strcpy(srcline.mnemonic, "END"); return; } srcline.labelled = SYM.islabel; if (srcline.labelled) // must look for the opcode { srcline.labelled = srcline.errors.isempty(); sprintf(srcline.labfield, "%.*s", ASM_alength, SYM.str); GetSym(srcline.errors); // probably an opcode } if (SYM.sym == LA_idsym) // has a mnemonic { sprintf(srcline.mnemonic, "%.*s", ASM_alength, SYM.str); GetSym(srcline.errors); // possibly an address if (startaddress.memb(SYM.sym)) getaddress(srcline); } if (SYM.sym == LA_comsym || SYM.sym == LA_unknown) { strcpy(srcline.comment, SYM.str); GetSym(srcline.errors); } if (SYM.sym != LA_eolsym) // spurious symbol { strcpy(srcline.comment, SYM.str); srcline.errors.incl(ASM_excessfields); } while (SYM.sym != LA_eolsym && SYM.sym != LA_eofsym) GetSym(srcline.errors); // consume garbage } SA::SA(LA * L) { Lex = L; } ----- st.h -------------------------------------------------------------------- // Table handler for one-pass macro assembler for single-accumulator machine // Version using simple linked list // P.D. Terry, Rhodes University, 1996 #ifndef ST_H #define ST_H #include "misc.h" #include "mc.h" #include "sh.h" enum ST_actions { ST_add, ST_subtract }; typedef void (*ST_patch)(MC_bytes mem[], MC_bytes b, MC_bytes v, ST_actions a); struct ST_forwardrefs { // forward references for undefined labels MC_bytes byte; // to be patched ST_actions action; // taken when patching ST_forwardrefs *nlink; // to next reference }; struct ST_entries { ASM_alfa name; // name MC_bytes value; // value once defined bool defined; // true after defining occurrence encountered ST_entries *slink; // to next entry ST_forwardrefs *flink; // to forward references }; class ST { public: void printsymboltable(bool &errors); // Summarizes symbol table at end of assembly, and alters errors to true if // any symbols have remained undefined void enter(char *name, MC_bytes value); // Adds name to table with known value void valueofsymbol(char *name, MC_bytes location, MC_bytes &value, ST_actions action, bool &undefined); // Returns value of required name, and sets undefined if not found. // Records action to be applied later in fixing up forward references. // location is the current value of the instruction location counter void outstandingreferences(MC_bytes *mem, ST_patch fix); // Walks symbol table, applying fix to outstanding references in mem ST(SH *S); // Associates table handler with source handler S (for listings) private: SH *Srce; ST_entries *lastsym; void findentry(ST_entries *&symentry, char *name, bool &found); }; #endif /*ST_H*/ ----- st.cpp ------------------------------------------------------------------ // Table handler for one-pass macro assembler for single-accumulator machine // Version using simply linked list // P.D. Terry, Rhodes University, 1996 #include "st.h" void ST::printsymboltable(bool &errors) { fprintf(Srce->lst, "\nSymbol Table\n"); fprintf(Srce->lst, "------------\n"); ST_entries *symentry = lastsym; while (symentry) { Srce->writetext(symentry->name, 10); if (!symentry->defined) { fprintf(Srce->lst, " --- undefined"); errors = true; } else { Srce->writehex(symentry->value, 3); fprintf(Srce->lst, "%5d", symentry->value); } putc('\n', Srce->lst); symentry = symentry->slink; } putc('\n', Srce->lst); } void ST::findentry(ST_entries *&symentry, char *name, bool &found) { symentry = lastsym; found = false; while (!found && symentry) { if (!strcmp(name, symentry->name)) found = true; else symentry = symentry->slink; } if (found) return; symentry = new ST_entries; // make new forward reference entry sprintf(symentry->name, "%.*s", ASM_alength, name); symentry->value = 0; symentry->defined = false; symentry->flink = NULL; symentry->slink = lastsym; lastsym = symentry; } void ST::enter(char *name, MC_bytes value) { ST_entries *symentry; bool found; findentry(symentry, name, found); symentry->value = value; symentry->defined = true; } void ST::valueofsymbol(char *name, MC_bytes location, MC_bytes &value, ST_actions action, bool &undefined) { ST_entries *symentry; ST_forwardrefs *forwardentry; bool found; findentry(symentry, name, found); value = symentry->value; undefined = !symentry->defined; if (!undefined) return; forwardentry = new ST_forwardrefs; // new node in reference chain forwardentry->byte = location; forwardentry->action = action; if (found) // it was already in the table forwardentry->nlink = symentry->flink; else // new entry in the table forwardentry->nlink = NULL; symentry->flink = forwardentry; } void ST::outstandingreferences(MC_bytes mem[], ST_patch fix) { ST_forwardrefs *link; ST_entries *symentry = lastsym; while (symentry) { link = symentry->flink; while (link) { fix(mem, link->byte, symentry->value, link->action); link = link->nlink; } symentry = symentry->slink; } } ST::ST(SH *S) { Srce = S; lastsym = NULL; } ----- st.h -------------------------------------------------------------------- // Table handler for one-pass macro assembler for single-accumulator machine // Version using hashing technique with collision stepping // P.D. Terry, Rhodes University, 1996 #ifndef ST_H #define ST_H #include "misc.h" #include "mc.h" #include "sh.h" const int tablemax = 239; // symbol table size const int tablestep = 7; // a prime number enum ST_actions { ST_add, ST_subtract }; typedef void (*ST_patch)(MC_bytes mem[], MC_bytes b, MC_bytes v, ST_actions a); typedef short tableindex; struct ST_forwardrefs { // forward references for undefined labels MC_bytes byte; // to be patched ST_actions action; // taken when patching ST_forwardrefs *nlink; // to next reference }; struct ST_entries { ASM_alfa name; // name MC_bytes value; // value once defined bool used; // true when in use already bool defined; // true after defining occurrence encountered ST_forwardrefs *flink; // to forward references }; class ST { public: void printsymboltable(bool &errors); // Summarizes symbol table at end of assembly, and alters errors // to true if any symbols have remained undefined void enter(char *name, MC_bytes value); // Adds name to table with known value void valueofsymbol(char *name, MC_bytes location, MC_bytes &value, ST_actions action, bool &undefined); // Returns value of required name, and sets undefined if not found. // Records action to be applied later in fixing up forward references. // location is the current value of the instruction location counter void outstandingreferences(MC_bytes mem[], ST_patch fix); // Walks symbol table, applying fix to outstanding references in mem ST(SH *S); // Associates table handler with source handler S (for listings) private: SH *Srce; ST_entries hashtable[tablemax + 1]; void findentry(tableindex &symentry, char *name, bool &found); }; #endif /*ST_H*/ ----- st.cpp ------------------------------------------------------------------ // Table handler for one-pass macro assembler for single-accumulator machine // Version using hashing technique with collision stepping // P.D. Terry, Rhodes University, 1996 #include "st.h" void ST::printsymboltable(bool &errors) { fprintf(Srce->lst, "\nSymbol Table\n"); fprintf(Srce->lst, "------------\n"); for (tableindex i = 0; i < tablemax; i++) { if (hashtable[i].used) { Srce->writetext(hashtable[i].name, 10); if (!hashtable[i].defined) { fprintf(Srce->lst, " --- undefined"); errors = true; } else { Srce->writehex(hashtable[i].value, 3); fprintf(Srce->lst, "%5d", hashtable[i].value); } putc('\n', Srce->lst); } } putc('\n', Srce->lst); } tableindex hashkey(char *ident) { const int large = (maxint - 256); // large number in hashing function int sum = 0, l = strlen(ident); for (int i = 0; i < l; i++) sum = (sum + ident[i]) % large; return (sum % tablemax); } void ST::findentry(tableindex &symentry, char *name, bool &found) { enum { looking, entered, caninsert, overflow } state; symentry = hashkey(name); state = looking; tableindex start = symentry; while (state == looking) { if (!hashtable[symentry].used) { state = caninsert; break; } if (!strcmp(name, hashtable[symentry].name)) { state = entered; break; } symentry = (symentry + tablestep) % tablemax; if (symentry == start) state = overflow; } switch (state) { case caninsert: sprintf(hashtable[symentry].name, "%.*s", ASM_alength, name); hashtable[symentry].value = 0; hashtable[symentry].used = true; hashtable[symentry].flink = NULL; hashtable[symentry].defined = false; break; case overflow: printf("Symbol table overflow\n"); exit(1); break; case entered: // no further action break; } found = (state == entered); } void ST::enter(char *name, MC_bytes value) { tableindex symentry; bool found; findentry(symentry, name, found); hashtable[symentry].value = value; hashtable[symentry].defined = true; } void ST::valueofsymbol(char *name, MC_bytes location, MC_bytes &value, ST_actions action, bool &undefined) { tableindex symentry; ST_forwardrefs *forwardentry; bool found; findentry(symentry, name, found); value = hashtable[symentry].value; undefined = !hashtable[symentry].defined; if (!undefined) return; forwardentry = new ST_forwardrefs; // new node in reference chain forwardentry->byte = location; forwardentry->action = action; if (found) // it was already in the table forwardentry->nlink = hashtable[symentry].flink; else // new entry in the table forwardentry->nlink = NULL; hashtable[symentry].flink = forwardentry; } void ST::outstandingreferences(MC_bytes mem[], ST_patch fix) { ST_forwardrefs *link; for (tableindex i = 0; i < tablemax; i++) { if (hashtable[i].used) { link = hashtable[i].flink; while (link) { fix(mem, link->byte, hashtable[i].value, link->action); link = link->nlink; } } } } ST::ST(SH *S) { Srce = S; for (tableindex i = 0; i < tablemax; i++) hashtable[i].used = false; } ----- mh.h -------------------------------------------------------------------- // Macro analyzer for macro assemblers for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #ifndef MH_H #define MH_H #include "asmbase.h" typedef struct MH_macentries *MH_macro; class MH { public: void newmacro(MH_macro &m, SA_unpackedlines header); // Creates m as a new macro, with given header line that includes the // formal parameters void storeline(MH_macro m, SA_unpackedlines line); // Adds line to the definition of macro m void checkmacro(char *name, MH_macro &m, bool &ismacro, int ¶ms); // Checks to see whether name is that of a predefined macro. Returns // ismacro as the result of the search. If successful, returns m as // the macro, and params as the number of formal parameters void expand(MH_macro m, SA_addresses actualparams, ASMBASE *assembler, bool &errors); // Expands macro m by invoking assembler for each line of the macro // definition, and using the actualparams supplied in place of the // formal parameters appearing in the macro header. // errors is altered to true if the assembly fails for any reason MH(); // Initializes macro handler private: MH_macro lastmac; int position(MH_macro m, char *str); void substituteactualparameters(MH_macro m, SA_addresses actualparams, SA_unpackedlines &nextline); }; #endif /*MH_H*/ ----- mh.cpp ------------------------------------------------------------------ // Macro analyzer for macro assemblers for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #include "misc.h" #include "mh.h" struct MH_lines { SA_unpackedlines text; // a single line of macro text MH_lines *link; // link to the next line in the macro }; struct MH_macentries { SA_unpackedlines definition; // header line MH_macro mlink; // link to next macro in list MH_lines *firstline, *lastline; // links to the text of this macro }; void MH::newmacro(MH_macro &m, SA_unpackedlines header) { m = new MH_macentries; m->definition = header; // store formal parameters m->firstline = NULL; // no text yet m->mlink = lastmac; // link to rest of macro definitions lastmac = m; // and this becomes the last macro added } void MH::storeline(MH_macro m, SA_unpackedlines line) { MH_lines *newline = new MH_lines; newline->text = line; // store source line newline->link = NULL; // at the end of the queue if (m->firstline == NULL) // first line of macro? m->firstline = newline; // form head of new queue else m->lastline->link = newline; // add to tail of existing queue m->lastline = newline; } void MH::checkmacro(char *name, MH_macro &m, bool &ismacro, int ¶ms) { m = lastmac; ismacro = false; params = 0; while (m && !ismacro) { if (!strcmp(name, m->definition.labfield)) { ismacro = true; params = m->definition.address.length; } else m = m->mlink; } } int MH::position(MH_macro m, char *str) // Search formals for match to str; returns 0 if no match { bool found = false; int i = m->definition.address.length - 1; while (i >= 0 && !found) { if (!strcmp(str, m->definition.address.term[i].name)) found = true; else i--; } return i; } void MH::substituteactualparameters(MH_macro m, SA_addresses actualparams, SA_unpackedlines &nextline) // Substitute label, mnemonic or address components into // nextline where necessary { int j = 0, i = position(m, nextline.labfield); // check label if (i >= 0) strcpy(nextline.labfield, actualparams.term[i].name); i = position(m, nextline.mnemonic); // check mnemonic if (i >= 0) strcpy(nextline.mnemonic, actualparams.term[i].name); j = 0; // check address fields while (j < nextline.address.length) { i = position(m, nextline.address.term[j].name); if (i >= 0) nextline.address.term[j] = actualparams.term[i]; j += 2; // bypass commas } } void MH::expand(MH_macro m, SA_addresses actualparams, ASMBASE *assembler, bool &errors) { SA_unpackedlines nextline; if (!m) return; // nothing to do MH_lines *current = m->firstline; while (current) { nextline = current->text; // retrieve line of macro text substituteactualparameters(m, actualparams, nextline); assembler->assembleline(nextline, errors); // and asssemble it current = current->link; } } MH::MH() { lastmac = NULL; } ----- asmbase.h --------------------------------------------------------------- // Base assembler class for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #ifndef ASMBASE_H #define ASMBASE_H #include "misc.h" #include "sa.h" class ASMBASE { public: virtual void assembleline(SA_unpackedlines &srcline, bool &failure) = 0; // Assemble srcline, reporting failure if it occurs }; #endif /*A_H*/ ----- as.h -------------------------------------------------------------------- // One-pass macro assembler for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #ifndef AS_H #define AS_H #include "asmbase.h" #include "mc.h" #include "st.h" #include "sh.h" #include "mh.h" class AS : ASMBASE { public: void assemble(bool &errors); // Assembles and lists program. // Assembled code is dumped to file for later interpretation, and left // in pseudo-machine memory for immediate interpretation if desired. // Returns errors = true if assembly fails virtual void assembleline(SA_unpackedlines &srcline, bool &failure); // Assemble srcline, reporting failure if it occurs AS(char *sourcename, char *listname, char *version, MC *M); // Instantiates version of the assembler to process sourcename, creating // listings in listname, and generating code for associated machine M private: SH *Srce; LA *Lex; SA *Parser; ST *Table; MC *Machine; MH *Macro; struct { ASM_alfa spelling; MC_bytes byte; } optable[256]; int opcodes; // number of opcodes actually defined struct objlines { MC_bytes location, opcode, address; }; objlines objline; // current line as assembled MC_bytes location; // location counter bool assembling; // monitor progress of assembly bool include; // handle conditional assembly MC_bytes bytevalue(char *mnemonic); void enter(char *mnemonic, MC_bytes thiscode); void termvalue(SA_terms term, MC_bytes &value, ST_actions action, bool &undefined, bool &badaddress); void evaluate(SA_addresses address, MC_bytes &value, bool &undefined, bool &malformed); void listerrors(ASM_errorset allerrors, bool &failure); void listcode(void); void listsourceline(SA_unpackedlines &srcline, bool coderequired, bool &failure); void definemacro(SA_unpackedlines &srcline, bool &failure); void firstpass(bool &errors); }; #endif /*AS_H*/ ----- as.cpp ------------------------------------------------------------------ // One-pass macro assembler for the single-accumulator machine // P.D. Terry, Rhodes University, 1996 #include "as.h" const bool nocodelisted = false; const bool codelisted = true; enum directives { AS_err = 61, // erroneous opcode AS_nul = 62, // blank opcode AS_beg = 63, // introduce program AS_end = 64, // end of source AS_mac = 65, // introduce macro AS_ds = 66, // define storage AS_equ = 67, // equate AS_org = 68, // set location counter AS_if = 69, // conditional AS_dc = 70 // define constant byte }; MC_bytes AS::bytevalue(char *mnemonic) { int look, l = 1, r = opcodes; do // binary search { look = (l + r) / 2; if (strcmp(mnemonic, optable[look].spelling) <= 0) r = look - 1; if (strcmp(mnemonic, optable[look].spelling) >= 0) l = look + 1; } while (l <= r); if (l > r + 1) return (optable[look].byte); // found it else return (optable[0].byte); // err entry } void AS::enter(char *mnemonic, MC_bytes thiscode) // Add (mnemonic, thiscode) to optable for future look up { strcpy(optable[opcodes].spelling, mnemonic); optable[opcodes].byte = thiscode; opcodes++; } void backpatch(MC_bytes mem[], MC_bytes location, MC_bytes value, ST_actions how) { switch (how) { case ST_add: mem[location] = (mem[location] + value) % 256; break; case ST_subtract: mem[location] = (mem[location] - value + 256) % 256; break; } } void AS::termvalue(SA_terms term, MC_bytes &value, ST_actions action, bool &undefined, bool &badaddress) // Determine value of a single term, recording outstanding action // if undefined so far, and recording badaddress if malformed { undefined = false; switch (term.kind) { case SA_absent: case SA_numeric: value = term.number % 256; break; case SA_star: value = location; break; case SA_alphameric: Table->valueofsymbol(term.name, location, value, action, undefined); break; default: badaddress = true; value = 0; break; } } void AS::evaluate(SA_addresses address, MC_bytes &value, bool &undefined, bool &malformed) // Determine value of address, recording whether undefined or malformed { ST_actions nextaction; MC_bytes nextvalue; bool unknown; malformed = false; termvalue(address.term[0], value, ST_add, undefined, malformed); int i = 1; while (i < address.length) { switch (address.term[i].kind) { case SA_plus: nextaction = ST_add; break; case SA_minus: nextaction = ST_subtract; break; default: nextaction = ST_add; malformed = true; break; } i++; termvalue(address.term[i], nextvalue, nextaction, unknown, malformed); switch (nextaction) { case ST_add: value = (value + nextvalue) % 256; break; case ST_subtract: value = (value - nextvalue + 256) % 256; break; } undefined = (undefined || unknown); i++; } } static char *ErrorMsg[] = { " - unknown opcode", " - address field not resolved", " - invalid address field", " - label missing", " - spurious address field", " - address field missing", " - address field too long", " - wrong number of parameters", " - invalid formal parameters", " - invalid label", " - unknown character", " - mismatched quotes", " - number too large", }; void AS::listerrors(ASM_errorset allerrors, bool &failure) { if (allerrors.isempty()) return; failure = true; fprintf(Srce->lst, "Next line has errors"); for (int error = ASM_invalidcode; error <= ASM_overflow; error++) if (allerrors.memb(error)) fprintf(Srce->lst, "%s\n", ErrorMsg[error]); } void AS::listcode(void) // List generated code bytes on source listing { Srce->writehex(objline.location, 4); if (objline.opcode >= AS_err && objline.opcode <= AS_if) fprintf(Srce->lst, " "); else if (objline.opcode <= MC_hlt) // OneByteOps Srce->writehex(objline.opcode, 7); else if (objline.opcode == AS_dc) // DC special case Srce->writehex(objline.address, 7); else // TwoByteOps { Srce->writehex(objline.opcode, 3); Srce->writehex(objline.address, 4); } } void AS::listsourceline(SA_unpackedlines &srcline, bool coderequired, bool &failure) // List srcline, with option of listing generated code { listerrors(srcline.errors, failure); if (coderequired) listcode(); else fprintf(Srce->lst, " "); Srce->writetext(srcline.labfield, 9); Srce->writetext(srcline.mnemonic, 9); int width = strlen(srcline.address.term[0].name); fputs(srcline.address.term[0].name, Srce->lst); for (int i = 1; i < srcline.address.length; i++) { width += strlen(srcline.address.term[i].name) + 1; putc(' ', Srce->lst); fputs(srcline.address.term[i].name, Srce->lst); } if (width < 30) Srce->writetext(" ", 30 - width); fprintf(Srce->lst, "%s\n", srcline.comment); } void AS::definemacro(SA_unpackedlines &srcline, bool &failure) // Handle introduction of a macro (possibly nested) { MC_bytes opcode; MH_macro macro; bool declared = false; int i = 0; if (srcline.labelled) // name must be present declared = true; else srcline.errors.incl(ASM_unlabelled); if (!(srcline.address.length & 1)) // must be an odd number of terms srcline.errors.incl(ASM_invalidaddress); while (i < srcline.address.length) // check that formals are names { if (srcline.address.term[i].kind != SA_alphameric) srcline.errors.incl(ASM_nonalpha); i += 2; // bypass commas } listsourceline(srcline, nocodelisted, failure); if (declared) Macro->newmacro(macro, srcline); // store header do { Parser->parse(srcline); // next line of macro text opcode = bytevalue(srcline.mnemonic); if (opcode == AS_mac) // nested macro? definemacro(srcline, failure); // recursion handles it else { listsourceline(srcline, nocodelisted, failure); if (declared && opcode != AS_end && srcline.errors.isempty()) Macro->storeline(macro, srcline); // add to macro text } } while (opcode != AS_end); } void AS::assembleline(SA_unpackedlines &srcline, bool &failure) // Assemble single srcline { if (!include) { include = true; return; } // conditional assembly bool badaddress, found, undefined; MH_macro macro; int formal; Macro->checkmacro(srcline.mnemonic, macro, found, formal); if (found) // expand macro and exit { if (srcline.labelled) Table->enter(srcline.labfield, location); if (formal != srcline.address.length) // number of params okay? srcline.errors.incl(ASM_mismatched); listsourceline(srcline, nocodelisted, failure); if (srcline.errors.isempty()) // okay to expand? Macro->expand(macro, srcline.address, this, failure); return; } badaddress = false; objline.location = location; objline.address = 0; objline.opcode = bytevalue(srcline.mnemonic); if (objline.opcode == AS_err) // check various constraints srcline.errors.incl(ASM_invalidcode); else if (objline.opcode > AS_mac || objline.opcode > MC_hlt && objline.opcode < AS_err) { if (srcline.address.length == 0) srcline.errors.incl(ASM_noaddress); } else if (objline.opcode != AS_mac && srcline.address.length != 0) srcline.errors.incl(ASM_hasaddress); if (objline.opcode >= AS_err && objline.opcode <= AS_dc) { switch (objline.opcode) // directives { case AS_beg: location = 0; break; case AS_org: evaluate(srcline.address, location, undefined, badaddress); if (undefined) srcline.errors.incl(ASM_undefinedlabel); objline.location = location; break; case AS_ds: if (srcline.labelled) Table->enter(srcline.labfield, location); evaluate(srcline.address, objline.address, undefined, badaddress); if (undefined) srcline.errors.incl(ASM_undefinedlabel); location = (location + objline.address) % 256; break; case AS_nul: case AS_err: if (srcline.labelled) Table->enter(srcline.labfield, location); break; case AS_equ: evaluate(srcline.address, objline.address, undefined, badaddress); if (srcline.labelled) Table->enter(srcline.labfield, objline.address); else srcline.errors.incl(ASM_unlabelled); if (undefined) srcline.errors.incl(ASM_undefinedlabel); break; case AS_dc: if (srcline.labelled) Table->enter(srcline.labfield, location); evaluate(srcline.address, objline.address, undefined, badaddress); Machine->mem[location] = objline.address; location = (location + 1) % 256; break; case AS_if: evaluate(srcline.address, objline.address, undefined, badaddress); if (undefined) srcline.errors.incl(ASM_undefinedlabel); include = (objline.address != 0); break; case AS_mac: definemacro(srcline, failure); break; case AS_end: assembling = false; break; } } else // machine ops { if (srcline.labelled) Table->enter(srcline.labfield, location); Machine->mem[location] = objline.opcode; if (objline.opcode > MC_hlt) // TwoByteOps { location = (location + 1) % 256; evaluate(srcline.address, objline.address, undefined, badaddress); Machine->mem[location] = objline.address; } location = (location + 1) % 256; // bump location counter } if (badaddress) srcline.errors.incl(ASM_invalidaddress); if (objline.opcode != AS_mac) listsourceline(srcline, codelisted, failure); } void AS::firstpass(bool &errors) // Make first and only pass over source code { SA_unpackedlines srcline; location = 0; assembling = true; include = true; errors = false; while (assembling) { Parser->parse(srcline); assembleline(srcline, errors); } Table->printsymboltable(errors); if (!errors) Table->outstandingreferences(Machine->mem, backpatch); } void AS::assemble(bool &errors) { printf("Assembling ...\n"); fprintf(Srce->lst, "(One Pass Macro Assembler)\n\n"); firstpass(errors); Machine->listcode(); } AS::AS(char *sourcename, char *listname, char *version, MC *M) { Machine = M; Srce = new SH(sourcename, listname, version); Lex = new LA(Srce); Parser = new SA(Lex); Table = new ST(Srce); Macro = new MH(); // enter opcodes and mnemonics in ALPHABETIC order // done this way for ease of modification later opcodes = 0; // bogus one for erroneous data enter("Error ", AS_err); // for lines with no opcode enter(" ", AS_nul); enter("ACI", MC_aci); enter("ACX", MC_acx); enter("ADC", MC_adc); enter("ADD", MC_add); enter("ADI", MC_adi); enter("ADX", MC_adx); enter("ANA", MC_ana); enter("ANI", MC_ani); enter("ANX", MC_anx); enter("BCC", MC_bcc); enter("BCS", MC_bcs); enter("BEG", AS_beg); enter("BNG", MC_bng); enter("BNZ", MC_bnz); enter("BPZ", MC_bpz); enter("BRN", MC_brn); enter("BZE", MC_bze); enter("CLA", MC_cla); enter("CLC", MC_clc); enter("CLX", MC_clx); enter("CMC", MC_cmc); enter("CMP", MC_cmp); enter("CPI", MC_cpi); enter("CPX", MC_cpx); enter("DC", AS_dc); enter("DEC", MC_dec); enter("DEX", MC_dex); enter("DS", AS_ds); enter("END", AS_end); enter("EQU", AS_equ); enter("HLT", MC_hlt); enter("IF", AS_if); enter("INA", MC_ina); enter("INB", MC_inb); enter("INC", MC_inc); enter("INH", MC_inh); enter("INI", MC_ini); enter("INX", MC_inx); enter("JSR", MC_jsr); enter("LDA", MC_lda); enter("LDI", MC_ldi); enter("LDX", MC_ldx); enter("LSI", MC_lsi); enter("LSP", MC_lsp); enter("MAC", AS_mac); enter("NOP", MC_nop); enter("ORA", MC_ora); enter("ORG", AS_org); enter("ORI", MC_ori); enter("ORX", MC_orx); enter("OTA", MC_ota); enter("OTB", MC_otb); enter("OTC", MC_otc); enter("OTH", MC_oth); enter("OTI", MC_oti); enter("POP", MC_pop); enter("PSH", MC_psh); enter("RET", MC_ret); enter("SBC", MC_sbc); enter("SBI", MC_sbi); enter("SBX", MC_sbx); enter("SCI", MC_sci); enter("SCX", MC_scx); enter("SHL", MC_shl); enter("SHR", MC_shr); enter("STA", MC_sta); enter("STX", MC_stx); enter("SUB", MC_sub); enter("TAX", MC_tax); } ----- mc.h -------------------------------------------------------------------- // Definition of simple single-accumulator machine and simple emulator // P.D. Terry, Rhodes University, 1996 #ifndef MC_H #define MC_H #include "misc.h" // machine instructions - order important enum MC_opcodes { MC_nop, MC_cla, MC_clc, MC_clx, MC_cmc, MC_inc, MC_dec, MC_inx, MC_dex, MC_tax, MC_ini, MC_inh, MC_inb, MC_ina, MC_oti, MC_otc, MC_oth, MC_otb, MC_ota, MC_psh, MC_pop, MC_shl, MC_shr, MC_ret, MC_hlt, MC_lda, MC_ldx, MC_ldi, MC_lsp, MC_lsi, MC_sta, MC_stx, MC_add, MC_adx, MC_adi, MC_adc, MC_acx, MC_aci, MC_sub, MC_sbx, MC_sbi, MC_sbc, MC_scx, MC_sci, MC_cmp, MC_cpx, MC_cpi, MC_ana, MC_anx, MC_ani, MC_ora, MC_orx, MC_ori, MC_brn, MC_bze, MC_bnz, MC_bpz, MC_bng, MC_bcc, MC_bcs, MC_jsr, MC_bad = 255 }; typedef enum { running, finished, nodata, baddata, badop } status; typedef unsigned char MC_bytes; class MC { public: MC_bytes mem[256]; // virtual machine memory void listcode(void); // Lists the 256 bytes stored in mem on requested output file void emulator(MC_bytes initpc, FILE *data, FILE *results, bool tracing); // Emulates action of the instructions stored in mem, with program counter // initialized to initpc. data and results are used for I/O. // Tracing at the code level may be requested void interpret(void); // Interactively opens data and results files, and requests entry point. // Then interprets instructions stored in MC_mem MC_bytes opcode(char *str); // Maps str to opcode, or to MC_bad (0FFH) if no match can be found MC(); // Initializes accumulator machine private: struct processor { MC_bytes a; // Accumulator MC_bytes sp; // Stack pointer MC_bytes x; // Index register MC_bytes ir; // Instruction register MC_bytes pc; // Program count bool z, p, c; // Condition flags }; processor cpu; status ps; char *mnemonics[256]; void trace(FILE *results, MC_bytes pcnow); void postmortem(FILE *results, MC_bytes pcnow); void setflags(MC_bytes MC_register); MC_bytes index(void); }; #endif /*MC_H*/ ----- mc.cpp ------------------------------------------------------------------ // Definition of simple single-accumulator machine and simple emulator // P.D. Terry, Rhodes University, 1996 #include "misc.h" #include "mc.h" // set break-in character as CTRL-A (cannot easily use \033 on MS-DOS) const int ESC = 1; inline void increment(MC_bytes &x) // Increment with folding at 256 { x = (x + 257) % 256; } inline void decrement(MC_bytes &x) // Decrement with folding at 256 { x = (x + 255) % 256; } MC_bytes MC::opcode(char *str) // Simple linear search suffices for illustration { for (int i = 0; str[i]; i++) str[i] = toupper(str[i]); MC_bytes l = MC_nop; while (l <= MC_jsr && strcmp(str, mnemonics[l])) l++; if (l <= MC_jsr) return l; else return MC_bad; } void MC::listcode(void) // Simply print all 256 bytes in 16 rows { MC_bytes nextbyte = 0; char filename[256]; printf("Listing code ... \n"); printf("Listing file [NUL] ? "); gets(filename); if (*filename == '\0') return; FILE *listfile = fopen(filename, "w"); if (listfile == NULL) listfile = stdout; putc('\n', listfile); for (int i = 1; i <= 16; i++) { for (int j = 1; j <= 16; j++) { fprintf(listfile, "%4d", mem[nextbyte]); increment(nextbyte); } putc('\n', listfile); } if (listfile != stdout) fclose(listfile); } void MC::trace(FILE *results, MC_bytes pcnow) // Simple trace facility for run time debugging { fprintf(results, " PC = %02X A = %02X ", pcnow, cpu.a); fprintf(results, " X = %02X SP = %02X ", cpu.x, cpu.sp); fprintf(results, " Z = %d P = %d C = %d", cpu.z, cpu.p, cpu.c); fprintf(results, " OPCODE = %02X (%s)\n", cpu.ir, mnemonics[cpu.ir]); } void MC::postmortem(FILE *results, MC_bytes pcnow) // Report run time error and position { switch (ps) { case badop: fprintf(results, "Illegal opcode"); break; case nodata: fprintf(results, "No more data"); break; case baddata: fprintf(results, "Invalid data"); break; } fprintf(results, " at %d\n", pcnow); trace(results, pcnow); printf("\nPress RETURN to continue\n"); scanf("%*[^\n]"); getchar(); listcode(); } inline void MC::setflags(MC_bytes MC_register) // Set P and Z flags according to contents of register { cpu.z = (MC_register == 0); cpu.p = (MC_register <= 127); } inline MC_bytes MC::index(void) // Get indexed address with folding at 256 { return ((mem[cpu.pc] + cpu.x) % 256); } void readchar(FILE *data, char &ch, status &ps) // Read ch and check for break-in and other awkward values { if (feof(data)) { ps = nodata; ch = ' '; return; } ch = getc(data); if (ch == ESC) ps = finished; if (ch < ' ' || feof(data)) ch = ' '; } int hexdigit(char ch) // Convert CH to equivalent value { if (ch >= 'a' && ch <= 'e') return(ch + 10 - 'a'); if (ch >= 'A' && ch <= 'E') return(ch + 10 - 'A'); if (isdigit(ch)) return(ch - '0'); else return(0); } int getnumber(FILE *data, int base, status &ps) // Read number in required base { bool negative = false; char ch; int num = 0; do { readchar(data, ch, ps); } while (!(ch > ' ' || feof(data) || ps != running)); if (ps == running) { if (feof(data)) ps = nodata; else { if (ch == '-') { negative = true; readchar(data, ch, ps); } else if (ch == '+') readchar(data, ch, ps); if (!isxdigit(ch)) ps = baddata; else { while (isxdigit(ch) && ps == running) { if (hexdigit(ch) < base && num <= (maxint - hexdigit(ch)) / base) num = base * num + hexdigit(ch); else ps = baddata; readchar(data, ch, ps); } } } if (negative) num = -num; if (num > 0) return num % 256; else return (256 - abs(num) % 256) % 256; } return 0; } void MC::emulator(MC_bytes initpc, FILE *data, FILE *results, bool tracing) { MC_bytes pcnow; // Old program count MC_bytes carry; // Value of carry bit cpu.z = false; cpu.p = false; cpu.c = false; // initialize flags cpu.a = 0; cpu.x = 0; cpu.sp = 0; // initialize registers cpu.pc = initpc; // initialize program counter ps = running; do { cpu.ir = mem[cpu.pc]; // fetch pcnow = cpu.pc; // record for use in tracing/postmortem increment(cpu.pc); // and bump in anticipation if (tracing) trace(results, pcnow); switch (cpu.ir) // execute { case MC_nop: break; case MC_cla: cpu.a = 0; break; case MC_clc: cpu.c = false; break; case MC_clx: cpu.x = 0; break; case MC_cmc: cpu.c = !cpu.c; break; case MC_inc: increment(cpu.a); setflags(cpu.a); break; case MC_dec: decrement(cpu.a); setflags(cpu.a); break; case MC_inx: increment(cpu.x); setflags(cpu.x); break; case MC_dex: decrement(cpu.x); setflags(cpu.x); break; case MC_tax: cpu.x = cpu.a; break; case MC_ini: cpu.a = getnumber(data, 10, ps); setflags(cpu.a); break; case MC_inb: cpu.a = getnumber(data, 2, ps); setflags(cpu.a); break; case MC_inh: cpu.a = getnumber(data, 16, ps); setflags(cpu.a); break; case MC_ina: char ascii; readchar(data, ascii, ps); if (feof(data)) ps = nodata; else { cpu.a = ascii; setflags(cpu.a); } break; case MC_oti: if (cpu.a < 128) fprintf(results, "%d ", cpu.a); else fprintf(results, "%d ", cpu.a - 256); if (tracing) putc('\n', results); break; case MC_oth: fprintf(results, "%02X ", cpu.a); if (tracing) putc('\n', results); break; case MC_otc: fprintf(results, "%d ", cpu.a); if (tracing) putc('\n', results); break; case MC_ota: putc(cpu.a, results); if (tracing) putc('\n', results); break; case MC_otb: int bits[8]; MC_bytes number = cpu.a; for (int loop = 0; loop <= 7; loop++) { bits[loop] = number % 2; number /= 2; } for (loop = 7; loop >= 0; loop--) fprintf(results, "%d", bits[loop]); putc(' ', results); if (tracing) putc('\n', results); break; case MC_psh: decrement(cpu.sp); mem[cpu.sp] = cpu.a; break; case MC_pop: cpu.a = mem[cpu.sp]; increment(cpu.sp); setflags(cpu.a); break; case MC_shl: cpu.c = (cpu.a * 2 > 255); cpu.a = cpu.a * 2 % 256; setflags(cpu.a); break; case MC_shr: cpu.c = cpu.a & 1; cpu.a /= 2; setflags(cpu.a); break; case MC_ret: cpu.pc = mem[cpu.sp]; increment(cpu.sp); break; case MC_hlt: ps = finished; break; case MC_lda: cpu.a = mem[mem[cpu.pc]]; increment(cpu.pc); setflags(cpu.a); break; case MC_ldx: cpu.a = mem[index()]; increment(cpu.pc); setflags(cpu.a); break; case MC_ldi: cpu.a = mem[cpu.pc]; increment(cpu.pc); setflags(cpu.a); break; case MC_lsp: cpu.sp = mem[mem[cpu.pc]]; increment(cpu.pc); break; case MC_lsi: cpu.sp = mem[cpu.pc]; increment(cpu.pc); break; case MC_sta: mem[mem[cpu.pc]] = cpu.a; increment(cpu.pc); break; case MC_stx: mem[index()] = cpu.a; increment(cpu.pc); break; case MC_add: cpu.c = (cpu.a + mem[mem[cpu.pc]] > 255); cpu.a = (cpu.a + mem[mem[cpu.pc]]) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_adx: cpu.c = (cpu.a + mem[index()] > 255); cpu.a = (cpu.a + mem[index()]) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_adi: cpu.c = (cpu.a + mem[cpu.pc] > 255); cpu.a = (cpu.a + mem[cpu.pc]) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_adc: carry = cpu.c; cpu.c = (cpu.a + mem[mem[cpu.pc]] + carry > 255); cpu.a = (cpu.a + mem[mem[cpu.pc]] + carry) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_acx: carry = cpu.c; cpu.c = (cpu.a + mem[index()] + carry > 255); cpu.a = (cpu.a + mem[index()] + carry) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_aci: carry = cpu.c; cpu.c = (cpu.a + mem[cpu.pc] + carry > 255); cpu.a = (cpu.a + mem[cpu.pc] + carry) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_sub: cpu.c = (cpu.a < mem[mem[cpu.pc]]); cpu.a = (cpu.a - mem[mem[cpu.pc]] + 256) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_sbx: cpu.c = (cpu.a < mem[index()]); cpu.a = (cpu.a - mem[index()] + 256) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_sbi: cpu.c = (cpu.a < mem[cpu.pc]); cpu.a = (cpu.a - mem[cpu.pc] + 256) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_sbc: carry = cpu.c; cpu.c = (cpu.a < mem[mem[cpu.pc]] + carry); cpu.a = (cpu.a - mem[mem[cpu.pc]] - carry + 256) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_scx: carry = cpu.c; cpu.c = (cpu.a < mem[index()] + carry); cpu.a = (cpu.a - mem[index()] - carry + 256) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_sci: carry = cpu.c; cpu.c = (cpu.a < mem[cpu.pc] + carry); cpu.a = (cpu.a - mem[cpu.pc] - carry + 256) % 256; increment(cpu.pc); setflags(cpu.a); break; case MC_cmp: cpu.c = (cpu.a < mem[mem[cpu.pc]]); setflags((cpu.a - mem[mem[cpu.pc]] + 256) % 256); increment(cpu.pc); break; case MC_cpx: cpu.c = (cpu.a < mem[index()]); setflags((cpu.a - mem[index()] + 256) % 256); increment(cpu.pc); break; case MC_cpi: cpu.c = (cpu.a < mem[cpu.pc]); setflags((cpu.a - mem[cpu.pc] + 256) % 256); increment(cpu.pc); break; case MC_ana: cpu.a &= mem[mem[cpu.pc]]; increment(cpu.pc); setflags(cpu.a); cpu.c = false; break; case MC_anx: cpu.a &= mem[index()]; increment(cpu.pc); setflags(cpu.a); cpu.c = false; break; case MC_ani: cpu.a &= mem[cpu.pc]; increment(cpu.pc); setflags(cpu.a); cpu.c = false; break; case MC_ora: cpu.a |= mem[mem[cpu.pc]]; increment(cpu.pc); setflags(cpu.a); cpu.c = false; break; case MC_orx: cpu.a |= mem[index()]; increment(cpu.pc); setflags(cpu.a); cpu.c = false; break; case MC_ori: cpu.a |= mem[cpu.pc]; increment(cpu.pc); setflags(cpu.a); cpu.c = false; break; case MC_brn: cpu.pc = mem[cpu.pc]; break; case MC_bze: if (cpu.z) cpu.pc = mem[cpu.pc]; else increment(cpu.pc); break; case MC_bnz: if (!cpu.z) cpu.pc = mem[cpu.pc]; else increment(cpu.pc); break; case MC_bpz: if (cpu.p) cpu.pc = mem[cpu.pc]; else increment(cpu.pc); break; case MC_bng: if (!cpu.p) cpu.pc = mem[cpu.pc]; else increment(cpu.pc); break; case MC_bcs: if (cpu.c) cpu.pc = mem[cpu.pc]; else increment(cpu.pc); break; case MC_bcc: if (!cpu.c) cpu.pc = mem[cpu.pc]; else increment(cpu.pc); break; case MC_jsr: decrement(cpu.sp); mem[cpu.sp] = (cpu.pc + 1) % 256; // push return address cpu.pc = mem[cpu.pc]; break; default: ps = badop; break; } } while (ps == running); if (ps != finished) postmortem(results, pcnow); } void MC::interpret(void) { char filename[256]; FILE *data, *results; bool tracing; int entry; printf("\nTrace execution (y/N/q)? "); char reply = getchar(); scanf("%*[^\n]"); getchar(); if (toupper(reply) != 'Q') { tracing = toupper(reply) == 'Y'; printf("\nData file [STDIN] ? "); gets(filename); if (filename[0] == '\0') data = NULL; else data = fopen(filename, "r"); if (data == NULL) { printf("taking data from stdin\n"); data = stdin; } printf("\nResults file [STDOUT] ? "); gets(filename); if (filename[0] == '\0') results = NULL; else results = fopen(filename, "w"); if (results == NULL) { printf("sending results to stdout\n"); results = stdout; } printf("Entry point? "); if (scanf("%d%*[^\n]", &entry) != 1) entry = 0; getchar(); emulator(entry % 256, data, results, tracing); if (results != stdout) fclose(results); if (data != stdin) fclose(data); } } MC::MC() { for (int i = 0; i <= 255; i++) mem[i] = MC_bad; // Initialize mnemonic table for (i = 0; i <= 255; i++) mnemonics[i] = "???"; mnemonics[MC_aci] = "ACI"; mnemonics[MC_acx] = "ACX"; mnemonics[MC_adc] = "ADC"; mnemonics[MC_add] = "ADD"; mnemonics[MC_adi] = "ADI"; mnemonics[MC_adx] = "ADX"; mnemonics[MC_ana] = "ANA"; mnemonics[MC_ani] = "ANI"; mnemonics[MC_anx] = "ANX"; mnemonics[MC_bcc] = "BCC"; mnemonics[MC_bcs] = "BCS"; mnemonics[MC_bng] = "BNG"; mnemonics[MC_bnz] = "BNZ"; mnemonics[MC_bpz] = "BPZ"; mnemonics[MC_brn] = "BRN"; mnemonics[MC_bze] = "BZE"; mnemonics[MC_cla] = "CLA"; mnemonics[MC_clc] = "CLC"; mnemonics[MC_clx] = "CLX"; mnemonics[MC_cmc] = "CMC"; mnemonics[MC_cmp] = "CMP"; mnemonics[MC_cpi] = "CPI"; mnemonics[MC_cpx] = "CPX"; mnemonics[MC_dec] = "DEC"; mnemonics[MC_dex] = "DEX"; mnemonics[MC_hlt] = "HLT"; mnemonics[MC_ina] = "INA"; mnemonics[MC_inb] = "INB"; mnemonics[MC_inc] = "INC"; mnemonics[MC_inh] = "INH"; mnemonics[MC_ini] = "INI"; mnemonics[MC_inx] = "INX"; mnemonics[MC_jsr] = "JSR"; mnemonics[MC_lda] = "LDA"; mnemonics[MC_ldi] = "LDI"; mnemonics[MC_ldx] = "LDX"; mnemonics[MC_lsi] = "LSI"; mnemonics[MC_lsp] = "LSP"; mnemonics[MC_nop] = "NOP"; mnemonics[MC_ora] = "ORA"; mnemonics[MC_ori] = "ORI"; mnemonics[MC_orx] = "ORX"; mnemonics[MC_ota] = "OTA"; mnemonics[MC_otb] = "OTB"; mnemonics[MC_otc] = "OTC"; mnemonics[MC_oth] = "OTH"; mnemonics[MC_oti] = "OTI"; mnemonics[MC_pop] = "POP"; mnemonics[MC_psh] = "PSH"; mnemonics[MC_ret] = "RET"; mnemonics[MC_sbc] = "SBC"; mnemonics[MC_sbi] = "SBI"; mnemonics[MC_sbx] = "SBX"; mnemonics[MC_sci] = "SCI"; mnemonics[MC_scx] = "SCX"; mnemonics[MC_shl] = "SHL"; mnemonics[MC_shr] = "SHR"; mnemonics[MC_sta] = "STA"; mnemonics[MC_stx] = "STX"; mnemonics[MC_sub] = "SUB"; mnemonics[MC_tax] = "TAX"; }