static char ch; // look ahead character for scanner static void GetChar() { // Obtains next character ch from input, or CHR(0) if EOF reached // Reflect ch to output if (atEndOfFile) { ch = EOF; } else { ch = input.ReadChar(); atEndOfFile = ch == EOF; if (!atEndOfFile) { output.Write(ch); if (ch == '\n') { lineCnt++; chPos = 0; } else { chPos++; } } } } // GetChar
public static void Main(string[] args) { // check that arguments have been supplied if (args.Length != 2) { Console.WriteLine("missing args"); System.Environment.Exit(1); } // attempt to open data file InFile data = new InFile(args[0]); if (data.OpenError()) { Console.WriteLine("cannot open " + args[0]); System.Environment.Exit(1); } // attempt to open results file OutFile results = new OutFile(args[1]); if (results.OpenError()) { Console.WriteLine("cannot open " + args[1]); System.Environment.Exit(1); } // various initializations int total = 0; IntSet mySet = new IntSet(); IntSet smallSet = new IntSet(1, 2, 3, 4, 5); string smallSetStr = smallSet.ToString(); // read and process data file int item = data.ReadInt(); while (!data.NoMoreData()) { total = total + item; if (item > 0) { mySet.Incl(item); } item = data.ReadInt(); } // write various results to output file results.Write("total = "); results.WriteLine(total, 5); results.WriteLine("unique positive numbers " + mySet.ToString()); results.WriteLine("union with " + smallSetStr + " = " + mySet.Union(smallSet).ToString()); results.WriteLine("intersection with " + smallSetStr + " = " + mySet.Intersection(smallSet).ToString()); /* or simply * results.WriteLine("union with " + smallSetStr + " = " + mySet.Union(smallSet)); * results.WriteLine("intersection with " + smallSetStr + " = " + mySet.Intersection(smallSet)); */ results.Close(); } // Main
} // Types.Name public static void Show(OutFile lst) { // For use in debugging foreach (string s in typeNames) { lst.Write(s + " "); } lst.WriteLine(); } // Types.Show
static void StackDump(OutFile results, int pcNow) { // Dump local variable and stack area - useful for debugging int onLine = 0; results.Write("\nStack dump at " + pcNow); results.Write(" SP:"); results.Write(cpu.sp, 4); results.Write(" FP:"); results.Write(cpu.fp, 4); results.Write(" HP:"); results.Write(cpu.hp, 4); results.Write(" HB:"); results.WriteLine(heapBase, 4); for (int i = stackBase - 1; i >= cpu.sp; i--) { results.Write(i, 7); results.Write(mem[i], 5); onLine++; if (onLine % 8 == 0) { results.WriteLine(); } } results.WriteLine(); } // PVM.StackDump
} // PVM.StackDump static void HeapDump(OutFile results, int pcNow) { // Dump heap area - useful for debugging int onLine = 0; results.Write("\nHeap dump at " + pcNow); results.Write(" SP:"); results.Write(cpu.sp, 4); results.Write(" FP:"); results.Write(cpu.fp, 4); results.Write(" HP:"); results.Write(cpu.hp, 4); results.Write(" HB:"); results.WriteLine(heapBase, 4); for (int i = heapBase; i < cpu.hp; i++) { results.Write(i, 7); results.Write(mem[i], 5); onLine++; if (onLine % 8 == 0) { results.WriteLine(); } } results.WriteLine(); } // PVM.HeapDump
static char ch; // look ahead character for scanner static void GetChar() { // Obtains next character ch from input, or CHR(0) if EOF reached // Reflect ch to output if (atEndOfFile) ch = EOF; else { ch = input.ReadChar(); atEndOfFile = ch == EOF; if (!atEndOfFile) output.Write(ch); } } // GetChar
} // Table.Truncate public static void PrintTable(OutFile lst) { // Prints symbol table for diagnostic/debugging purposes lst.WriteLine(); lst.WriteLine("Symbol table"); Scope scope = topScope; while (scope != null) { Entry entry = scope.firstInScope; while (entry != sentinelEntry) { lst.Write(Truncate(entry.name) + " "); lst.Write(entry.level, -3); lst.Write(Truncate(Types.Name(entry.type))); switch (entry.kind) { case Kinds.Con: lst.Write(" Constant "); lst.WriteLine(entry.value, 0); break; case Kinds.Var: lst.Write(" Variable "); lst.Write(entry.offset, 3); lst.WriteLine(); break; case Kinds.Fun: lst.Write(" Function "); lst.Write(entry.entryPoint, 3); lst.WriteLine(entry.nParams, 3); break; } entry = entry.nextInScope; } scope = scope.outer; } lst.WriteLine(); } // Table.PrintTable
} // PVM.HeapDump static void Trace(OutFile results, int pcNow, bool traceStack, bool traceHeap) { // Simple trace facility for run time debugging if (traceStack) { StackDump(results, pcNow); } if (traceHeap) { HeapDump(results, pcNow); } results.Write(" PC:"); results.Write(pcNow, 5); results.Write(" FP:"); results.Write(cpu.fp, 5); results.Write(" SP:"); results.Write(cpu.sp, 5); results.Write(" HP:"); results.Write(cpu.hp, 5); results.Write(" TOS:"); if (cpu.sp < memSize) { results.Write(mem[cpu.sp], 5); } else { results.Write(" ????"); } results.Write(" " + mnemonics[cpu.ir], -8); switch (cpu.ir) // two word opcodes { case PVM.call: case PVM.bfalse: case PVM.btrue: case PVM.brn: case PVM.bze: case PVM.dsp: case PVM.lda: case PVM.ldc: case PVM.ldl: case PVM.stl: case PVM.stlc: case PVM.prns: case PVM.fprns: results.Write(mem[cpu.pc], 7); break; default: break; } results.WriteLine(); } // PVM.Trace
} // PVM.Interpret public static void ListCode(string fileName, int codeLen) { // Lists the codeLen instructions stored in mem on a named output file int i, j; if (fileName == null) { return; } OutFile codeFile = new OutFile(fileName); /* ------------- The following may be useful for debugging the interpreter * i = 0; * while (i < codeLen) { * codeFile.Write(mem[i], 5); * if ((i + 1) % 15 == 0) codeFile.WriteLine(); * i++; * } * codeFile.WriteLine(); * * ------------- */ i = 0; codeFile.WriteLine("ASSEM\nBEGIN"); while (i < codeLen && mem[i] != PVM.nul) { int o = mem[i] % (PVM.nul + 1); // force in range codeFile.Write(" {"); codeFile.Write(i, 5); codeFile.Write(" } "); codeFile.Write(mnemonics[o], -8); switch (o) // two word opcodes { case PVM.call: case PVM.bfalse: case PVM.btrue: case PVM.brn: case PVM.bze: case PVM.dsp: case PVM.lda: case PVM.ldc: case PVM.ldl: case PVM.stl: case PVM.stlc: i = (i + 1) % memSize; codeFile.Write(mem[i]); break; case PVM.prns: // special case i = (i + 1) % memSize; j = mem[i]; codeFile.Write(" \""); while (mem[j] != 0) { switch (mem[j]) { case '\\': codeFile.Write("\\\\"); break; case '\"': codeFile.Write("\\\""); break; case '\'': codeFile.Write("\\\'"); break; case '\b': codeFile.Write("\\b"); break; case '\t': codeFile.Write("\\t"); break; case '\n': codeFile.Write("\\n"); break; case '\f': codeFile.Write("\\f"); break; case '\r': codeFile.Write("\\r"); break; default: codeFile.Write((char)mem[j]); break; } j--; } codeFile.Write("\""); break; } // switch i = (i + 1) % memSize; codeFile.WriteLine(); } // while (i < codeLen) codeFile.WriteLine("END."); codeFile.Close(); } // PVM.ListCode
} // PVM.PostMortem // The interpreters and utility methods public static void Emulator(int initPC, int codeLen, int initSP, InFile data, OutFile results, bool tracing, bool traceStack, bool traceHeap) { // Emulates action of the codeLen instructions stored in mem[0 .. codeLen-1], with // program counter initialized to initPC, stack pointer initialized to initSP. // data and results are used for I/O. Tracing at the code level may be requested int pcNow; // current program counter int loop; // internal loops int tos, sos; // value popped from stack stackBase = initSP; heapBase = codeLen; // initialize boundaries cpu.hp = heapBase; // initialize registers cpu.sp = stackBase; cpu.gp = stackBase; cpu.mp = stackBase; cpu.fp = stackBase; cpu.pc = initPC; // initialize program counter ps = running; // prepare to execute int ops = 0; timer.Start(); do { ops++; pcNow = cpu.pc; // retain for tracing/postmortem cpu.ir = mem[cpu.pc++]; // fetch // if (tracing) Trace(results, pcNow, traceStack, traceHeap); switch (cpu.ir) // execute { case PVM.nop: // no operation break; case PVM.dsp: // decrement stack pointer (allocate space for variables) cpu.sp -= mem[cpu.pc++]; break; case PVM.ldc: // push constant value mem[--cpu.sp] = mem[cpu.pc++]; break; case PVM.lda: // push local address mem[--cpu.sp] = cpu.fp - 1 - mem[cpu.pc++]; break; case PVM.ldv: // dereference mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.sto: // store tos = mem[cpu.sp++]; mem[mem[cpu.sp++]] = tos; break; case PVM.ldxa: // heap array indexing tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] + tos; break; case PVM.inpi: // integer input mem[mem[cpu.sp++]] = data.ReadInt(); break; case PVM.inpc: // char input mem[mem[cpu.sp++]] = data.ReadChar(); break; case PVM.prni: // integer output // if (tracing) results.Write(padding); results.Write(mem[cpu.sp++], 0); // if (tracing) results.WriteLine(); break; case PVM.prnc: // integer output results.Write((char)mem[cpu.sp++], 0); break; case PVM.inpb: // boolean input mem[mem[cpu.sp++]] = data.ReadBool() ? 1 : 0; break; case PVM.prnb: // boolean output // if (tracing) results.Write(padding); if (mem[cpu.sp++] != 0) { results.Write(" true "); } else { results.Write(" false "); } // if (tracing) results.WriteLine(); break; case PVM.prns: // string output // if (tracing) results.Write(padding); loop = mem[cpu.pc++]; while (mem[loop] != 0) { results.Write((char)mem[loop]); loop--; } // if (tracing) results.WriteLine(); break; case PVM.prnl: // newline results.WriteLine(); break; case PVM.neg: // integer negation mem[cpu.sp] = -mem[cpu.sp]; break; case PVM.add: // integer addition tos = mem[cpu.sp++]; mem[cpu.sp] += tos; break; case PVM.sub: // integer subtraction tos = mem[cpu.sp++]; mem[cpu.sp] -= tos; break; case PVM.mul: tos = mem[cpu.sp++]; sos = mem[cpu.sp]; // integer multiplication int freeSpace = (memSize - cpu.hp - (memSize - cpu.sp)); if ((freeSpace / tos) > sos) { mem[cpu.sp] *= tos; } else { ps = badVal; } break; case PVM.div: // integer division (quotient) tos = mem[cpu.sp++]; if (tos == 0) { ps = divZero; } else { mem[cpu.sp] /= tos; } break; case PVM.rem: // integer division (remainder) tos = mem[cpu.sp++]; mem[cpu.sp] %= tos; break; case PVM.not: // logical negation mem[cpu.sp] = mem[cpu.sp] == 0 ? 1 : 0; break; case PVM.and: // logical and tos = mem[cpu.sp++]; mem[cpu.sp] &= tos; break; case PVM.or: // logical or tos = mem[cpu.sp++]; mem[cpu.sp] |= tos; break; case PVM.ceq: // logical equality tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] == tos ? 1 : 0; break; case PVM.cne: // logical inequality tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] != tos ? 1 : 0; break; case PVM.clt: // logical less tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] < tos ? 1 : 0; break; case PVM.cle: // logical less or equal tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] <= tos ? 1 : 0; break; case PVM.cgt: // logical greater tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] > tos ? 1 : 0; break; case PVM.cge: // logical greater or equal tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] >= tos ? 1 : 0; break; case PVM.brn: // unconditional branch cpu.pc = mem[cpu.pc++]; break; case PVM.bze: // pop top of stack, branch if false int target = mem[cpu.pc++]; if (mem[cpu.sp++] == 0) { cpu.pc = target; } break; case PVM.anew: // heap array allocation int size = mem[cpu.sp]; mem[cpu.sp] = cpu.hp; cpu.hp += size; break; case PVM.halt: // halt ps = finished; break; case PVM.stk: // stack dump (debugging) StackDump(results, pcNow); break; case PVM.heap: // heap dump (debugging) HeapDump(results, pcNow); break; case PVM.ldc_0: // push constant 0 mem[--cpu.sp] = 0; break; case PVM.ldc_1: // push constant 1 mem[--cpu.sp] = 1; break; case PVM.ldc_2: // push constant 2 mem[--cpu.sp] = 2; break; case PVM.ldc_3: // push constant 3 mem[--cpu.sp] = 3; break; case PVM.lda_0: // push local address 0 mem[--cpu.sp] = cpu.fp - 1 - 0; break; case PVM.lda_1: // push local address 1 mem[--cpu.sp] = cpu.fp - 1 - 1; break; case PVM.lda_2: // push local address 2 mem[--cpu.sp] = cpu.fp - 1 - 2; break; case PVM.lda_3: // push local address 3 mem[--cpu.sp] = cpu.fp - 1 - 3; break; case PVM.ldl: // push local value mem[--cpu.sp] = cpu.fp - 1 - mem[cpu.pc++]; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.ldl_0: // push value of local variable 0 mem[--cpu.sp] = cpu.fp - 1 - 0; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.ldl_1: // push value of local variable 1 mem[--cpu.sp] = cpu.fp - 1 - 1; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.ldl_2: // push value of local variable 2 mem[--cpu.sp] = cpu.fp - 1 - 2; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.ldl_3: // push value of local variable 3 mem[--cpu.sp] = cpu.fp - 1 - 3; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.stl: // store local value mem[--cpu.sp] = cpu.fp - 1 - mem[cpu.pc++]; mem[mem[cpu.sp++]] = mem[cpu.sp++]; break; case PVM.stlc: // store local value case PVM.stl_0: // pop to local variable 0 mem[--cpu.sp] = cpu.fp - 1 - 0; mem[mem[cpu.sp++]] = mem[cpu.sp++]; break; case PVM.stl_1: // pop to local variable 1 mem[--cpu.sp] = cpu.fp - 1 - 1; mem[mem[cpu.sp++]] = mem[cpu.sp++]; break; case PVM.stl_2: // pop to local variable 2 mem[--cpu.sp] = cpu.fp - 1 - 2; mem[mem[cpu.sp++]] = mem[cpu.sp++]; break; case PVM.stl_3: // pop to local variable 3 mem[--cpu.sp] = cpu.fp - 1 - 3; mem[mem[cpu.sp++]] = mem[cpu.sp++]; break; case PVM.stoc: // character checked store //case PVM.inpc: // character input // character output case PVM.cap: tos = mem[cpu.sp++]; if (96 < tos && tos < 123) { mem[--cpu.sp] = (tos - 32); } else { mem[--cpu.sp] = (tos); } break; // toUpperCase case PVM.low: tos = mem[cpu.sp++]; if (64 < tos && tos < 91) { mem[--cpu.sp] = (tos + 31); } else { ps = badOp; } break; // toLowerCase case PVM.islet: tos = mem[cpu.sp++]; if (64 < tos && tos < 91 || 96 < tos && tos < 123) { mem[--cpu.sp] = 1; } else { mem[--cpu.sp] = 0; } // isLetter break; case PVM.inc: //NOT DONE PROPERLY tos = mem[cpu.sp++]; // ++ //NOT DONE PROPERLY mem[--cpu.sp] = (tos += 1); //NOT DONE PROPERLY break; //NOT DONE PROPERLY case PVM.dec: //NOT DONE PROPERLY tos = mem[cpu.sp++]; // -- //NOT DONE PROPERLY mem[--cpu.sp] = (tos -= 1); //NOT DONE PROPERLY break; default: // unrecognized opcode ps = badOp; break; } } while (ps == running); TimeSpan ts = timer.Elapsed; // Format and display the TimeSpan value. string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("\n\n" + ops + " operations. Run Time " + elapsedTime + "\n\n"); if (ps != finished) { PostMortem(results, pcNow); } timer.Reset(); timer.Stop(); } // PVM.Emulator
} // PVM.PostMortem // The interpreters and utility methods public static void Emulator(int initPC, int codeLen, int initSP, InFile data, OutFile results, bool tracing, bool traceStack, bool traceHeap) { // Emulates action of the codeLen instructions stored in mem[0 .. codeLen-1], with // program counter initialized to initPC, stack pointer initialized to initSP. // data and results are used for I/O. Tracing at the code level may be requested int pcNow; // current program counter int loop; // internal loops int tos, sos; // value popped from stack int adr; stackBase = initSP; heapBase = codeLen; // initialize boundaries cpu.hp = heapBase; // initialize registers cpu.sp = stackBase; cpu.gp = stackBase; cpu.mp = stackBase; cpu.fp = stackBase; cpu.pc = initPC; // initialize program counter ps = running; // prepare to execute int ops = 0; timer.Start(); do { ops++; pcNow = cpu.pc; // retain for tracing/postmortem cpu.ir = mem[cpu.pc++]; // fetch // if (tracing) Trace(results, pcNow, traceStack, traceHeap); switch (cpu.ir) { // execute case PVM.nop: // no operation break; case PVM.dsp: // decrement stack pointer (allocate space for variables) cpu.sp -= mem[cpu.pc++]; break; case PVM.ldc: // push constant value mem[--cpu.sp] = mem[cpu.pc++]; break; case PVM.lda: // push local address mem[--cpu.sp] = cpu.fp - 1 - mem[cpu.pc++]; break; case PVM.ldv: // dereference mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.sto: // store tos = mem[cpu.sp++]; mem[mem[cpu.sp++]] = tos; break; case PVM.ldxa: // heap array indexing tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] + tos; break; case PVM.inpi: // integer input mem[mem[cpu.sp++]] = data.ReadInt(); break; case PVM.prni: // integer output // if (tracing) results.Write(padding); results.Write(mem[cpu.sp++], 0); // if (tracing) results.WriteLine(); break; case PVM.inpb: // boolean input mem[mem[cpu.sp++]] = data.ReadBool() ? 1 : 0; break; case PVM.prnb: // boolean output // if (tracing) results.Write(padding); if (mem[cpu.sp++] != 0) results.Write(" true "); else results.Write(" false "); // if (tracing) results.WriteLine(); break; case PVM.prns: // string output // if (tracing) results.Write(padding); loop = mem[cpu.pc++]; while (mem[loop] != 0) { results.Write((char)mem[loop]); loop--; } // if (tracing) results.WriteLine(); break; case PVM.prnl: // newline results.WriteLine(); break; case PVM.neg: // integer negation mem[cpu.sp] = -mem[cpu.sp]; break; case PVM.add: // integer addition tos = mem[cpu.sp++]; mem[cpu.sp] += tos; break; case PVM.sub: // integer subtraction tos = mem[cpu.sp++]; mem[cpu.sp] -= tos; break; case PVM.mul: // integer multiplication tos = mem[cpu.sp++]; int temp = mem[cpu.sp]; int answer = temp * tos; if (((tos > 0 && temp > 0) && answer < 0) || ((tos < 0 && temp < 0) && answer > 0) || ((tos > 0 || temp > 0) && answer < 0) || ((tos < 0 || temp < 0) && answer > 0) || answer > maxInt || answer < -maxInt) { PostMortem(results, pcNow); break; } mem[cpu.sp] *= tos; break; case PVM.div: // integer division (quotient) tos = mem[cpu.sp++]; if (tos == 0) { PostMortem(results, pcNow); break; } mem[cpu.sp] /= tos; break; case PVM.rem: // integer division (remainder) tos = mem[cpu.sp++]; mem[cpu.sp] %= tos; break; case PVM.not: // logical negation mem[cpu.sp] = mem[cpu.sp] == 0 ? 1 : 0; break; case PVM.and: // logical and tos = mem[cpu.sp++]; mem[cpu.sp] &= tos; break; case PVM.or: // logical or tos = mem[cpu.sp++]; mem[cpu.sp] |= tos; break; case PVM.ceq: // logical equality tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] == tos ? 1 : 0; break; case PVM.cne: // logical inequality tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] != tos ? 1 : 0; break; case PVM.clt: // logical less tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] < tos ? 1 : 0; break; case PVM.cle: // logical less or equal tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] <= tos ? 1 : 0; break; case PVM.cgt: // logical greater tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] > tos ? 1 : 0; break; case PVM.cge: // logical greater or equal tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] >= tos ? 1 : 0; break; case PVM.brn: // unconditional branch cpu.pc = mem[cpu.pc++]; break; case PVM.bze: // pop top of stack, branch if false int target = mem[cpu.pc++]; if (mem[cpu.sp++] == 0) cpu.pc = target; break; case PVM.anew: // heap array allocation int size = mem[cpu.sp]; mem[cpu.sp] = cpu.hp; cpu.hp += size; break; case PVM.halt: // halt ps = finished; break; case PVM.stk: // stack dump (debugging) StackDump(results, pcNow); break; case PVM.heap: // heap dump (debugging) HeapDump(results, pcNow); break; case PVM.ldc_0: // push constant 0 mem[--cpu.sp] = 0; break; case PVM.ldc_1: // push constant 1 mem[--cpu.sp] = 1; break; case PVM.ldc_2: // push constant 2 mem[--cpu.sp] = 2; break; case PVM.ldc_3: // push constant 3 mem[--cpu.sp] = 3; break; case PVM.lda_0: // push local address 0 mem[--cpu.sp] = cpu.fp - mem[cpu.pc++]; break; case PVM.lda_1: // push local address 1 mem[--cpu.sp] = cpu.fp -1 -mem[cpu.pc++]; break; case PVM.lda_2: // push local address 2 mem[--cpu.sp] = cpu.fp -2- mem[cpu.pc++]; break; case PVM.lda_3: // push local address 3 mem[--cpu.sp] = cpu.fp -3- mem[cpu.pc++]; break; case PVM.ldl_0: // push value of local variable 0 mem[--cpu.sp] = cpu.fp - mem[cpu.pc++]; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.ldl_1: // push value of local variable 1 mem[--cpu.sp] = cpu.fp -1- mem[cpu.pc++]; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.ldl_2: // push value of local variable 2 mem[--cpu.sp] = cpu.fp -2- mem[cpu.pc++]; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.ldl_3: // push value of local variable 3 mem[--cpu.sp] = cpu.fp -3- mem[cpu.pc++]; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.stl: // store local value // we have bug here with PushPop, test here shows: adr = cpu.fp - 1 - mem[cpu.pc++]; mem[cpu.sp++] = mem[adr]; break; case PVM.stl_0: // pop to local variable 0 mem[cpu.sp] = mem[mem[cpu.sp]]; mem[--cpu.sp] = cpu.fp - mem[cpu.pc++]; break; case PVM.stl_1: // pop to local variable 1 mem[cpu.sp] = mem[mem[cpu.sp]]; mem[--cpu.sp] = cpu.fp -1- mem[cpu.pc++]; break; case PVM.stl_2: // pop to local variable 2 mem[cpu.sp] = mem[mem[cpu.sp]]; mem[--cpu.sp] = cpu.fp -2- mem[cpu.pc++]; break; case PVM.stl_3: // pop to local variable 3 mem[cpu.sp] = mem[mem[cpu.sp]]; mem[--cpu.sp] = cpu.fp -3-mem[cpu.pc++]; break; case PVM.inpc: // character input var line = data.ReadLine(); char tempChar = string.IsNullOrEmpty(line) ? ' ' : line[0]; mem[cpu.sp++] = tempChar; if (data.Error()) ps = badData; break; case PVM.prnc: // character output if (tracing) results.WriteLine(padding); int readIn = mem[mem[cpu.sp++]]; results.WriteLine((char)readIn); if (tracing) results.WriteLine(); break; case PVM.cap: // toUpperCase int cUp = mem[cpu.sp++]; cUp = (int)char.ToUpper((char)cUp); mem[cpu.sp++] = cUp; break; case PVM.low: // toLowerCase int cLow = mem[cpu.sp++]; cLow = (int)char.ToLower((char)cLow); mem[cpu.sp++] = cLow; break; case PVM.islet: // isLetter int val = mem[mem[cpu.sp]]; results.WriteLine("Character readIn: " + ((char)val).ToString()); if (Char.IsLetter((char)val)) mem[cpu.sp] = 1; else mem[cpu.sp] = 0; break; case PVM.inc: // ++ int inc_a = mem[cpu.sp++] + 1; mem[cpu.sp++] = inc_a; break; case PVM.dec: // -- int dec_a = mem[cpu.sp++] - 1; mem[cpu.sp++] = dec_a; break; case PVM.ldl: // push local value mem[--cpu.sp] = mem[cpu.pc++]; var N = mem[cpu.sp]; mem[--cpu.sp] = cpu.fp - 1 - N; mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.stlc: // store local value tos = mem[cpu.sp++]; mem[mem[cpu.sp++]] = tos; break; case PVM.stoc: // character checked store default: // unrecognized opcode ps = badOp; break; } } while (ps == running); TimeSpan ts = timer.Elapsed; // Format and display the TimeSpan value. string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("\n\n" + ops + " operations. Run Time " + elapsedTime + "\n\n"); if (ps != finished) PostMortem(results, pcNow); timer.Reset(); timer.Stop(); } // PVM.Emulator
} // PVM.Interpret public static void ListCode(string fileName, int codeLen) { // Lists the codeLen instructions stored in mem on a named output file int i, j, n; if (fileName == null) { return; } OutFile codeFile = new OutFile(fileName); i = 0; codeFile.WriteLine("ASSEM\nBEGIN"); while (i < codeLen) { int o = mem[i] % (PVM.nul + 1); // force in range codeFile.Write(" {"); codeFile.Write(i, 5); codeFile.Write(" } "); codeFile.Write(mnemonics[o], -8); switch (o) { case PVM.brn: case PVM.bze: case PVM.dsp: case PVM.lda: case PVM.ldl: case PVM.stl: case PVM.ldc: i = (i + 1) % memSize; codeFile.Write(mem[i]); break; case PVM.prns: i = (i + 1) % memSize; j = mem[i]; codeFile.Write(" \""); while (mem[j] != 0) { switch (mem[j]) { case '\\': codeFile.Write("\\\\"); break; case '\"': codeFile.Write("\\\""); break; case '\'': codeFile.Write("\\\'"); break; case '\b': codeFile.Write("\\b"); break; case '\t': codeFile.Write("\\t"); break; case '\n': codeFile.Write("\\n"); break; case '\f': codeFile.Write("\\f"); break; case '\r': codeFile.Write("\\r"); break; default: codeFile.Write((char)mem[j]); break; } j--; } codeFile.Write("\""); break; } i = (i + 1) % memSize; codeFile.WriteLine(); } codeFile.WriteLine("END."); codeFile.Close(); } // PVM.ListCode
} // PVM.PostMortem // The interpreters and utility methods public static void Emulator(int initPC, int codeLen, int initSP, InFile data, OutFile results, bool tracing, bool traceStack, bool traceHeap) { // Emulates action of the codeLen instructions stored in mem[0 .. codeLen-1], with // program counter initialized to initPC, stack pointer initialized to initSP. // data and results are used for I/O. Tracing at the code level may be requested int pcNow; // current program counter int loop; // internal loops int tos, sos; // value popped from stack stackBase = initSP; heapBase = codeLen; // initialize boundaries cpu.hp = heapBase; // initialize registers cpu.sp = stackBase; cpu.gp = stackBase; cpu.mp = stackBase; cpu.fp = stackBase; cpu.pc = initPC; // initialize program counter ps = running; // prepare to execute int ops = 0; timer.Start(); do { ops++; pcNow = cpu.pc; // retain for tracing/postmortem cpu.ir = mem[cpu.pc++]; // fetch // if (tracing) Trace(results, pcNow, traceStack, traceHeap); switch (cpu.ir) // execute { case PVM.nop: // no operation break; case PVM.dsp: // decrement stack pointer (allocate space for variables) cpu.sp -= mem[cpu.pc++]; break; case PVM.ldc: // push constant value mem[--cpu.sp] = mem[cpu.pc++]; break; case PVM.lda: // push local address mem[--cpu.sp] = cpu.fp - 1 - mem[cpu.pc++]; break; case PVM.ldv: // dereference mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.sto: // store tos = mem[cpu.sp++]; mem[mem[cpu.sp++]] = tos; break; case PVM.ldxa: // heap array indexing tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] + tos; break; case PVM.inpi: // integer input mem[mem[cpu.sp++]] = data.ReadInt(); break; case PVM.prni: // integer output // if (tracing) results.Write(padding); results.Write(mem[cpu.sp++], 0); // if (tracing) results.WriteLine(); break; case PVM.inpb: // boolean input mem[mem[cpu.sp++]] = data.ReadBool() ? 1 : 0; break; case PVM.prnb: // boolean output // if (tracing) results.Write(padding); if (mem[cpu.sp++] != 0) { results.Write(" true "); } else { results.Write(" false "); } // if (tracing) results.WriteLine(); break; case PVM.prns: // string output // if (tracing) results.Write(padding); loop = mem[cpu.pc++]; while (mem[loop] != 0) { results.Write((char)mem[loop]); loop--; } // if (tracing) results.WriteLine(); break; case PVM.prnl: // newline results.WriteLine(); break; case PVM.neg: // integer negation mem[cpu.sp] = -mem[cpu.sp]; break; case PVM.add: // integer addition tos = mem[cpu.sp++]; mem[cpu.sp] += tos; break; case PVM.sub: // integer subtraction tos = mem[cpu.sp++]; mem[cpu.sp] -= tos; break; /* * case PVM.mul: // integer multiplication ORIGINAL * tos = mem[cpu.sp++]; * mem[cpu.sp] *= tos; * break; */ case PVM.mul: // integer multiplication UPDATED tos = mem[cpu.sp++]; //if multiplication laws are incorrect an overflow must have occured. //e.g. -? x -? = -? || ? x ? = -? !!!!!! if ((tos > 0 && mem[cpu.sp] > 0 && (mem[cpu.sp] * tos < 0)) || (tos < 0 && mem[cpu.sp] > 0 && (mem[cpu.sp] * tos > 0))) { ps = badVal; } else { mem[cpu.sp] *= tos; } break; /* * case PVM.div: // integer division (quotient) ORIGINAL * tos = mem[cpu.sp++]; * mem[cpu.sp] /= tos; * break; */ case PVM.div: // integer division (quotient) UPDATED tos = mem[cpu.sp++]; if (tos == 0) { ps = divZero; } else { mem[cpu.sp] /= tos; } break; case PVM.rem: // integer division (remainder) tos = mem[cpu.sp++]; mem[cpu.sp] %= tos; break; case PVM.not: // logical negation mem[cpu.sp] = mem[cpu.sp] == 0 ? 1 : 0; break; case PVM.and: // logical and tos = mem[cpu.sp++]; mem[cpu.sp] &= tos; break; case PVM.or: // logical or tos = mem[cpu.sp++]; mem[cpu.sp] |= tos; break; case PVM.ceq: // logical equality tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] == tos ? 1 : 0; break; case PVM.cne: // logical inequality tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] != tos ? 1 : 0; break; case PVM.clt: // logical less tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] < tos ? 1 : 0; break; case PVM.cle: // logical less or equal tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] <= tos ? 1 : 0; break; case PVM.cgt: // logical greater tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] > tos ? 1 : 0; break; case PVM.cge: // logical greater or equal tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] >= tos ? 1 : 0; break; case PVM.brn: // unconditional branch cpu.pc = mem[cpu.pc++]; break; case PVM.bze: // pop top of stack, branch if false int target = mem[cpu.pc++]; if (mem[cpu.sp++] == 0) { cpu.pc = target; } break; /* * case PVM.anew: // heap array allocation ORIGINAL * int size = mem[cpu.sp]; * mem[cpu.sp] = cpu.hp; * cpu.hp += size; * break; */ case PVM.anew: // heap array allocation UPDATED: as per textbook on page 51 int size = mem[cpu.sp]; if (size <= 0 || size + 1 > cpu.sp - cpu.hp) { ps = badAll; } else { mem[cpu.hp] = size; mem[cpu.sp] = cpu.hp; cpu.hp += size + 1; } break; case PVM.halt: // halt ps = finished; break; case PVM.stk: // stack dump (debugging) StackDump(results, pcNow); break; case PVM.heap: // heap dump (debugging) HeapDump(results, pcNow); break; case PVM.ldc_0: // push constant 0 case PVM.ldc_1: // push constant 1 case PVM.ldc_2: // push constant 2 case PVM.ldc_3: // push constant 3 case PVM.lda_0: // push local address 0 case PVM.lda_1: // push local address 1 case PVM.lda_2: // push local address 2 case PVM.lda_3: // push local address 3 case PVM.ldl: // push local value case PVM.ldl_0: // push value of local variable 0 case PVM.ldl_1: // push value of local variable 1 case PVM.ldl_2: // push value of local variable 2 case PVM.ldl_3: // push value of local variable 3 case PVM.stl: // store local value case PVM.stlc: // store local value case PVM.stl_0: // pop to local variable 0 case PVM.stl_1: // pop to local variable 1 case PVM.stl_2: // pop to local variable 2 case PVM.stl_3: // pop to local variable 3 case PVM.stoc: // character checked store case PVM.inpc: // character input case PVM.prnc: // character output case PVM.cap: // toUpperCase ADDED mem[cpu.sp] = Char.ToUpper((char)mem[cpu.sp]); break; case PVM.low: // toLowerCase case PVM.islet: // isLetter case PVM.inc: // ++ ADDED mem[mem[cpu.sp++]]++; break; case PVM.dec: // -- ADDED mem[mem[cpu.sp++]]--; break; default: // unrecognized opcode ps = badOp; break; } } while (ps == running); TimeSpan ts = timer.Elapsed; // Format and display the TimeSpan value. string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("\n\n" + ops + " operations. Run Time " + elapsedTime + "\n\n"); if (ps != finished) { PostMortem(results, pcNow); } timer.Reset(); timer.Stop(); } // PVM.Emulator
} // PVM.InBounds public static void Emulator(int initPC, int codeLen, int initSP, InFile data, OutFile results, bool tracing, bool traceStack, bool traceHeap) { // Emulates action of the codeLen instructions stored in mem[0 .. codeLen-1], with // program counter initialized to initPC, stack pointer initialized to initSP. // data and results are used for I/O. Tracing at the code level may be requested int pcNow; // current program counter int loop; // internal loops int tos, sos; // values popped from stack int adr; // effective address for memory accesses int target; // destination for int store = 0; // saves the next location of int store2 = 0; // compares stackBase = initSP; heapBase = codeLen; // initialize boundaries cpu.hp = heapBase; // initialize registers cpu.sp = stackBase; cpu.gp = stackBase; cpu.mp = stackBase; cpu.fp = stackBase; cpu.pc = initPC; // initialize program counter for (int i = heapBase; i < stackBase; i++) { mem[i] = 0; // set entire memory to null or 0 } ps = running; // prepare to execute int ops = 0; timer.Start(); do { ops++; pcNow = cpu.pc; // retain for tracing/postmortem if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; break; } cpu.ir = Next(); // fetch if (tracing) { Trace(results, pcNow, traceStack, traceHeap); } switch (cpu.ir) // execute { case PVM.nop: // no operation break; case PVM.dsp: // decrement stack pointer (allocate space for variables) int localSpace = Next(); cpu.sp -= localSpace; if (InBounds(cpu.sp)) // initialize all local variables to zero/null { for (loop = 0; loop < localSpace; loop++) { mem[cpu.sp + loop] = 0; } } break; case PVM.ldc: // push constant value Push(Next()); break; case PVM.ldc_m1: // push constant -1 Push(-1); break; case PVM.ldc_0: // push constant 0 Push(0); break; case PVM.ldc_1: // push constant 1 Push(1); break; case PVM.ldc_2: // push constant 2 Push(2); break; case PVM.ldc_3: // push constant 3 Push(3); break; case PVM.ldc_4: // push constant 4 Push(4); break; case PVM.ldc_5: // push constant 5 Push(5); break; case PVM.lda: // push local address adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { Push(adr); } break; case PVM.lda_0: // push local address 0 adr = cpu.fp - 1; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_1: // push local address 1 adr = cpu.fp - 2; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_2: // push local address 2 adr = cpu.fp - 3; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_3: // push local address 3 adr = cpu.fp - 4; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_4: // push local address 4 adr = cpu.fp - 5; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_5: // push local address 5 adr = cpu.fp - 6; if (InBounds(adr)) { Push(adr); } break; case PVM.ldga: // push global address adr = cpu.gp - 1 - Next(); if (InBounds(adr)) { Push(adr); } break; case PVM.ldg_0: // push local address 0 adr = cpu.gp - 1; if (InBounds(adr)) { Push(adr); } break; case PVM.ldg_1: // push local address 1 adr = cpu.gp - 2; if (InBounds(adr)) { Push(adr); } break; case PVM.ldg_2: // push local address 2 adr = cpu.gp - 3; if (InBounds(adr)) { Push(adr); } break; case PVM.ldg_3: // push local address 3 adr = cpu.gp - 4; if (InBounds(adr)) { Push(adr); } break; case PVM.ldg_4: // push local address 4 adr = cpu.gp - 5; if (InBounds(adr)) { Push(adr); } break; case PVM.ldg_5: // push local address 5 adr = cpu.gp - 6; if (InBounds(adr)) { Push(adr); } break; case PVM.ldl: // push local value adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_0: // push value of local variable 0 adr = cpu.fp - 1; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_1: // push value of local variable 1 adr = cpu.fp - 2; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_2: // push value of local variable 2 adr = cpu.fp - 3; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_3: // push value of local variable 3 adr = cpu.fp - 4; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_4: // push value of local variable 4 adr = cpu.fp - 5; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_5: // push value of local variable 5 adr = cpu.fp - 6; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.stl: // store local value adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stlc: // character checked pop to local variable tos = Pop(); adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { if (tos >= 0 && tos <= maxChar) { mem[adr] = tos; } else { ps = badVal; } } break; case PVM.stl_0: // pop to local variable 0 adr = cpu.fp - 1; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_1: // pop to local variable 1 adr = cpu.fp - 2; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_2: // pop to local variable 2 adr = cpu.fp - 3; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_3: // pop to local variable 3 adr = cpu.fp - 4; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_4: // pop to local variable 4 adr = cpu.fp - 5; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_5: // pop to local variable 5 adr = cpu.fp - 6; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.ldv: // dereference adr = Pop(); if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.sto: // store tos = Pop(); adr = Pop(); if (InBounds(adr)) { mem[adr] = tos; } break; case PVM.stoc: // character checked store tos = Pop(); adr = Pop(); if (InBounds(adr)) { if (tos >= 0 && tos <= maxChar) { mem[adr] = tos; } else { ps = badVal; } } break; case PVM.ldxa: // heap array indexing adr = Pop(); int heapPtr = Pop(); if (heapPtr == 0) { ps = nullRef; } else if (heapPtr < heapBase || heapPtr >= cpu.hp) { ps = badMem; } else if (adr < 0 || adr >= mem[heapPtr]) { ps = badInd; } else { Push(heapPtr + adr + 1); } break; case PVM.inpi: // integer input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadInt(); if (data.Error()) { ps = badData; } } break; case PVM.inpb: // boolean input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadBool() ? 1 : 0; if (data.Error()) { ps = badData; } } break; case PVM.inpc: // character input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadChar(); if (data.Error()) { ps = badData; } } break; case PVM.inpl: // skip to end of input line data.ReadLine(); break; case PVM.i2c: // check convert character to integer if (mem[cpu.sp] < 0 || mem[cpu.sp] > maxChar) { ps = badVal; } else { Push((int)Pop()); } break; case PVM.c2i: // check convert integer to adr = Pop(); if ((adr >= 65 && adr <= 90) || (adr >= 97 && adr <= 122)) { Push((char)adr); } else { ps = badCast; } break; case PVM.prni: // integer output if (tracing) { results.Write(padding); } results.Write(Pop(), 0); if (tracing) { results.WriteLine(); } break; case PVM.prnb: // boolean output if (tracing) { results.Write(padding); } if (Pop() != 0) { results.Write(" true "); } else { results.Write(" false "); } if (tracing) { results.WriteLine(); } break; case PVM.prnc: // character output if (tracing) { results.Write(padding); } results.Write((char)(Math.Abs(Pop()) % (maxChar + 1)), 1); if (tracing) { results.WriteLine(); } break; case PVM.prns: // string output if (tracing) { results.Write(padding); } loop = Next(); while (ps == running && mem[loop] != 0) { results.Write((char)mem[loop]); loop--; if (loop < stackBase) { ps = badMem; } } if (tracing) { results.WriteLine(); } break; case PVM.prnl: // newline results.WriteLine(); break; case PVM.neg: // integer negation Push(-Pop()); break; case PVM.add: // integer addition tos = Pop(); Push(Pop() + tos); break; case PVM.sub: // integer subtraction tos = Pop(); Push(Pop() - tos); break; case PVM.mul: // integer multiplication tos = Pop(); sos = Pop(); if (tos != 0 && Math.Abs(sos) > maxInt / Math.Abs(tos)) { ps = badVal; } else { Push(sos * tos); } break; case PVM.div: // integer division (quotient) tos = Pop(); if (tos == 0) { ps = divZero; } else { Push(Pop() / tos); } break; case PVM.rem: // integer division (remainder) tos = Pop(); if (tos == 0) { ps = divZero; } else { Push(Pop() % tos); } break; case PVM.not: // logical negation Push(Pop() == 0 ? 1 : 0); break; case PVM.and: // logical and tos = Pop(); Push(Pop() & tos); break; case PVM.or: // logical or tos = Pop(); Push(Pop() | tos); break; case PVM.ceq: // logical equality tos = Pop(); Push(Pop() == tos ? 1 : 0); break; case PVM.cne: // logical inequality tos = Pop(); Push(Pop() != tos ? 1 : 0); break; case PVM.clt: // logical less tos = Pop(); Push(Pop() < tos ? 1 : 0); break; case PVM.cle: // logical less or equal tos = Pop(); Push(Pop() <= tos ? 1 : 0); break; case PVM.cgt: // logical greater tos = Pop(); Push(Pop() > tos ? 1 : 0); break; case PVM.cge: // logical greater or equal tos = Pop(); Push(Pop() >= tos ? 1 : 0); break; case PVM.brn: // unconditional branch store = cpu.pc + 1; store2 = Next(); cpu.pc = store2; if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; } break; case PVM.bze: // pop top of stack, branch if false target = Next(); if (Pop() == 0) { cpu.pc = target; if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; } } break; case PVM.pjp: // jumps to the location of given parameter if (store == store2) { cpu.pc++; } else { cpu.pc = store; } if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; } break; case PVM.bfalse: // conditional short circuit "and" branch target = Next(); if (mem[cpu.sp] == 0) { cpu.pc = target; } else { cpu.sp++; } break; case PVM.btrue: // conditional short circuit "or" branch target = Next(); if (mem[cpu.sp] == 1) { cpu.pc = target; } else { cpu.sp++; } break; case PVM.anew: // heap array allocation int size = Pop(); if (size <= 0 || size + 1 > cpu.sp - cpu.hp - 2) { ps = badAll; } else { mem[cpu.hp] = size; // first element stores size for bounds checking Push(cpu.hp); cpu.hp += size + 1; // bump heap pointer // elements are already initialized to 0 / null (why?) } break; case PVM.halt: // halt ps = finished; break; case PVM.inc: // integer ++ adr = Pop(); if (InBounds(adr)) { mem[adr]++; } break; case PVM.dec: // integer -- adr = Pop(); if (InBounds(adr)) { mem[adr]--; } break; case PVM.incc: // ++ characters adr = Pop(); if (InBounds(adr)) { if (mem[adr] < maxChar) { mem[adr]++; } else { ps = badVal; } } break; case PVM.decc: // -- characters adr = Pop(); if (mem[adr] > 0) { mem[adr]--; } else { ps = badVal; } break; case PVM.stack: // stack dump (debugging) StackDump(results, pcNow); break; case PVM.heap: // heap dump (debugging) HeapDump(results, pcNow); break; case PVM.fhdr: // allocate frame header if (InBounds(cpu.sp - headerSize)) { mem[cpu.sp - headerSize] = cpu.mp; cpu.mp = cpu.sp; cpu.sp -= headerSize; } break; case PVM.call: // call function if (mem[cpu.pc] < 0) { ps = badAdr; } else { mem[cpu.mp - 2] = cpu.fp; mem[cpu.mp - 3] = cpu.pc + 1; cpu.fp = cpu.mp; cpu.pc = mem[cpu.pc]; } break; case PVM.retv: // return from void function cpu.sp = cpu.fp; cpu.mp = mem[cpu.fp - 4]; cpu.pc = mem[cpu.fp - 3]; cpu.fp = mem[cpu.fp - 2]; break; case PVM.ret: // return from non-void function cpu.sp = cpu.fp - 1; cpu.mp = mem[cpu.fp - 4]; cpu.pc = mem[cpu.fp - 3]; cpu.fp = mem[cpu.fp - 2]; break; case PVM.trap: // trap falling off end of function ps = badFun; break; case PVM.low: // toLowerCase Push(Char.ToLower((char)Pop())); break; case PVM.cap: // toUpperCase Push(Char.ToUpper((char)Pop())); break; case PVM.islet: // isLetter tos = Pop(); Push(Char.IsLetter((char)tos) ? 1 : 0); break; case PVM.pop: // pop and discard tos tos = Pop(); break; case PVM.dup: // duplicate tos tos = Pop(); Push(tos); Push(tos); break; default: // unrecognized opcode ps = badOp; break; } } while (ps == running); TimeSpan ts = timer.Elapsed; // Format and display the TimeSpan value. string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("\n\n" + ops + " operations. Run Time " + elapsedTime + "\n\n"); if (ps != finished) { PostMortem(results, pcNow); } timer.Reset(); timer.Stop(); } // PVM.Emulator
} // PVM.PostMortem // The interpreters and utility methods public static void Emulator(int initPC, int codeLen, int initSP, InFile data, OutFile results, bool tracing) { // Emulates action of the codeLen instructions stored in mem[0 .. codeLen-1], with // program counter initialized to initPC, stack pointer initialized to initSP. // data and results are used for I/O. Tracing at the code level may be requested int pcNow; // current program counter int loop; // internal loops int tos, sos; // value popped from stack stackBase = initSP; heapBase = codeLen; // initialize boundaries cpu.hp = heapBase; // initialize registers cpu.sp = stackBase; cpu.gp = stackBase; cpu.mp = stackBase; cpu.fp = stackBase; cpu.pc = initPC; // initialize program counter ps = running; // prepare to execute int ops = 0; timer.Start(); do { ops++; pcNow = cpu.pc; // retain for tracing/postmortem cpu.ir = mem[cpu.pc++]; // fetch // if (tracing) Trace(results, pcNow); switch (cpu.ir) // execute { case PVM.nop: // no operation break; case PVM.dsp: // decrement stack pointer (allocate space for variables) cpu.sp -= mem[cpu.pc++]; break; case PVM.ldc: // push constant value mem[--cpu.sp] = mem[cpu.pc++]; break; case PVM.lda: // push local address mem[--cpu.sp] = cpu.fp - 1 - mem[cpu.pc++]; break; case PVM.ldv: // dereference mem[cpu.sp] = mem[mem[cpu.sp]]; break; case PVM.sto: // store tos = mem[cpu.sp++]; mem[mem[cpu.sp++]] = tos; break; case PVM.ldxa: // heap array indexing int adr = mem[cpu.sp++]; int heapPtr = mem[cpu.sp]; if (heapPtr == 0) { ps = nullRef; } else if (heapPtr < heapBase || heapPtr >= cpu.hp) { ps = badMem; } else if (adr < 0 || adr >= mem[heapPtr]) { ps = badInd; } else { mem[cpu.sp] = heapPtr + adr + 1; } break; case PVM.inpi: // integer input mem[mem[cpu.sp++]] = data.ReadInt(); break; case PVM.prni: // integer output // if (tracing) results.Write(padding); results.Write(mem[cpu.sp++], 0); // if (tracing) results.WriteLine(); break; case PVM.inpb: // boolean input mem[mem[cpu.sp++]] = data.ReadBool() ? 1 : 0; break; case PVM.prnb: // boolean output // if (tracing) results.Write(padding); if (mem[cpu.sp++] != 0) { results.Write(" true "); } else { results.Write(" false "); } // if (tracing) results.WriteLine(); break; case PVM.prns: // string output // if (tracing) results.Write(padding); loop = mem[cpu.pc++]; while (mem[loop] != 0) { results.Write((char)mem[loop]); loop--; } // if (tracing) results.WriteLine(); break; case PVM.prnl: // newline results.WriteLine(); break; case PVM.neg: // integer negation mem[cpu.sp] = -mem[cpu.sp]; break; case PVM.add: // integer addition tos = mem[cpu.sp++]; mem[cpu.sp] += tos; break; case PVM.sub: // integer subtraction tos = mem[cpu.sp++]; mem[cpu.sp] -= tos; break; case PVM.mul: // integer multiplication tos = mem[cpu.sp++]; if (tos != 0 && Math.Abs(mem[cpu.sp]) > maxInt / Math.Abs(tos)) { ps = badVal; } else { mem[cpu.sp] *= tos; } break; case PVM.div: // integer division (quotient) tos = mem[cpu.sp++]; if (tos != 0) { mem[cpu.sp] /= tos; } else { ps = divZero; } break; case PVM.rem: // integer division (remainder) tos = mem[cpu.sp++]; if (tos != 0) { mem[cpu.sp] %= tos; } else { ps = divZero; } break; case PVM.not: // logical negation mem[cpu.sp] = mem[cpu.sp] == 0 ? 1 : 0; break; case PVM.and: // logical and tos = mem[cpu.sp++]; mem[cpu.sp] &= tos; break; case PVM.or: // logical or tos = mem[cpu.sp++]; mem[cpu.sp] |= tos; break; case PVM.ceq: // logical equality tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] == tos ? 1 : 0; break; case PVM.cne: // logical inequality tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] != tos ? 1 : 0; break; case PVM.clt: // logical less tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] < tos ? 1 : 0; break; case PVM.cle: // logical less or equal tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] <= tos ? 1 : 0; break; case PVM.cgt: // logical greater tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] > tos ? 1 : 0; break; case PVM.cge: // logical greater or equal tos = mem[cpu.sp++]; mem[cpu.sp] = mem[cpu.sp] >= tos ? 1 : 0; break; case PVM.brn: // unconditional branch cpu.pc = mem[cpu.pc++]; break; case PVM.bze: // pop top of stack, branch if false int target = mem[cpu.pc++]; if (mem[cpu.sp++] == 0) { cpu.pc = target; } break; case PVM.anew: // heap array allocation int size = mem[cpu.sp]; if (size <= 0 || size + 1 > cpu.sp - cpu.hp - 2) { ps = badAll; } else { mem[cpu.hp] = size; mem[cpu.sp] = cpu.hp; cpu.hp += size + 1; } break; case PVM.halt: // halt ps = finished; break; case PVM.stk: // stack dump (debugging) StackDump(results, pcNow); break; case PVM.heap: // heap dump (debugging) HeapDump(results, pcNow); break; case PVM.ldc_0: // push constant 0 mem[--cpu.sp] = 0; break; case PVM.ldc_1: // push constant 1 mem[--cpu.sp] = 1; break; case PVM.ldc_2: // push constant 2 mem[--cpu.sp] = 2; break; case PVM.ldc_3: // push constant 3 mem[--cpu.sp] = 3; break; case PVM.lda_0: // push local address 0 mem[--cpu.sp] = cpu.fp - 1; break; case PVM.lda_1: // push local address 1 mem[--cpu.sp] = cpu.fp - 2; break; case PVM.lda_2: // push local address 2 mem[--cpu.sp] = cpu.fp - 3; break; case PVM.lda_3: // push local address 3 mem[--cpu.sp] = cpu.fp - 4; break; case PVM.ldl: // push local value mem[--cpu.sp] = mem[cpu.fp - 1 - mem[cpu.pc++]]; break; case PVM.ldl_0: // push value of local variable 0 mem[--cpu.sp] = mem[cpu.fp - 1]; break; case PVM.ldl_1: // push value of local variable 1 mem[--cpu.sp] = mem[cpu.fp - 2]; break; case PVM.ldl_2: // push value of local variable 2 mem[--cpu.sp] = mem[cpu.fp - 3]; break; case PVM.ldl_3: // push value of local variable 3 mem[--cpu.sp] = mem[cpu.fp - 4]; break; case PVM.stl: // store local value mem[cpu.fp - 1 - mem[cpu.pc++]] = mem[cpu.sp++]; break; case PVM.stlc: // store local character value mem[cpu.fp - 1 - mem[cpu.pc++]] = mem[cpu.sp++]; break; case PVM.stl_0: // pop to local variable 0 mem[cpu.fp - 1] = mem[cpu.sp++]; break; case PVM.stl_1: // pop to local variable 1 mem[cpu.fp - 2] = mem[cpu.sp++]; break; case PVM.stl_2: // pop to local variable 2 mem[cpu.fp - 3] = mem[cpu.sp++]; break; case PVM.stl_3: // pop to local variable 3 mem[cpu.fp - 4] = mem[cpu.sp++]; break; case PVM.i2c: // check convert character to integer - unchecked break; case PVM.stoc: // character checked store tos = mem[cpu.sp++]; mem[mem[cpu.sp++]] = tos; break; case PVM.inpc: // character input mem[mem[cpu.sp++]] = data.ReadChar(); break; case PVM.prnc: // character output // if (tracing) results.Write(padding); results.Write((char)(Math.Abs(mem[cpu.sp++]) % (maxChar + 1)), 1); // if (tracing) results.WriteLine(); break; case PVM.low: // toLowerCase mem[cpu.sp] = Char.ToLower((char)mem[cpu.sp]); break; case PVM.islet: // isLetter mem[cpu.sp] = Char.IsLetter((char)mem[cpu.sp]) ? 1 : 0; break; case PVM.inc: // ++ mem[mem[cpu.sp++]]++; break; case PVM.dec: // -- mem[mem[cpu.sp++]]--; break; default: // unrecognized opcode ps = badOp; break; } } while (ps == running); TimeSpan ts = timer.Elapsed; string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("\n\n" + ops + " operations. Run Time " + elapsedTime + "\n\n"); if (ps != finished) { PostMortem(results, pcNow); } timer.Reset(); timer.Stop(); } // PVM.Emulator
} // PVM.HeapDump static void Trace(OutFile results, int pcNow) { // Simple trace facility for run time debugging results.Write(" PC:"); results.Write(pcNow, 5); results.Write(" FP:"); results.Write(cpu.fp, 5); results.Write(" SP:"); results.Write(cpu.sp, 5); results.Write(" HP:"); results.Write(cpu.hp, 5); results.Write(" TOS:"); if (cpu.sp < memSize) { results.Write(mem[cpu.sp], 5); } else { results.Write(" ????"); } results.Write(" " + mnemonics[cpu.ir], -8); switch (cpu.ir) { case PVM.brn: case PVM.bze: case PVM.dsp: case PVM.lda: case PVM.ldc: case PVM.ldl: //++ case PVM.stl: //++ case PVM.stlc: //++ case PVM.prns: results.Write(mem[cpu.pc], 7); break; default: break; } results.WriteLine(); } // PVM.Trace
} // PVM.Trace static void PostMortem(OutFile results, int pcNow) { // Reports run time error and position results.WriteLine(); switch (ps) { case badMem: results.Write("Memory violation"); break; case badData: results.Write("Invalid data"); break; case noData: results.Write("No more data"); break; case divZero: results.Write("Division by zero"); break; case badOp: results.Write("Illegal opcode"); break; case badInd: results.Write("Subscript out of range"); break; case badVal: results.Write("Value out of range"); break; case badAdr: results.Write("Bad address"); break; case badAll: results.Write("Heap allocation error"); break; case nullRef: results.Write("Null reference"); break; default: results.Write("Interpreter error!"); break; } results.WriteLine(" at " + pcNow); } // PVM.PostMortem
} // PVM.InBounds public static void Emulator(int initPC, int codeLen, int initSP, InFile data, OutFile results, bool tracing, bool traceStack, bool traceHeap) { // Emulates action of the codeLen instructions stored in mem[0 .. codeLen-1], with // program counter initialized to initPC, stack pointer initialized to initSP. // data and results are used for I/O. Tracing at the code level may be requested int pcNow; // current program counter int loop; // internal loops int tos, sos; // values popped from stack int adr; // effective address for memory accesses int target; // destination for branches stackBase = initSP; heapBase = codeLen; // initialize boundaries cpu.hp = heapBase; // initialize registers cpu.sp = stackBase; cpu.gp = stackBase; cpu.mp = stackBase; cpu.fp = stackBase; cpu.pc = initPC; // initialize program counter for (int i = heapBase; i < stackBase; i++) { mem[i] = 0; // set entire memory to null or 0 } ps = running; // prepare to execute int ops = 0; timer.Start(); do { ops++; pcNow = cpu.pc; // retain for tracing/postmortem if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; break; } cpu.ir = Next(); // fetch if (tracing) { Trace(results, pcNow, traceStack, traceHeap); } switch (cpu.ir) // execute { case PVM.nop: // no operation break; case PVM.dsp: // decrement stack pointer (allocate space for variables) int localSpace = Next(); cpu.sp -= localSpace; if (InBounds(cpu.sp)) // initialize all local variables to zero/null { for (loop = 0; loop < localSpace; loop++) { mem[cpu.sp + loop] = 0; } } break; case PVM.ldc: // push constant value Push(Next()); break; case PVM.ldc_m1: // push constant -1 Push(-1); break; case PVM.ldc_0: // push constant 0 Push(0); break; case PVM.ldc_1: // push constant 1 Push(1); break; case PVM.ldc_2: // push constant 2 Push(2); break; case PVM.ldc_3: // push constant 3 Push(3); break; case PVM.ldc_4: // push constant 4 Push(4); break; case PVM.ldc_5: // push constant 5 Push(5); break; case PVM.dup: // duplicate top of stack tos = Pop(); Push(tos); Push(tos); break; case PVM.rdup: // Remove top of stack tos = Pop(); break; case PVM.lda: // push local address adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { Push(adr); } break; case PVM.lda_0: // push local address 0 adr = cpu.fp - 1; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_1: // push local address 1 adr = cpu.fp - 2; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_2: // push local address 2 adr = cpu.fp - 3; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_3: // push local address 3 adr = cpu.fp - 4; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_4: // push local address 4 adr = cpu.fp - 5; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_5: // push local address 5 adr = cpu.fp - 6; if (InBounds(adr)) { Push(adr); } break; case PVM.ldl: // push local value adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_0: // push value of local variable 0 adr = cpu.fp - 1; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_1: // push value of local variable 1 adr = cpu.fp - 2; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_2: // push value of local variable 2 adr = cpu.fp - 3; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_3: // push value of local variable 3 adr = cpu.fp - 4; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_4: // push value of local variable 4 adr = cpu.fp - 5; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_5: // push value of local variable 5 adr = cpu.fp - 6; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.stl: // store local value adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stlc: // character checked pop to local variable tos = Pop(); adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { if (tos >= 0 && tos <= maxChar) { mem[adr] = tos; } else { ps = badVal; } } break; case PVM.stl_0: // pop to local variable 0 adr = cpu.fp - 1; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_1: // pop to local variable 1 adr = cpu.fp - 2; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_2: // pop to local variable 2 adr = cpu.fp - 3; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_3: // pop to local variable 3 adr = cpu.fp - 4; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_4: // pop to local variable 4 adr = cpu.fp - 5; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_5: // pop to local variable 5 adr = cpu.fp - 6; if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.ldv: // dereference adr = Pop(); if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.sto: // store tos = Pop(); adr = Pop(); if (InBounds(adr)) { mem[adr] = tos; } break; case PVM.stoc: // character checked store tos = Pop(); adr = Pop(); if (InBounds(adr)) { if (tos >= 0 && tos <= maxChar) { mem[adr] = tos; } else { ps = badVal; } } break; case PVM.ldxa: // heap array indexing adr = Pop(); int heapPtr = Pop(); if (heapPtr == 0) { ps = nullRef; } else if (heapPtr < heapBase || heapPtr >= cpu.hp) { ps = badMem; } else if (adr < 0 || adr >= mem[heapPtr]) { ps = badInd; } else { Push(heapPtr + adr + 1); } break; case PVM.inpi: // integer input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadInt(); if (data.Error()) { ps = badData; } } break; case PVM.inpb: // boolean input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadBool() ? 1 : 0; if (data.Error()) { ps = badData; } } break; case PVM.inpc: // character input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadChar(); if (data.Error()) { ps = badData; } } break; case PVM.inpl: // skip to end of input line data.ReadLine(); break; case PVM.i2c: // check (char) cast is in range if (mem[cpu.sp] < 0 || mem[cpu.sp] > maxChar) { ps = badVal; } break; case PVM.prni: // integer output if (tracing) { results.Write(padding); } results.Write(Pop(), 0); if (tracing) { results.WriteLine(); } break; case PVM.prnb: // boolean output if (tracing) { results.Write(padding); } if (Pop() != 0) { results.Write(" true "); } else { results.Write(" false "); } if (tracing) { results.WriteLine(); } break; case PVM.prnc: // character output if (tracing) { results.Write(padding); } results.Write((char)(Math.Abs(Pop()) % (maxChar + 1)), 1); if (tracing) { results.WriteLine(); } break; case PVM.prns: // string output if (tracing) { results.Write(padding); } loop = Next(); while (ps == running && mem[loop] != 0) { results.Write((char)mem[loop]); loop--; if (loop < stackBase) { ps = badMem; } } if (tracing) { results.WriteLine(); } break; case PVM.fprns: // string output if (tracing) { results.Write(padding); } loop = Next(); StringBuilder str = new StringBuilder(); while (ps == running && mem[loop] != 0) { //results.Write((char) mem[loop]); loop--; str.Append((char)mem[loop]); loop--; if (loop < stackBase) { ps = badMem; } } results.Write(str.ToString(), Pop()); if (tracing) { results.WriteLine(); } break; case PVM.prnl: // newline results.WriteLine(); break; case PVM.fprint: tos = Pop(); sos = Pop(); results.Write(sos, tos); break; case PVM.neg: // integer negation Push(-Pop()); break; case PVM.add: // integer addition tos = Pop(); Push(Pop() + tos); break; case PVM.sub: // integer subtraction tos = Pop(); Push(Pop() - tos); break; case PVM.mul: // integer multiplication tos = Pop(); sos = Pop(); if (tos != 0 && Math.Abs(sos) > maxInt / Math.Abs(tos)) { ps = badVal; } else { Push(sos * tos); } break; case PVM.div: // integer division (quotient) tos = Pop(); if (tos == 0) { ps = divZero; } else { Push(Pop() / tos); } break; case PVM.max: // Finds max. First Pops elements off stack // to see how many elements. int numElements = Pop(); if (numElements == 0) { ps = noData; } else { List <int> maxElements = new List <int> (); for (int i = 0; i < numElements; i++) { maxElements.Add(Pop()); } maxElements.Sort(); maxElements.Reverse(); Push(maxElements[0]); } break; case PVM.min: // Finds min. First Pops elements off stack // to see how many elements int numMin = Pop(); if (numMin == 0) { ps = noData; } else { List <int> minElements = new List <int> (); for (int i = 0; i < numMin; i++) { minElements.Add(Pop()); } minElements.Sort(); Push(minElements[0]); } break; case PVM.sqrt: // Square root tos = Pop(); Push(Convert.ToInt32(System.Math.Sqrt(tos))); break; case PVM.rem: // integer division (remainder) tos = Pop(); if (tos == 0) { ps = divZero; } else { Push(Pop() % tos); } break; case PVM.not: // logical negation Push(Pop() == 0 ? 1 : 0); break; case PVM.and: // logical and tos = Pop(); Push(Pop() & tos); break; case PVM.or: // logical or tos = Pop(); Push(Pop() | tos); break; case PVM.ceq: // logical equality tos = Pop(); Push(Pop() == tos ? 1 : 0); break; case PVM.cne: // logical inequality tos = Pop(); Push(Pop() != tos ? 1 : 0); break; case PVM.clt: // logical less tos = Pop(); Push(Pop() < tos ? 1 : 0); break; case PVM.cle: // logical less or equal tos = Pop(); Push(Pop() <= tos ? 1 : 0); break; case PVM.cgt: // logical greater tos = Pop(); Push(Pop() > tos ? 1 : 0); break; case PVM.cge: // logical greater or equal tos = Pop(); Push(Pop() >= tos ? 1 : 0); break; case PVM.brn: // unconditional branch cpu.pc = Next(); if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; } break; case PVM.bze: // pop top of stack, branch if false target = Next(); if (Pop() == 0) { cpu.pc = target; if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; } } break; case PVM.bfalse: // conditional short circuit "and" branch target = Next(); if (mem[cpu.sp] == 0) { cpu.pc = target; } else { cpu.sp++; } break; case PVM.btrue: // conditional short circuit "or" branch target = Next(); if (mem[cpu.sp] == 1) { cpu.pc = target; } else { cpu.sp++; } break; case PVM.anew: // heap array allocation int size = Pop(); if (size <= 0 || size + 1 > cpu.sp - cpu.hp - 2) { ps = badAll; } else { mem[cpu.hp] = size; // first element stores size for bounds checking Push(cpu.hp); cpu.hp += size + 1; // bump heap pointer // elements are already initialized to 0 / null (why?) } break; case PVM.halt: // halt ps = finished; break; case PVM.inc: // integer ++ adr = Pop(); if (InBounds(adr)) { mem[adr]++; } break; case PVM.dec: // integer -- adr = Pop(); if (InBounds(adr)) { mem[adr]--; } break; case PVM.incc: // ++ characters adr = Pop(); if (InBounds(adr)) { if (mem[adr] < maxChar) { mem[adr]++; } else { ps = badVal; } } break; case PVM.decc: // -- characters adr = Pop(); if (InBounds(adr)) { if (mem[adr] > 0) { mem[adr]--; } else { ps = badVal; } } break; case cpy: int addrC1 = Pop(); int addrC2 = Pop(); int addrLengthC1 = mem[mem[addrC1]]; int addrLengthC2 = mem[mem[addrC2]]; if (addrLengthC1 != addrLengthC2) { ps = badVal; } else { int i = 1; while ((i <= addrLengthC1)) { mem[mem[addrC2] + i] = mem[mem[addrC1] + i]; i++; } } break; case eql: int addr1 = Pop(); int addr2 = Pop(); int addrLength1 = mem[mem[addr1]]; int addrLength2 = mem[mem[addr2]]; if (addrLength1 != addrLength2) { Push(0); // push flase if lenghts arnt == } else { bool isEqual = true; int i = 1; while (i < addrLength1 + 1) { if (mem[mem[addr1] + i] != mem[mem[addr2] + i]) { isEqual = false; } i++; } if (isEqual) { Push(1); } else { Push(0); } } break; case PVM.stack: // stack dump (debugging) StackDump(results, pcNow); break; case PVM.heap: // heap dump (debugging) HeapDump(results, pcNow); break; case PVM.fhdr: // allocate frame header if (InBounds(cpu.sp - headerSize)) { mem[cpu.sp - headerSize] = cpu.mp; cpu.mp = cpu.sp; cpu.sp -= headerSize; } break; case PVM.call: // call function if (mem[cpu.pc] < 0) { ps = badAdr; } else { mem[cpu.mp - 2] = cpu.fp; mem[cpu.mp - 3] = cpu.pc + 1; cpu.fp = cpu.mp; cpu.pc = mem[cpu.pc]; } break; case PVM.retv: // return from void function cpu.sp = cpu.fp; cpu.mp = mem[cpu.fp - 4]; cpu.pc = mem[cpu.fp - 3]; cpu.fp = mem[cpu.fp - 2]; break; default: // unrecognized opcode ps = badOp; break; } // switch(cpu.ir) } while (ps == running); if (ps != finished) { PostMortem(results, pcNow); } TimeSpan ts = timer.Elapsed; string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("\n" + ops + " operations. Run Time " + elapsedTime); timer.Reset(); timer.Stop(); } // PVM.Emulator
} // PVM.InBounds public static void Emulator(int initPC, int codeLen, int initSP, InFile data, OutFile results, bool tracing, bool traceStack, bool traceHeap) { // Emulates action of the codeLen instructions stored in mem[0 .. codeLen-1], with // program counter initialized to initPC, stack pointer initialized to initSP. // data and results are used for I/O. Tracing at the code level may be requested int pcNow; // current program counter int loop; // internal loops int tos, sos; // values popped from stack int adr; // effective address for memory accesses int target; // destination for branches stackBase = initSP; heapBase = codeLen; // initialize boundaries cpu.hp = heapBase; // initialize registers cpu.sp = stackBase; cpu.gp = stackBase; cpu.mp = stackBase; cpu.fp = stackBase; cpu.pc = initPC; // initialize program counter for (int i = heapBase; i < stackBase; i++) mem[i] = 0; // set entire memory to null or 0 ps = running; // prepare to execute int ops = 0; timer.Start(); do { ops++; pcNow = cpu.pc; // retain for tracing/postmortem if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; break; } cpu.ir = Next(); // fetch if (tracing) Trace(results, pcNow, traceStack, traceHeap); switch (cpu.ir) { // execute case PVM.nop: // no operation break; case PVM.dsp: // decrement stack pointer (allocate space for variables) int localSpace = Next(); cpu.sp -= localSpace; if (InBounds(cpu.sp)) // initialize all local variables to zero/null for (loop = 0; loop < localSpace; loop++) mem[cpu.sp + loop] = 0; break; case PVM.ldc: // push constant value Push(Next()); break; case PVM.ldc_m1: // push constant -1 Push(-1); break; case PVM.ldc_0: // push constant 0 Push(0); break; case PVM.ldc_1: // push constant 1 Push(1); break; case PVM.ldc_2: // push constant 2 Push(2); break; case PVM.ldc_3: // push constant 3 Push(3); break; case PVM.ldc_4: // push constant 4 Push(4); break; case PVM.ldc_5: // push constant 5 Push(5); break; case PVM.dup: // duplicate top of stack tos = Pop(); Push(tos); Push(tos); break; case PVM.lda: // push local address adr = cpu.fp - 1 - Next(); if (InBounds(adr)) Push(adr); break; case PVM.lda_0: // push local address 0 adr = cpu.fp - 1; if (InBounds(adr)) Push(adr); break; case PVM.lda_1: // push local address 1 adr = cpu.fp - 2; if (InBounds(adr)) Push(adr); break; case PVM.lda_2: // push local address 2 adr = cpu.fp - 3; if (InBounds(adr)) Push(adr); break; case PVM.lda_3: // push local address 3 adr = cpu.fp - 4; if (InBounds(adr)) Push(adr); break; case PVM.lda_4: // push local address 4 adr = cpu.fp - 5; if (InBounds(adr)) Push(adr); break; case PVM.lda_5: // push local address 5 adr = cpu.fp - 6; if (InBounds(adr)) Push(adr); break; case PVM.ldl: // push local value adr = cpu.fp - 1 - Next(); if (InBounds(adr)) Push(mem[adr]); break; case PVM.ldl_0: // push value of local variable 0 adr = cpu.fp - 1; if (InBounds(adr)) Push(mem[adr]); break; case PVM.ldl_1: // push value of local variable 1 adr = cpu.fp - 2; if (InBounds(adr)) Push(mem[adr]); break; case PVM.ldl_2: // push value of local variable 2 adr = cpu.fp - 3; if (InBounds(adr)) Push(mem[adr]); break; case PVM.ldl_3: // push value of local variable 3 adr = cpu.fp - 4; if (InBounds(adr)) Push(mem[adr]); break; case PVM.ldl_4: // push value of local variable 4 adr = cpu.fp - 5; if (InBounds(adr)) Push(mem[adr]); break; case PVM.ldl_5: // push value of local variable 5 adr = cpu.fp - 6; if (InBounds(adr)) Push(mem[adr]); break; case PVM.stl: // store local value adr = cpu.fp - 1 - Next(); if (InBounds(adr)) mem[adr] = Pop(); break; case PVM.stlc: // character checked pop to local variable tos = Pop(); adr = cpu.fp - 1 - Next(); if (InBounds(adr)) if (tos >= 0 && tos <= maxChar) mem[adr] = tos; else ps = badVal; break; case PVM.stl_0: // pop to local variable 0 adr = cpu.fp - 1; if (InBounds(adr)) mem[adr] = Pop(); break; case PVM.stl_1: // pop to local variable 1 adr = cpu.fp - 2; if (InBounds(adr)) mem[adr] = Pop(); break; case PVM.stl_2: // pop to local variable 2 adr = cpu.fp - 3; if (InBounds(adr)) mem[adr] = Pop(); break; case PVM.stl_3: // pop to local variable 3 adr = cpu.fp - 4; if (InBounds(adr)) mem[adr] = Pop(); break; case PVM.stl_4: // pop to local variable 4 adr = cpu.fp - 5; if (InBounds(adr)) mem[adr] = Pop(); break; case PVM.stl_5: // pop to local variable 5 adr = cpu.fp - 6; if (InBounds(adr)) mem[adr] = Pop(); break; case PVM.ldv: // dereference adr = Pop(); if (InBounds(adr)) Push(mem[adr]); break; case PVM.sto: // store tos = Pop(); adr = Pop(); if (InBounds(adr)) mem[adr] = tos; break; case PVM.stoc: // character checked store tos = Pop(); adr = Pop(); if (InBounds(adr)) if (tos >= 0 && tos <= maxChar) mem[adr] = tos; else ps = badVal; break; case PVM.ldxa: // heap array indexing adr = Pop(); int heapPtr = Pop(); if (heapPtr == 0) ps = nullRef; else if (heapPtr < heapBase || heapPtr >= cpu.hp) ps = badMem; else if (adr < 0 || adr >= mem[heapPtr]) ps = badInd; else Push(heapPtr + adr + 1); break; case PVM.inpi: // integer input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadInt(); if (data.Error()) ps = badData; } break; case PVM.inpb: // boolean input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadBool() ? 1 : 0; if (data.Error()) ps = badData; } break; case PVM.inpc: // character input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadChar(); if (data.Error()) ps = badData; } break; case PVM.inpl: // skip to end of input line data.ReadLine(); break; case PVM.i2c: // check (char) cast is in range if (mem[cpu.sp] < 0 || mem[cpu.sp] > maxChar) ps = badVal; break; case PVM.prni: // integer output if (tracing) results.Write(padding); results.Write(Pop(), 0); if (tracing) results.WriteLine(); break; case PVM.prnb: // boolean output if (tracing) results.Write(padding); if (Pop() != 0) results.Write(" true "); else results.Write(" false "); if (tracing) results.WriteLine(); break; case PVM.prnc: // character output if (tracing) results.Write(padding); results.Write((char) (Math.Abs(Pop()) % (maxChar + 1)), 1); if (tracing) results.WriteLine(); break; case PVM.prns: // string output if (tracing) results.Write(padding); loop = Next(); while (ps == running && mem[loop] != 0) { results.Write((char) mem[loop]); loop--; if (loop < stackBase) ps = badMem; } if (tracing) results.WriteLine(); break; case PVM.prnl: // newline results.WriteLine(); break; case PVM.neg: // integer negation Push(-Pop()); break; case PVM.add: // integer addition tos = Pop(); Push(Pop() + tos); break; case PVM.sub: // integer subtraction tos = Pop(); Push(Pop() - tos); break; case PVM.mul: // integer multiplication tos = Pop(); sos = Pop(); if (tos != 0 && Math.Abs(sos) > maxInt / Math.Abs(tos)) ps = badVal; else Push(sos * tos); break; case PVM.div: // integer division (quotient) tos = Pop(); if (tos == 0) ps = divZero; else Push(Pop() / tos); break; case PVM.rem: // integer division (remainder) tos = Pop(); if (tos == 0) ps = divZero; else Push(Pop() % tos); break; case PVM.not: // logical negation Push(Pop() == 0 ? 1 : 0); break; case PVM.and: // logical and tos = Pop(); Push(Pop() & tos); break; case PVM.or: // logical or tos = Pop(); Push(Pop() | tos); break; case PVM.ceq: // logical equality tos = Pop(); Push(Pop() == tos ? 1 : 0); break; case PVM.cne: // logical inequality tos = Pop(); Push(Pop() != tos ? 1 : 0); break; case PVM.clt: // logical less tos = Pop(); Push(Pop() < tos ? 1 : 0); break; case PVM.cle: // logical less or equal tos = Pop(); Push(Pop() <= tos ? 1 : 0); break; case PVM.cgt: // logical greater tos = Pop(); Push(Pop() > tos ? 1 : 0); break; case PVM.cge: // logical greater or equal tos = Pop(); Push(Pop() >= tos ? 1 : 0); break; case PVM.brn: // unconditional branch cpu.pc = Next(); if (cpu.pc < 0 || cpu.pc >= codeLen) ps = badAdr; break; case PVM.bze: // pop top of stack, branch if false target = Next(); if (Pop() == 0) { cpu.pc = target; if (cpu.pc < 0 || cpu.pc >= codeLen) ps = badAdr; } break; case PVM.bfalse: // conditional short circuit "and" branch target = Next(); if (mem[cpu.sp] == 0) cpu.pc = target; else cpu.sp++; break; case PVM.btrue: // conditional short circuit "or" branch target = Next(); if (mem[cpu.sp] == 1) cpu.pc = target; else cpu.sp++; break; case PVM.anew: // heap array allocation int size = Pop(); if (size <= 0 || size + 1 > cpu.sp - cpu.hp - 2) ps = badAll; else { mem[cpu.hp] = size; // first element stores size for bounds checking Push(cpu.hp); cpu.hp += size + 1; // bump heap pointer // elements are already initialized to 0 / null (why?) } break; case PVM.halt: // halt ps = finished; break; case PVM.inc: // integer ++ adr = Pop(); if (InBounds(adr)) mem[adr]++; break; case PVM.dec: // integer -- adr = Pop(); if (InBounds(adr)) mem[adr]--; break; case PVM.incc: // ++ characters adr = Pop(); if (InBounds(adr)) if (mem[adr] < maxChar) mem[adr]++; else ps = badVal; break; case PVM.decc: // -- characters adr = Pop(); if (InBounds(adr)) if (mem[adr] > 0) mem[adr]--; else ps = badVal; break; case PVM.stack: // stack dump (debugging) StackDump(results, pcNow); break; case PVM.heap: // heap dump (debugging) HeapDump(results, pcNow); break; case PVM.fhdr: // allocate frame header if (InBounds(cpu.sp - headerSize)) { mem[cpu.sp - headerSize] = cpu.mp; cpu.mp = cpu.sp; cpu.sp -= headerSize; } break; case PVM.call: // call function if (mem[cpu.pc] < 0) ps = badAdr; else { mem[cpu.mp - 2] = cpu.fp; mem[cpu.mp - 3] = cpu.pc + 1; cpu.fp = cpu.mp; cpu.pc = mem[cpu.pc]; } break; case PVM.retv: // return from void function cpu.sp = cpu.fp; cpu.mp = mem[cpu.fp - 4]; cpu.pc = mem[cpu.fp - 3]; cpu.fp = mem[cpu.fp - 2]; break; case PVM.max: tos = Pop(); sos = Pop(); if (tos > sos) Push(tos); else Push(sos); break; case PVM.min: tos = Pop(); sos = Pop(); if (tos < sos) Push(tos); else Push(sos); break; case PVM.sqr: tos = Pop(); Push((int)Math.Sqrt(tos)); break; case PVM.aceq: tos =Pop(); //address of array 1 sos = Pop(); //address of array 1 int len_1 = mem[tos]; int len_2 = mem[sos]; if(len_1!=len_2){ Push(0); // if lengths aren't equal then arrays can't be equal } else{ int i=0; while(i<len_1 && mem[tos+1+i] == mem[sos+1+i]){ i++; } if(i==len_1) Push(1); else Push(0); } break; case PVM.acpy: tos =Pop(); //address to be copied sos = Pop(); //address of array to be assigned if(mem[tos]==mem[sos]){ //if the lengths are not the same leave it int i=0; while(i<mem[tos]){ mem[sos+1+i] = mem[tos+1+i]; } } /* else{ IO.WriteLine("Arrays must be the same size to be copied!"); ps=badVal; } */ break; case PVM.wprni: // integer output with wdith if (tracing) results.Write(padding); tos=Pop(); results.Write(Pop(),tos); if (tracing) results.WriteLine(); break; case PVM.wprnb: // boolean output with width if (tracing) results.Write(padding); tos = Pop(); if (Pop() != 0) results.Write("true",tos); else results.Write("false",tos); if (tracing) results.WriteLine(); break; case PVM.wprnc: // character output with width if (tracing) results.Write(padding); tos = Pop(); results.Write((char) (Math.Abs(Pop()) % (maxChar + 1)), tos); if (tracing) results.WriteLine(); break; default: // unrecognized opcode ps = badOp; break; } // switch(cpu.ir) } while (ps == running); if (ps != finished) PostMortem(results, pcNow); TimeSpan ts = timer.Elapsed; string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("\n" + ops + " operations. Run Time " + elapsedTime); timer.Reset(); timer.Stop(); } // PVM.Emulator
} // PVM.InBounds public static void Emulator(int initPC, int codeLen, int initSP, InFile data, OutFile results, bool tracing, bool traceStack, bool traceHeap) { // Emulates action of the codeLen instructions stored in mem[0 .. codeLen-1], with // program counter initialized to initPC, stack pointer initialized to initSP. // data and results are used for I/O. Tracing at the code level may be requested int pcNow; // current program counter int loop; // internal loops int tos, sos; // value popped from stack int adr; // effective address for memory accesses stackBase = initSP; heapBase = codeLen; // initialize boundaries cpu.hp = heapBase; // initialize registers cpu.sp = stackBase; cpu.gp = stackBase; cpu.mp = stackBase; cpu.fp = stackBase; cpu.pc = initPC; // initialize program counter ps = running; // prepare to execute int ops = 0; timer.Start(); do { ops++; pcNow = cpu.pc; // retain for tracing/postmortem if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; break; } cpu.ir = Next(); // fetch if (tracing) { Trace(results, pcNow, traceStack, traceHeap); } switch (cpu.ir) // execute { case PVM.nop: // no operation break; case PVM.dsp: // decrement stack pointer (allocate space for variables) int localSpace = Next(); cpu.sp -= localSpace; if (InBounds(cpu.sp)) // initialize { for (loop = 0; loop < localSpace; loop++) { mem[cpu.sp + loop] = 0; } } break; case PVM.ldc: // push constant value Push(Next()); break; case PVM.lda: // push local address adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { Push(adr); } break; case PVM.ldv: // dereference Push(mem[Pop()]); break; case PVM.sto: // store tos = Pop(); adr = Pop(); if (InBounds(adr)) { mem[adr] = tos; } break; case PVM.ldxa: // heap array indexing adr = Pop(); int heapPtr = Pop(); if (heapPtr == 0) { ps = nullRef; } else if (heapPtr < heapBase || heapPtr >= cpu.hp) { ps = badMem; } else if (adr < 0 || adr >= mem[heapPtr]) { ps = badInd; } else { Push(heapPtr + adr + 1); } break; case PVM.inpi: // integer input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadInt(); if (data.Error()) { ps = badData; } } break; case PVM.prni: // integer output if (tracing) { results.Write(padding); } results.Write(Pop(), 0); if (tracing) { results.WriteLine(); } break; case PVM.inpb: // boolean input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadBool() ? 1 : 0; if (data.Error()) { ps = badData; } } break; case PVM.prnb: // boolean output if (tracing) { results.Write(padding); } if (Pop() != 0) { results.Write(" true "); } else { results.Write(" false "); } if (tracing) { results.WriteLine(); } break; case PVM.prns: // string output if (tracing) { results.Write(padding); } loop = Next(); while (ps == running && mem[loop] != 0) { results.Write((char)mem[loop]); loop--; if (loop < stackBase) { ps = badMem; } } if (tracing) { results.WriteLine(); } break; case PVM.prnl: // newline results.WriteLine(); break; case PVM.neg: // integer negation Push(-Pop()); break; case PVM.add: // integer addition tos = Pop(); Push(Pop() + tos); break; case PVM.sub: // integer subtraction tos = Pop(); Push(Pop() - tos); break; case PVM.mul: // integer multiplication //tos = Pop(); Push(Pop() * tos); tos = Pop(); int prod = Pop(); if (tos > 0 && prod > 2147483647 / tos) { Push(tos * prod); } else { ps = badVal; } break; case PVM.div: // integer division (quotient) //tos = Pop(); Push(Pop() / tos); tos = Pop(); if (tos != 0) { Push(Pop() / tos); } else { ps = divZero; } break; case PVM.rem: // integer division (remainder) tos = Pop(); Push(Pop() % tos); break; case PVM.not: // logical negation Push(Pop() == 0 ? 1 : 0); break; case PVM.and: // logical and tos = Pop(); Push(Pop() & tos); break; case PVM.or: // logical or tos = Pop(); Push(Pop() | tos); break; case PVM.ceq: // logical equality tos = Pop(); Push(Pop() == tos ? 1 : 0); break; case PVM.cne: // logical inequality tos = Pop(); Push(Pop() != tos ? 1 : 0); break; case PVM.clt: // logical less tos = Pop(); Push(Pop() < tos ? 1 : 0); break; case PVM.cle: // logical less or equal tos = Pop(); Push(Pop() <= tos ? 1 : 0); break; case PVM.cgt: // logical greater tos = Pop(); Push(Pop() > tos ? 1 : 0); break; case PVM.cge: // logical greater or equal tos = Pop(); Push(Pop() >= tos ? 1 : 0); break; case PVM.brn: // unconditional branch cpu.pc = Next(); if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; } break; case PVM.bze: // pop top of stack, branch if false int target = Next(); if (Pop() == 0) { cpu.pc = target; if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; } } break; case PVM.anew: // heap array allocation int size = Pop(); if (size <= 0 || size + 1 > cpu.sp - cpu.hp - 2) { ps = badAll; } else { mem[cpu.hp] = size; Push(cpu.hp); cpu.hp += size + 1; } break; case PVM.halt: // halt ps = finished; break; case PVM.stk: // stack dump (debugging) StackDump(results, pcNow); break; case PVM.heap: // heap dump (debugging) HeapDump(results, pcNow); break; case PVM.ldc_0: // push constant 0 case PVM.ldc_1: // push constant 1 case PVM.ldc_2: // push constant 2 case PVM.ldc_3: // push constant 3 case PVM.lda_0: // push local address 0 case PVM.lda_1: // push local address 1 case PVM.lda_2: // push local address 2 case PVM.lda_3: // push local address 3 case PVM.ldl: // push local value case PVM.ldl_0: // push value of local variable 0 case PVM.ldl_1: // push value of local variable 1 case PVM.ldl_2: // push value of local variable 2 case PVM.ldl_3: // push value of local variable 3 case PVM.stl: // store local value case PVM.stlc: // store local value case PVM.stl_0: // pop to local variable 0 case PVM.stl_1: // pop to local variable 1 case PVM.stl_2: // pop to local variable 2 case PVM.stl_3: // pop to local variable 3 case PVM.stoc: // character checked store case PVM.inpc: // character input case PVM.prnc: // character output case PVM.cap: // toUpperCase case PVM.low: // toLowerCase case PVM.islet: // isLetter case PVM.inc: // ++ case PVM.dec: // -- default: // unrecognized opcode ps = badOp; break; } } while (ps == running); TimeSpan ts = timer.Elapsed; // Format and display the TimeSpan value. string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); Console.WriteLine("\n\n" + ops + " operations. Run Time " + elapsedTime + "\n\n"); if (ps != finished) { PostMortem(results, pcNow); } timer.Reset(); timer.Stop(); } // PVM.Emulator
} // PVM.InBounds public static void Emulator(int initPC, int codeLen, int initSP, InFile data, OutFile results, bool tracing, bool traceStack, bool traceHeap) { // Emulates action of the codeLen instructions stored in mem[0 .. codeLen-1], with // program counter initialized to initPC, stack pointer initialized to initSP. // data and results are used for I/O. Tracing at the code level may be requested int pcNow; // current program counter int loop; // internal loops int tos, sos; // value popped from stack int adr; // effective address for memory accesses stackBase = initSP; heapBase = codeLen; // initialize boundaries cpu.hp = heapBase; // initialize registers cpu.sp = stackBase; cpu.gp = stackBase; cpu.mp = stackBase; cpu.fp = stackBase; cpu.pc = initPC; // initialize program counter ps = running; // prepare to execute int ops = 0; timer.Start(); do { ops++; pcNow = cpu.pc; // retain for tracing/postmortem if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; break; } cpu.ir = Next(); // fetch if (tracing) { Trace(results, pcNow, traceStack, traceHeap); } switch (cpu.ir) // execute { case PVM.nop: // no operation break; case PVM.dsp: // decrement stack pointer (allocate space for variables) int localSpace = Next(); cpu.sp -= localSpace; if (InBounds(cpu.sp)) // initialize { for (loop = 0; loop < localSpace; loop++) { mem[cpu.sp + loop] = 0; } } break; case PVM.ldc: // push constant value Push(Next()); break; case PVM.lda: // push local address adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { Push(adr); } break; case PVM.ldv: // dereference Push(mem[Pop()]); break; case PVM.sto: // store tos = Pop(); adr = Pop(); if (InBounds(adr)) { mem[adr] = tos; } break; case PVM.ldxa: // heap array indexing adr = Pop(); int heapPtr = Pop(); if (heapPtr == 0) { ps = nullRef; } else if (heapPtr < heapBase || heapPtr >= cpu.hp) { ps = badMem; } else if (adr < 0 || adr >= mem[heapPtr]) { ps = badInd; } else { Push(heapPtr + adr + 1); } break; case PVM.inpi: // integer input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadInt(); if (data.Error()) { ps = badData; } } break; case PVM.prni: // integer output if (tracing) { results.Write(padding); } results.Write(Pop(), 0); if (tracing) { results.WriteLine(); } break; case PVM.inpb: // boolean input adr = Pop(); if (InBounds(adr)) { mem[adr] = data.ReadBool() ? 1 : 0; if (data.Error()) { ps = badData; } } break; case PVM.inpc: // character input adr = Pop(); if (InBounds(adr)) { mem[adr] = ((int)(data.ReadChar())); if (data.Error()) { ps = badData; } } break; case PVM.prnb: // boolean output if (tracing) { results.Write(padding); } if (Pop() != 0) { results.Write(" true "); } else { results.Write(" false "); } if (tracing) { results.WriteLine(); } break; case PVM.prns: // string output if (tracing) { results.Write(padding); } loop = Next(); while (ps == running && mem[loop] != 0) { results.Write((char)(mem[loop])); loop--; if (loop < stackBase) { ps = badMem; } } if (tracing) { results.WriteLine(); } break; case PVM.prnc: // character output if (tracing) { results.Write(padding); } results.Write((char)Pop()); if (tracing) { results.WriteLine(); } break; case PVM.prnl: // newline results.WriteLine(); break; case PVM.neg: // integer negation Push(-Pop()); break; case PVM.add: // integer addition tos = Pop(); Push(Pop() + tos); break; case PVM.sub: // integer subtraction tos = Pop(); Push(Pop() - tos); break; case PVM.mul: // integer multiplication tos = Pop(); if ((mem[cpu.sp] / 2147483647) > tos) { Push(Pop() * tos); } else { ps = badVal; } break; case PVM.div: // integer division (quotient) tos = Pop(); if (tos != 0) { Push(Pop() / tos); } else { ps = divZero; } break; case PVM.rem: // integer division (remainder) tos = Pop(); if (tos != 0) { Push(Pop() % tos); } else { ps = divZero; } break; case PVM.not: // logical negation Push(Pop() == 0 ? 1 : 0); break; case PVM.and: // logical and tos = Pop(); Push(Pop() & tos); break; case PVM.or: // logical or tos = Pop(); Push(Pop() | tos); break; case PVM.ceq: // logical equality tos = Pop(); Push(Pop() == tos ? 1 : 0); break; case PVM.cne: // logical inequality tos = Pop(); Push(Pop() != tos ? 1 : 0); break; case PVM.clt: // logical less tos = Pop(); Push(Pop() < tos ? 1 : 0); break; case PVM.cle: // logical less or equal tos = Pop(); Push(Pop() <= tos ? 1 : 0); break; case PVM.cgt: // logical greater tos = Pop(); Push(Pop() > tos ? 1 : 0); break; case PVM.cge: // logical greater or equal tos = Pop(); Push(Pop() >= tos ? 1 : 0); break; case PVM.brn: // unconditional branch cpu.pc = Next(); if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; } break; case PVM.bze: // pop top of stack, branch if false int target = Next(); if (Pop() == 0) { cpu.pc = target; if (cpu.pc < 0 || cpu.pc >= codeLen) { ps = badAdr; } } break; case PVM.anew: // heap array allocation int size = Pop(); if (size <= 0 || size + 1 > cpu.sp - cpu.hp - 2) { ps = badAll; } else { mem[cpu.hp] = size; Push(cpu.hp); cpu.hp += size + 1; } break; case PVM.halt: // halt ps = finished; break; case PVM.stk: // stack dump (debugging) StackDump(results, pcNow); break; case PVM.heap: // heap dump (debugging) HeapDump(results, pcNow); break; case PVM.ldc_0: // push constant 0 Push(0); break; case PVM.ldc_1: // push constant 1 Push(1); break; case PVM.ldc_2: // push constant 2 Push(2); break; case PVM.ldc_3: // push constant 3 Push(3); break; case PVM.lda_0: // push local address 0 adr = cpu.fp - 1; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_1: // push local address 1 adr = cpu.fp - 2; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_2: // push local address 2 adr = cpu.fp - 3; if (InBounds(adr)) { Push(adr); } break; case PVM.lda_3: // push local address 3 adr = cpu.fp - 4; if (InBounds(adr)) { Push(adr); } break; case PVM.ldl: // push local value adr = cpu.fp - 1 - Next(); if (inbounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_0: // push value of local variable 0 adr = cpu.fp - 1; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_1: // push value of local variable 1 adr = cpu.fp - 2; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_2: // push value of local variable 2 adr = cpu.fp - 3; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.ldl_3: // push value of local variable 3 adr = cpu.fp - 4; if (InBounds(adr)) { Push(mem[adr]); } break; case PVM.stl: // store local value adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stlc: // store local value adr = cpu.fp - 1 - Next(); if (InBounds(adr)) { mem[adr] = Pop(); } break; case PVM.stl_0: // pop to local variable 0 mem[cpu.fp - 1] = Pop(); break; case PVM.stl_1: // pop to local variable 1 mem[cpu.fp - 2] = Pop(); break; case PVM.stl_2: // pop to local variable 2 mem[cpu.fp - 3] = Pop(); break; case PVM.stl_3: // pop to local variable 3 mem[cpu.fp - 4] = Pop(); break; case PVM.stoc: // character checked store tos ch = Pop(); adr = Pop(); if (ch < 256 && ch >= 0) { if (InBounds(adr)) { mem[ard] = tos; } } break; case PVM.cap: // toUpperCase tos = Pop(); mem[tos] -= 32; break; case PVM.low: // toLowerCase tos = pop(); mem[tos] += 32; break; case PVM.islet: // isLetter tos = Pop(); if ((tos > 64 && tos < 91) || (tos > 96 && tos < 123)) { case PVM.inc: // ++ tos = Pop(); } mem[tos]++; break; case PVM.dec: // -- tos = Pop(); mem[tos]--; break;