/* Moon Simulator. * * Author: Peter Grogono * Last modified: 30 January 1995 * * This source file was created using a tab width of 4 characters. */ #include #include #include #include #include /* Notes on definitions. * The memory occupies about 6 * MEMSIZE bytes. Enlarging it may create * problems for architectures with memory restrictions, such as the PC. * * The simulator is not completely independent of the underlying * processor. Known variations include: * - trace output depends on whether the host is big- or little endian. * - the MOON `sr' op uses the C `>>' operator. */ #define MEMSIZE 8191 /* Memory size in 4-byte words. */ #define MAXREG 16 /* Number of registers. */ #define MAXINFILES 20 /* Restricts # input files. */ #define MAXNAMELEN 60 /* Restricts path/file name length. */ #define BUFLEN 255 /* Scanner input buffer. */ #define TOKLEN 255 /* Scanner token buffer. */ #define RANGE 20 /* Determines size of memory dump. */ #define FALSE 0 #define TRUE 1 #define BYTE unsigned char /* The following function reports syntax errors. It is declared here so * that it can be used by memory functions; its definition appears in the * loader section. */ void syntaxerror (char *message); /* The following function reports run-time errors. It is declared here so * that it can be used by memory functions; its definition appears in the * execution section. */ void runtimeerror (char *message); /************************ MEMORY ********************************************/ /* A word of memory contains an instruction (A or B format), four bytes, * or a 32-bit signed integer. The simulated memory also contains two * flags, to tell whether the word contains an instruction and whether * it is a breakpoint. * * The simulated memory is an array of four-byte words. Simulated * addresses are byte addresses. Consequently, all addresses are * shifted right 2 bits before being used. Only the memory module * should know about this. Conventionally, is a byte address * and is a word address. */ typedef union { struct { unsigned op:6; unsigned ri:4; unsigned rj:4; unsigned rk:4; unsigned :14; } fmta; struct { unsigned op:6; unsigned ri:4; unsigned rj:4; unsigned :2; int k:16; /* Unsigned is not portable! */ } fmtb; BYTE byts [4]; long data; } wordtype; /* Each component of the memory array contains: * - word: The contents of simulated memory. * - cont: `a' => format A instruction * `b' => format B instruction * 'd' => data * `u' => undefined * - breakpoint: True if this is a breakpoint. */ struct { wordtype word; char cont; short breakpoint; } mem [MEMSIZE]; long ic; /* Instruction counter: contains address of instruction that will be executed next. */ wordtype ir; /* Instruction register: contains the instruction that will be executed next. */ long mar; /* Memory address register; stores word address of last access. */ wordtype mdr; /* Memory data register; stores result of last access. */ long entrypoint = -1; /* Address of first instruction */ long cycles = 0; /* Counts memory cycles. */ /* Initialize the memory: value=zero, kind=undef, no breakpoint. * Set "hardware" addresses to an illegal value. */ void initmem () { long wordaddr; for (wordaddr = 0; wordaddr < MEMSIZE; wordaddr++) { mem[wordaddr].word.data = 0; mem[wordaddr].cont = 'u'; mem[wordaddr].breakpoint = FALSE; ic = -1; mar = -1; } } /* Report a run-time error if an illegal address is used. */ short outofrange (long addr) { if (addr < 0 || (addr >> 2) > MEMSIZE) { runtimeerror("address error"); return 1; } return 0; } /* Report a run-time error if a memory word access is not on a * four-byte boundary. */ short misaligned (long addr) { if (addr & 3) { runtimeerror("alignment error"); return 1; } return 0; } /* Fetch the instruction at , store it in , and increment . */ short fetch () { short cont; if (outofrange(ic)) { ir.data = 0; return 0; } cont = mem[ic >> 2].cont; if (!(cont == 'a' || cont == 'b')) { runtimeerror("illegal instruction"); ir.data = 0; return 0; } ir = mem[ic >> 2].word; ic += 4; cycles += 10; return cont; } /* Fetch a data word from memory and return it. */ long getmemword (long addr) { long wordaddr; if (outofrange(addr) || misaligned(addr)) return 0; wordaddr = addr >> 2; if (wordaddr == mar) cycles += 1; else { mar = wordaddr; mdr = mem[wordaddr].word; cycles += 10; } return mdr.data; } /* Store a word in memory. */ void putmemword (long addr, long data) { long wordaddr; if (outofrange(addr) || misaligned(addr)) return; wordaddr = addr >> 2; if (mem[wordaddr].cont == 'a' || mem[wordaddr].cont == 'b') { runtimeerror("overwriting instructions"); return; } mdr.data = data; mar = wordaddr; mem[mar].word = mdr; mem[mar].cont = 'd'; cycles += 10; return; } /* Fetch a byte from memory. */ BYTE getmembyte (long addr) { long wordaddr = addr >> 2; short offset = addr & 3; if (outofrange(addr)) return 0; if (wordaddr == mar) cycles += 1; else { mar = wordaddr; cycles += 10; } mdr = mem[mar].word; return mdr.byts[offset]; } /* Store a byte in memory. */ void putmembyte (long addr, BYTE byt) { long wordaddr = addr >> 2; short offset = addr & 3; if (outofrange(addr)) return; if (mem[wordaddr].cont == 'a' || mem[wordaddr].cont == 'b') { runtimeerror("overwriting instructions"); return; } mem[wordaddr].word.byts[offset] = byt & 255; mem[wordaddr].cont = 'd'; return; } /* Store an instruction in memory. Used only by loader. */ void putmeminstr (long addr, wordtype word, char cont) { if (addr & 3) syntaxerror("alignment error"); else { long wordaddr = addr >> 2; mem[wordaddr].word = word; mem[wordaddr].cont = cont; } } /* Store a character in memory. Used only by loader. */ void putmemchar (long addr, short byte, char cont) { long wordaddr = addr >> 2; mem[wordaddr].word.byts[addr & 3] = byte; mem[wordaddr].cont = cont; } /********************** REGISTERS *******************************************/ /* There are sixteen registers, numbered 0 through 15. Each register * is a 32-bit word. Functions check the register address although an * error would indicate a fault in the loader rather than the simulator. * Register 0 is always 0. */ long regs [MAXREG]; /* Fetch the value of a register. */ long fetchreg (unsigned short regnum) { if (regnum > 15) { runtimeerror("simulator error (illegal register code)"); return 0; } return regs[regnum]; } /* Store a value in a register. */ void storereg (unsigned short regnum, long data) { if (regnum > 15) runtimeerror("simulator error (illegal register code)"); if (regnum > 0) regs[regnum] = data; } /******************** INSTRUCTION CODES *************************************/ /* Instruction codes and the corresponding strings; obviously, these two * declarations should correspond. The directives have codes although * they are not stored in the memory. Note that 0 is an illegal * instruction. */ enum optype { bad, lw, lb, sw, sb, add, sub, mul, divr, mod, and, or, not, ceq, cne, clt, cle, cgt, cge, addi, subi, muli, divi, modi, andi, ori, ceqi, cnei, clti, clei, cgti, cgei, sl, sr, gtc, ptc, bz, bnz, j, jr, jl, jlr, nop, hlt, entry, align, org, dw, db, res, last }; char opnames [last] [6] = { "", "lw", "lb", "sw", "sb", "add", "sub", "mul", "div", "mod", "and", "or", "not", "ceq", "cne", "clt", "cle", "cgt", "cge", "addi", "subi", "muli", "divi", "modi", "andi", "ori", "ceqi", "cnei", "clti", "clei", "cgti", "cgei", "sl", "sr", "getc", "putc", "bz", "bnz", "j", "jr", "jl", "jlr", "nop", "hlt", "entry", "align", "org", "dw", "db", "res" }; void showfmta (long addr, wordtype word) { char *opcode = opnames[word.fmta.op]; switch (word.fmta.op) { /* Operands Ri, Rj, Rk */ case add: case sub: case mul: case divr: case mod: case and: case or: case ceq: case cne: case clt: case cle: case cgt: case cge: printf("%5ld %-6s r%d, r%d, r%d", addr, opcode, word.fmta.ri, word.fmta.rj, word.fmta.rk); break; /* Operands Ri, Rj */ case not: case jlr: printf("%5ld %-6s r%d, r%d", addr, opcode, word.fmta.ri, word.fmta.rj); break; /* No operands */ case nop: case hlt: printf("%5ld %-6s", addr, opcode); break; } } void showfmtb (long addr, wordtype word) { char *opcode = opnames[word.fmtb.op]; switch (word.fmtb.op) { /* Operands Ri, K(Rj) */ case lw: case lb: printf("%5ld %-6s r%d, %d(r%d)", addr, opcode, word.fmtb.ri, word.fmtb.k, word.fmtb.rj); break; /* Operands K(Rj), Ri */ case sw: case sb: printf("%5ld %-6s %d(r%d), r%d", addr, opcode, word.fmtb.k, word.fmtb.rj, word.fmtb.ri); break; /* Operands Ri, Rj, K */ case addi: case subi: case muli: case divi: case modi: case andi: case ori: case ceqi: case cnei: case clti: case clei: case cgti: case cgei: printf("%5ld %-6s r%d, r%d, %d", addr, opcode, word.fmtb.ri, word.fmtb.rj, word.fmtb.k); break; /* Operands Ri, K */ case sl: case sr: case bz: case bnz: case jl: printf("%5ld %-6s r%d, %d", addr, opcode, word.fmtb.ri, word.fmtb.k); break; /* Operands Ri */ case gtc: case ptc: case jr: printf("%5ld %-6s r%d", addr, opcode, word.fmtb.ri); break; /* Operands K */ case j: printf("%5ld %-6s %d", addr, opcode, word.fmtb.k); break; } } /* Convert a word to a string of 4 characters. Non-graphics to ".". * Exact output depends on whether the host is big-endian or * little-endian. */ char *wordtochars (char *buf, wordtype word) { int i; for (i = 0; i < 4; i++) { char c = word.byts[i]; if (32 <= c && c <= 126) buf[i] = c; else buf[i] = '.'; } buf[4] = '\0'; return buf; } /* Display one word of memory. */ void showword (long addr) { char charbuf [5]; long wordaddr = addr >> 2; wordtype word = mem[wordaddr].word; if (addr & 3) { printf("Internal error: bad address!\n"); exit(1); } switch (mem[wordaddr].cont) { case 'a': showfmta(addr, word); break; case 'b': showfmtb(addr, word); break; case 'd': printf("%5ld %08lX %s %4ld", addr, word.data, wordtochars(charbuf, word), word.data); break; case 'u': printf("%5ld ??", addr); break; } } /****************************** SYMBOLS *************************************/ /* There is one for each symbol. It records the name and value * of the symbol and how often it has been defined (once is correct). * The also contains a pointer to a list of 's, each * of which contains an address where the value of the symbol is used. */ struct symnode { char *name; long val; short defs; struct usenode *uses; struct symnode *next; }; struct usenode { long addr; struct usenode *next; }; /* The base of the symbol table. */ struct symnode *symbols = NULL; /* Return a pointer to a symbol entry. This always succeeds, because * it creates a new entry if it can't find a matching entry. */ struct symnode *findsymbol (char *name) { struct symnode *p = symbols; while (p) { if (!strcmp(name, p->name)) return p; p = p->next; } /* No entry exists, so make one. */ p = (struct symnode *) malloc (sizeof(struct symnode)); if (p == NULL) { printf("No more memory!\n"); exit(1); } p->name = (char *) malloc(strlen(name) + 1); strcpy(p->name, name); p->val = 0; p->defs = 0; p->uses = NULL; p->next = symbols; symbols = p; return p; } /* Define a symbol. That is, associate the value with * the symbol . */ void defsymbol (char *name, long val) { struct symnode *p = findsymbol(name); p->val = val; p->defs++; } /* Use a symbol. That is, record the fact that the symbol * must be stored at . */ void usesymbol (char *name, long addr) { struct symnode *p = findsymbol(name); struct usenode *u = (struct usenode *) malloc (sizeof (struct usenode)); if (u == NULL) { printf("No more memory!\n"); exit(1); } u->addr = addr; u->next = p->uses; p->uses = u; } /* Return the value of a symbol, or -1 if it doesn't exist. */ long getsymbolval (char *name) { struct symnode *p = symbols; while (p) { if (!strcmp(name, p->name)) return (p->val); p = p->next; } return -1; } /* Display all symbols and their uses. */ void showsymbols () { short count = 0; struct symnode *p = symbols; while (p) { struct usenode *u = p->uses; printf("%-8s = %4ld Used at: ", p->name, p->val); while (u) { printf("%ld ", u->addr); u = u->next; } printf("\n"); p = p->next; if (++count > 20) { printf("Press enter to continue"); getchar(); count = 0; } } } /* Check symbol list for errors and return error count. */ int checksymbols () { int errors = 0; struct symnode *p = symbols; while (p) { if (p->defs == 0) { printf("Undefined symbol: %s.\n", p->name); errors++; } else if (p->defs > 1) { printf("Redefined symbol: %s.\n", p->name); errors++; } p = p->next; } return errors; } /* Store symbols at their respective locations. */ void storesymbols () { struct symnode *p = symbols; while (p) { struct usenode *u = p->uses; while (u) { long wordaddr = (u -> addr) >> 2; switch (mem[wordaddr].cont) { case 'b': mem[wordaddr].word.fmtb.k = (int) p->val; break; case 'd': mem[wordaddr].word.data = p->val; break; default: printf("Symbol storage error!\n"); break; } u = u->next; } p = p->next; } } /***************************** EXECUTION ************************************/ short newreg; /* Address of a register that has changed */ long newmem; /* Address of a memory location that has changed */ short running; /* True if the processor is running, false after errors */ long numsteps; /* Number of instructions executed in trace mode. */ /* Report a run-time error and stop the program. */ void runtimeerror (char *message) { printf("\n%5ld Run-time error: %s.\n", ic, message); running = FALSE; } /* Execute the instruction at address . */ void execinstr (short tracing) { long w1, w2, k, rk; short cont = fetch(); /* Move next instruction to `ir'. */ int ch; if (!running) return; newreg = -1; newmem = -1; switch (cont) { /* Format A instructions with register operands. */ case 'a': switch (ir.fmta.op) { /* add Ri, Rj, Rk */ case add: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) + fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* sub Ri, Rj, Rk */ case sub: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) - fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* mul Ri, Rj, Rk */ case mul: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) * fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* divr Ri, Rj, Rk */ case divr: rk = fetchreg(ir.fmta.rk); if (rk == 0) runtimeerror("division by zero"); else { storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) / rk); newreg = ir.fmta.ri; } break; /* mod Ri, Rj, Rk */ case mod: rk = fetchreg(ir.fmta.rk); if (rk == 0) runtimeerror("modulus with zero operand"); else { storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) % rk); newreg = ir.fmta.ri; } break; /* and Ri, Rj, Rk (32-bit logical AND) */ case and: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) & fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* or Ri, Rj, Rk (32-bit logical OR) */ case or: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) | fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* ceq Ri, Rj, Rk (Rj = Rk) */ case ceq: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) == fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* cne Ri, Rj, Rk */ case cne: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) != fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* clt Ri, Rj, Rk */ case clt: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) < fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* cle Ri, Rj, Rk */ case cle: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) <= fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* cgt Ri, Rj, Rk */ case cgt: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) > fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* cge Ri, Rj, Rk */ case cge: storereg(ir.fmta.ri, fetchreg(ir.fmta.rj) >= fetchreg(ir.fmta.rk)); newreg = ir.fmta.ri; break; /* not Ri, Rj (32-bit complement) */ case not: if (fetchreg(ir.fmta.rj) == 0) storereg(ir.fmta.ri, 1); else storereg(ir.fmta.ri, 0); newreg = ir.fmta.ri; break; /* jlr Ri, Rj (Jump to register and link) */ case jlr: storereg(ir.fmta.ri, ic); ic = fetchreg(ir.fmta.rj); newreg = ir.fmta.ri; break; /* nop */ case nop: break; /* hlt */ case hlt: running = FALSE; break; } break; /* Format B instructions have a 16-bit immediate operand. */ case 'b': switch (ir.fmtb.op) { /* lw Ri, K(Rj) (Load word) */ case lw: storereg(ir.fmtb.ri, getmemword(fetchreg(ir.fmtb.rj) + (long) ir.fmtb.k)); newreg = ir.fmtb.ri; break; /* lb Ri, K(Rj) (Load byte) */ case lb: w1 = getmembyte(fetchreg(ir.fmtb.rj) + (long) ir.fmtb.k); w2 = fetchreg(ir.fmtb.ri); storereg(ir.fmtb.ri, (w1) | (w2 & ~255)); newreg = ir.fmtb.ri; break; /* sw K(Rj), Ri (Store word) */ case sw: newmem = fetchreg(ir.fmtb.rj) + (long) ir.fmtb.k; putmemword(newmem, fetchreg(ir.fmtb.ri)); break; /* sb K(Rj), Ri (Store byte) */ case sb: newmem = fetchreg(ir.fmtb.rj) + (long) ir.fmtb.k; putmembyte(newmem, (BYTE) (fetchreg(ir.fmtb.ri) & 255)); break; /* addi Ri, Rj, K (Add immediate) */ case addi: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) + (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* subi Ri, Rj, K */ case subi: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) - (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* muli Ri, Rj, K */ case muli: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) * (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* divi Ri, Rj, K */ case divi: k = (long) ir.fmtb.k; if (k == 0) runtimeerror("division by zero"); else { storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) / k); newreg = ir.fmtb.ri; } break; /* modi Ri, Rj, K */ case modi: k = (long) ir.fmtb.k; if (k == 0) runtimeerror("division by zero"); else { storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) % k); newreg = ir.fmtb.ri; } break; /* andi Ri, Rj, K */ case andi: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) & (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* ori Ri, Rj, K */ case ori: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) | (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* ceqi Ri, Rj, K */ case ceqi: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) == (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* cnei Ri, Rj, K */ case cnei: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) != (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* clti Ri, Rj, K */ case clti: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) < (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* clei Ri, Rj, K */ case clei: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) <= (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* cgti Ri, Rj, K */ case cgti: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) > (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* cgei Ri, Rj, K */ case cgei: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.rj) >= (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* sl Ri, K (Shift left logical) */ case sl: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.ri) << (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* sr Ri, K (Shift right logical) */ case sr: storereg(ir.fmtb.ri, fetchreg(ir.fmtb.ri) >> (long) ir.fmtb.k); newreg = ir.fmtb.ri; break; /* bz Ri, K (Branch to K if Ri == 0) */ case bz: if (fetchreg(ir.fmtb.ri) == 0) ic = (long) ir.fmtb.k; break; /* bnz Ri, K (Branch to K if Ri != 0) */ case bnz: if (fetchreg(ir.fmtb.ri) != 0) ic = (long) ir.fmtb.k; break; /* jl Ri, K (Branch to K with link in Ri) */ case jl: storereg(ir.fmtb.ri, ic); ic = (long) ir.fmtb.k; newreg = ir.fmtb.ri; break; /* getc Ri (Read one character to Ri) */ case gtc: if (tracing) { char buf [256]; printf("\nEnter data for getc: "); fgets(buf, 256, stdin); if (buf[0] == '\0') ch = '\n'; else ch = buf[0]; } else ch = (BYTE) getchar(); storereg(ir.fmtb.ri, ch); newreg = ir.fmtb.ri; break; /* putc Ri (Write the character in Ri) */ case ptc: if (tracing) printf(" Output from putc: %c", fetchreg(ir.fmtb.ri)); else printf("%c", fetchreg(ir.fmtb.ri)); break; /* jr Ri (Jump to Ri) */ case jr: ic = fetchreg(ir.fmtb.ri); break; /* j K (Jump to K) */ case j: ic = (long) ir.fmtb.k; break; } break; } } /* Dump words of memory from addr-10 to addr+10. */ void dump (long addr) { long first = (addr - RANGE) & ~3; long last = (addr + RANGE) & ~3; if (first < 0) first = 0; if (last > MEMSIZE) last = MEMSIZE; for (addr = first; addr < last; addr += 4) { showword(addr); printf("\n"); } } /* Display a register. */ void showreg (short regnum) { wordtype word; char charbuf [5]; word.data = regs[regnum]; printf(" r%d = %08lX %s %4ld", regnum, word.data, wordtochars(charbuf, word), word.data); } /* Execute one instruction in trace mode. */ void traceinstr () { char charbuf [5]; showword(ic); execinstr(TRUE); if (running) { if (newreg >= 0) showreg(newreg); else if (newmem >= 0) { long addr = newmem >> 2; printf(" M[%ld] = %08lX %s %ld", newmem, mem[addr].word.data, wordtochars(charbuf, mem[addr].word), mem[addr].word.data); } printf("\n"); if (mem[ic >> 2].breakpoint) { printf("%5ld Breakpoint\n", ic); running = FALSE; } } } /* Execute instructions in trace mode. */ void runfor (long steps) { long cnt; running = TRUE; for (cnt = 0; cnt < steps; cnt++) { traceinstr(); if (!running) break; } } /* Fetch the operand of a trace instruction. The operand should be * either a number or a symbol. */ long getoperand (char *cp) { char *first; long val; while (*cp == ' ' || *cp == '\t') cp++; first = cp; if (isdigit(*cp)) { if (sscanf(first, "%ld", &val) == 1) return val; else { printf("?\n"); return -1; } } else if (isalpha(*cp)) { val = getsymbolval(cp); if (val < 0) printf("?\n"); return val; } else { printf("?\n"); return - 1; } } /* Show tracing instructions. */ void showtraceusage() { printf("The tracer prompts with `IC:-\'. IC is the instruction counter.\n"); printf("Upper or lower case letters are accepted. n must be positive.\n"); printf("\n"); printf(" Trace K instructions.\n"); printf("n Trace n instructions.\n"); printf("B Display breakpoints.\n"); printf("Bn Set a breakpoint at n.\n"); printf("C Clear all breakpoints.\n"); printf("Cn Clear the breakpoint at n.\n"); printf("D Dump memory near IC.\n"); printf("Dn Dump memory near n.\n"); printf("I Set IC to entry point.\n"); printf("In Set IC to n.\n"); printf("K Set K (# steps executed by ) to 10.\n"); printf("Kn Set K to n.\n"); printf("Q Quit.\n"); printf("R Show registers.\n"); printf("S Show symbols.\n"); printf("X Run to next break point.\n"); printf("Xn Run until IC = n.\n"); printf("\n"); } /* Execute the program with tracing. */ void exectrace () { char cmd [BUFLEN]; long addr; short regnum; ic = entrypoint; numsteps = 10; running = TRUE; while (1) { printf("%5ld:- ", ic); fgets(cmd, BUFLEN, stdin); if (!strcmp(cmd, "q") || !strcmp(cmd, "Q")) break; else if (!strcmp(cmd, "")) runfor(numsteps); else if (isdigit(*cmd)) { long steps = getoperand(cmd); if (steps > 0) runfor(steps); } else { char *cp = cmd; switch (*cp++) { /* B = show all breakpoints; Bn = set breakpoint. */ case 'b': case 'B': if (*cp == '\0') { printf("Breakpoints are at: "); for (addr = 0; addr < MEMSIZE; addr++) { if (mem[addr].breakpoint) printf("%ld ", addr << 2); } printf("\n"); } else { addr = getoperand(cp); if (addr >= 0) mem[addr >> 2].breakpoint = TRUE; } break; /* C = clear all breakpoints; Cn = clear a breakpoint. */ case 'c': case 'C': if (*cp == '\0') { for (addr = 0; addr < MEMSIZE; addr++) mem[addr].breakpoint = FALSE; } else { addr = getoperand(cp); if (addr >= 0) mem[addr >> 2].breakpoint = FALSE; } break; /* D = dump memory near ; Dn = dump memory near n. */ case 'd': case 'D': if (*cp == '\0') dump(ic); else { addr = getoperand(cp); if (addr >= 0) dump(addr); } break; /* Explain how to use it. */ case 'h': case 'H': case '?': showtraceusage(); break; /* I = set to entry point; In = set to n. */ case 'i': case 'I': if (*cp == '\0') ic = entrypoint; else { addr = getoperand(cp); if (addr >= 0) ic = addr; } break; /* K = set steps to 10; Kn = set steps to n. */ case 'k': case 'K': if (*cp == '\0') numsteps = 10; else { numsteps = getoperand(cp); if (numsteps < 0) numsteps = 10; } break; /* R = show registers. */ case 'r': case 'R': for (regnum = 0; regnum < MAXREG; regnum++) { showreg(regnum); printf("\n"); } break; /* S = show symbols. */ case 's': case 'S': showsymbols(); break; /* X = run to next breakpoint; Xn = run to n. */ case 'x': case 'X': if (*cp == '\0') { running = TRUE; while (running) { execinstr(FALSE); if (mem[ic >> 2].breakpoint) { printf("%5ld Breakpoint\n", ic); break; } } } else { addr = getoperand(cp); if (addr < 0) printf("?\n"); else { running = TRUE; while (running) { execinstr(FALSE); if (ic == addr) break; } } } break; default: printf("?\n"); break; } } } printf("\n%ld cycles.\n", cycles); } /* Execute the program without tracing. */ void exec () { ic = entrypoint; running = TRUE; while (running) { execinstr(FALSE); } printf("\n%ld cycles.\n", cycles); } /******************************* PARSING ***********************************/ enum tokentype { T_BAD, T_REG, T_OP, T_SYM, T_NUM, T_STR, T_COMMA, T_LP, T_RP, T_NULL }; struct { char symval [TOKLEN]; /* Characters of the token */ enum tokentype kind; /* Chosen from the enumeration */ char *pos; /* Pointer to start of token */ short reg; /* Register number for T_REG */ short op; /* Op code for T_OP */ long intval; /* Value for T_NUM */ } token; char oldval [TOKLEN]; char errmes [BUFLEN]; /* Error message */ int errorcount = 0; /* Number of errors detected. */ char *bp; /* Buffer pointer */ /* Record an error for reporting later; only the first error is recorded. */ void syntaxerror (char *message) { errorcount++; if (!strcmp(errmes, "")) { strcpy(errmes, "Error at `"); strcat(errmes, oldval); strcat(errmes, " "); strcat(errmes, token.symval); strcat(errmes, "': "); strcat(errmes, message); } } /* True if character can occur in a symbol */ short issymchar (char c) { return (isalnum(c) || c == '_'); } /* True if the string is a valid register name. */ short isreg (char *p) { long regnum = 0; if (!(*p == 'R' || *p == 'r')) return FALSE; p++; while (*p) { if (isdigit(*p)) regnum = 10 * regnum + *p - '0'; else return FALSE; p++; } if (regnum < MAXREG) { token.reg = (short) regnum; return TRUE; } else { syntaxerror("Illegal symbol"); return FALSE; } } /* Read a token and store appropriate values in the structure . * For error reporting, the token repesenting the string must be left * in . */ void next () { char *t = token.symval; short op; strcpy(oldval, token.symval); while (*bp == ' ' || *bp == '\t') bp++; token.pos = bp; if (isalpha(*bp)) { /* Read a register, op code, directive, or symbol */ while (issymchar(*bp)) *t++ = *bp++; *t = '\0'; /* printf("<%s>\n", token.symval); */ if (isreg(token.symval)) { token.kind = T_REG; return; } for (op = lw; op < last; op++) { if (!strcmp(token.symval, opnames[op])) { token.op = op; token.kind = T_OP; return; } } token.kind = T_SYM; return; } else if (*bp == '-' || *bp == '+' || isdigit(*bp)) { /* Read a signed decimal integer */ *t++ = *bp++; while (isdigit(*bp)) *t++ = *bp++; *t = '\0'; sscanf(token.symval, "%ld", &token.intval); token.kind = T_NUM; return; } else if (*bp == '"') { /* Read a character string enclosed in quotes */ bp++; while (1) { if (*bp == '"') { *t = '\0'; token.kind = T_STR; bp++; break; } if (*bp == '\0' || *bp == '\n') { syntaxerror("unterminated string"); token.kind = T_BAD; *t = '\0'; break; } *t++ = *bp++; } return; } else if (*bp == ',') { bp++; token.kind = T_COMMA; strcpy(token.symval, ","); return; } else if (*bp == '(') { bp++; token.kind = T_LP; strcpy(token.symval, "("); return; } else if (*bp == ')') { bp++; token.kind = T_RP; strcpy(token.symval, ")"); return; } else if (*bp == '%' || *bp == '\n' || *bp == '\0') { token.kind = T_NULL; strcpy(token.symval, " "); return; } else { token.kind = T_BAD; strcpy(token.symval, " "); } } /* Match a token */ void match (enum tokentype kind) { if (token.kind == kind) { next(); return; } switch (kind) { case T_COMMA: syntaxerror("',' expected"); break; case T_LP: syntaxerror("'(' expected"); break; case T_RP: syntaxerror("')' expected"); break; default: syntaxerror("Syntax error"); break; } } /* Parse an opcode. The error should never occur, since this function * is called only when the token type is know. */ short getop () { if (token.kind == T_OP) { short res = token.op; next(); return res; } syntaxerror("Opcode expected"); return 0; } /* Parse a register and return the register number */ short getreg () { if (token.kind == T_REG) { short res = token.reg; next(); return res; } syntaxerror("Register expected"); return 0; } /* Parse a constant (number or symbol) and return value */ long getlong (long addr) { if (token.kind == T_NUM) { long res = token.intval; next(); return res; } else if (token.kind == T_SYM) { usesymbol(token.symval, addr); next(); return 0; } syntaxerror("Constant expected"); return 0; } /* Similar to getlong(), but checks that its argument can be stored * in 16 bits. */ int getint (long addr) { long val = getlong(addr); if (labs(val) <= 32767) return (int) val; syntaxerror("Value cannot be represented with 16 bits"); return 0; } char buffer [BUFLEN]; /* Input buffer */ long addr = 0; int linenum = 0; /* Parse one line of source code from the buffer. */ void readline () { wordtype word; word.data = 0; bp = buffer; strcpy(errmes, ""); strcpy(oldval, ""); next(); while (token.kind == T_SYM) { defsymbol(token.symval, addr); next(); } if (token.kind == T_OP) { switch (token.op) { /* Format A -- registers only */ /* Operands Ri, Rj, Rk */ case add: case sub: case mul: case divr: case mod: case and: case or: case ceq: case cne: case clt: case cle: case cgt: case cge: word.fmta.op = getop(); word.fmta.ri = getreg(); match(T_COMMA); word.fmta.rj = getreg(); match(T_COMMA); word.fmta.rk = getreg(); putmeminstr(addr, word, 'a'); addr += 4; break; /* Operands Ri, Rj */ case not: case jlr: word.fmta.op = getop(); word.fmta.ri = getreg(); match(T_COMMA); word.fmta.rj = getreg(); putmeminstr(addr, word, 'a'); addr += 4; break; /* No operands */ case nop: case hlt: word.fmta.op = getop(); putmeminstr(addr, word, 'a'); addr += 4; break; /* Format B - operands and constant fields */ /* Operands Ri, K(Rj) */ case lw: case lb: word.fmtb.op = getop(); word.fmtb.ri = getreg(); match(T_COMMA); word.fmtb.k = getint(addr); match(T_LP); word.fmtb.rj = getreg(); match(T_RP); putmeminstr(addr, word, 'b'); addr += 4; break; /* Operands K(Rj), Ri */ case sw: case sb: word.fmtb.op = getop(); word.fmtb.k = getint(addr); match(T_LP); word.fmtb.rj = getreg(); match(T_RP); match(T_COMMA); word.fmtb.ri = getreg(); putmeminstr(addr, word, 'b'); addr += 4; break; /* Operands Ri, Rj, K */ case addi: case subi: case muli: case divi: case modi: case andi: case ori: case ceqi: case cnei: case clti: case clei: case cgti: case cgei: word.fmtb.op = getop(); word.fmtb.ri = getreg(); match(T_COMMA); word.fmtb.rj = getreg(); match(T_COMMA); word.fmtb.k = getint(addr); putmeminstr(addr, word, 'b'); addr += 4; break; /* Operands Ri, K */ case sl: case sr: case bz: case bnz: case jl: word.fmtb.op = getop(); word.fmtb.ri = getreg(); match(T_COMMA); word.fmtb.k = getint(addr); putmeminstr(addr, word, 'b'); addr += 4; break; /* Operands Ri */ case gtc: case ptc: case jr: word.fmtb.op = getop(); word.fmtb.ri = getreg(); putmeminstr(addr, word, 'b'); addr += 4; break; /* Operands K */ case j: word.fmtb.op = getop(); word.fmtb.k = getint(addr); putmeminstr(addr, word, 'b'); addr += 4; break; /* Set the entry point of the program. */ case entry: next(); if (entrypoint < 0) { entrypoint = addr; break; } syntaxerror("More than one entry point"); break; /* Adjust the address to the next word boundary. */ case align: next(); if (addr & 3) addr = (addr & ~3) + 4; break; /* Set the address to the given value. */ case org: next(); addr = getlong(addr); break; /* Store words. */ case dw: next(); while (token.kind == T_NUM || token.kind == T_SYM) { word.data = getlong(addr); putmeminstr(addr, word, 'd'); addr += 4; if (token.kind == T_COMMA) next(); else break; } break; /* Store bytes */ case db: next(); while (1) { if (token.kind == T_NUM) { if (0 <= token.intval && token.intval <= 255) { putmemchar(addr, token.intval, 'd'); addr++; } else syntaxerror("Value cannot be represented with 8 bits"); next(); } else if (token.kind == T_STR) { char *t = token.symval; while (*t) { putmemchar(addr, *t++, 'd'); addr++; } next(); } if (token.kind == T_COMMA) next(); else if (token.kind == T_NULL) break; else { syntaxerror("Syntax error in byte list"); break; } } break; /* Reserve the given number of words. */ case res: next(); addr += getlong(addr); break; /* Should never get here. */ default: syntaxerror("Unrecognized statement"); break; } } if (token.kind != T_NULL && errorcount < 5) { printf("Warning: junk following `%s' on next line.\n", token.symval); printf("%4d %s\n", linenum, buffer); errorcount++; } } /* Load a source file. */ void load (FILE *inp, FILE *out, short listing) { linenum = 0; while (fgets(buffer, BUFLEN - 1, inp)) { long oldaddr = addr; linenum++; readline(); if (listing) fprintf(out, "%5d %5ld %s", linenum, oldaddr, buffer); if (strcmp(errmes, "")) { printf("%5d %5ld %s", linenum, oldaddr, buffer); printf(" >>>>> %s\n", errmes); if (listing) fprintf(out, " >>>>> %s\n", errmes); } } } /*************************** USER INSTRUCTION *******************************/ void showusage () { printf("Usage:\n"); printf(" moon { option | filename }\n"); printf("The command line may contain source file names and options in any order.\n"); printf("There should be at least one source file. Source files will be loaded\n"); printf("in the order in which they are given.\n"); printf("Options:\n"); printf(" +p print listing\n"); printf(" -p (default) do not print listing\n"); printf(" +s display symbol values\n"); printf(" -s (default) do not display symbol values\n"); printf(" +t start in trace mode\n"); printf(" -t (default) execute without tracing\n"); printf(" +x (default) execute the program\n"); printf(" -x do not execute the program\n"); printf("Input files:\n"); printf(" If an input file name does not contain `.', the suffix\n"); printf(" `.n' will be appended to it.\n"); printf("Listing files:\n"); printf(" Source files may be listed selectively. The command\n"); printf(" moon -p lib +p appl\n"); printf(" would create a listing for `appl.m' but not for `lib.m'.\n"); printf(" The list file is named `moon.prn' by default.\n"); printf(" Use +o or -o followed by a name to changes the list file name.\n"); } /*************************** MAIN PROGRAM ***********************************/ int main (int argc, char *argv[]) { struct { char name [MAXNAMELEN]; short list; } filedescs [MAXINFILES]; int numfiles = 0; char outname [MAXNAMELEN] = ""; short dump = FALSE; /* D Dump memory */ short listing = FALSE; /* P Generate a listing of the source code */ short symbols = FALSE; /* S Display symbol values */ short tracing = FALSE; /* T Execute program in trace mode */ short execute = TRUE; /* X Execute the program after loading */ short listreq = FALSE; /* A listing is needed */ long addr; int arg, fil; FILE *inp, *out; regs[0] = 0; /* Register 0 is always 0. */ /* If there are no arguments, help the poor user. */ if (argc <= 1) { showusage(); exit(0); } /* Process command line arguments. * There should be at least one argument. Arguments that start with * + (-) turn flags on (off). Other arguments are input file names. * The listing switch (+-l) is processed in sequence so that files * may be listed selectively. */ for (arg = 1; arg < argc; arg++) { char *p = argv[arg]; if (*p == '+') { p++; switch (*p++) { case 'd': case 'D': dump = TRUE; break; case 'o': case 'O': strcpy(outname, p); break; case 'p': case 'P': listing = TRUE; break; case 's': case 'S': symbols = TRUE; break; case 't': case 'T': tracing = TRUE; break; case 'x': case 'X': execute = TRUE; break; default: printf("Illegal option: +%s\n", --p); exit(1); } } else if (*p == '-') { p++; switch (*p++) { case 'd': case 'D': dump = FALSE; break; case 'o': case 'O': strcpy(outname, p); break; case 'p': case 'P': listing = FALSE; break; case 's': case 'S': symbols = FALSE; break; case 't': case 'T': tracing = FALSE; break; case 'x': case 'X': execute = FALSE; break; default: printf("Illegal option: -%s\n", --p); exit(1); } } else { if (numfiles >= MAXINFILES) { printf("Too many input files!\n"); exit(1); } strcpy(filedescs[numfiles].name, p); filedescs[numfiles].list = listing; if (listing) listreq = TRUE; numfiles++; } } /* Nothing to do if there were no files on the command line. */ if (numfiles == 0) { printf("No input files!\n"); exit(1); } /* Attempt to open an output file if a listing is required. * If no output file was named, use a default name. */ if (listreq) { if (strlen(outname) == 0) strcpy(outname,"moon.prn"); if ((out = fopen(outname, "w")) == NULL) { printf("Unable to open listing file %s.\n", outname); exit(1); } printf("Writing listing to %s.\n", outname); } /* Process each input file. If no extension is given, assume .m. * If the file can be opened, load assembler code from it. */ initmem(); defsymbol("topaddr", 4 * MEMSIZE); for (fil = 0; fil < numfiles; fil++) { if (!strchr(filedescs[fil].name, '.')) strcat(filedescs[fil].name, ".m"); if ((inp = fopen(filedescs[fil].name, "r")) == NULL) { printf("Unable to open input file: %s.\n", filedescs[fil].name); exit(1); } else { short listing = filedescs[fil].list; printf("Loading %s.\n", filedescs[fil].name); if (listing) { fprintf(out, "MOON listing of %s.\n\n", filedescs[fil].name); load(inp, out, TRUE); fprintf(out, "\n"); } else load(inp, out, FALSE); fclose(inp); } } if (listreq) fclose(out); /* Check symbols and entry point. If there are errors, stop now. */ errorcount += checksymbols(); if (entrypoint < 0) { printf("There is no `entry' directive.\n"); errorcount++; } if (errorcount > 0) { printf("Loader errors -- no execution.\n"); exit(1); } /* Store values of symbols where they are used in the program. * Display them if requested. */ storesymbols(); if (symbols) showsymbols(); /* If a dump was requested, dump the memory. This option is not * advertised and therefore need not be supported. */ if (dump) { printf("Memory dump:\n"); for (addr = 0; addr < MEMSIZE; addr += 4) { if (mem[addr >> 2].cont != 'u') { showword(addr); printf("\n"); } } printf("\n"); } /* Execute the program in normal or trace mode. */ if (execute) { if (tracing) exectrace(); else exec(); } return 0; }