} // 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.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.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
public static bool Assemble(string sourceName) { // Assembles source code from an input file sourceName and loads codeLen // words of code directly into memory PVM.mem[0 .. codeLen-1], // storing strings in the string pool at the top of memory in // PVM.mem[stkBase .. memSize-1]. // // Returns true if assembly succeeded // // Instruction format : // Instruction = [ Label ] Opcode [ AddressField ] [ Comment ] // Label = [ "{" ] Integer [ "}" ] // Opcode = Mnemonic // AddressField = Integer | "String" // Comment = String // // A string AddressField may only be used with a PRNS opcode // Instructions are supplied one to a line; terminated at end of input file string mnemonic; // mnemonic for matching char quote; // string delimiter src = new InFile(sourceName); if (src.OpenError()) { Console.WriteLine("Could not open input file\n"); System.Environment.Exit(1); } Console.WriteLine("Assembling code ... \n"); codeLen = 0; stkBase = PVM.memSize - 1; okay = true; do { SkipLabel(); // ignore labels at start of line if (!src.EOF()) // we must have a line { mnemonic = ReadMnemonic(); // unpack mnemonic if (mnemonic == null) { continue; // ignore directives } int op = PVM.OpCode(mnemonic); // look it up PVM.mem[codeLen] = op; // store in memory switch (op) { case PVM.prns: // requires a string address field do { quote = src.ReadChar(); // search for opening quote character }while (quote != '"' && quote != '\'' && quote != '\n'); codeLen = (codeLen + 1) % PVM.memSize; PVM.mem[codeLen] = stkBase - 1; // pointer to start of string if (quote == '\n') { Error("Missing string address", codeLen); } else // stack string in literal pool { ch = src.ReadChar(); while (ch != quote && ch != '\n') { if (ch == '\\') { ch = src.ReadChar(); if (ch == '\n') // closing quote missing { Error("Malformed string", codeLen); } switch (ch) { case 't': ch = '\t'; break; case 'n': ch = '\n'; break; case '\"': ch = '\"'; break; case '\'': ch = '\''; break; default: break; } } stkBase--; PVM.mem[stkBase] = ch; ch = src.ReadChar(); } if (ch == '\n') // closing quote missing { Error("Malformed string", codeLen); } } stkBase--; PVM.mem[stkBase] = 0; // terminate string with nul break; case PVM.brn: // all require numeric address field case PVM.bze: case PVM.dsp: case PVM.lda: case PVM.ldc: codeLen = (codeLen + 1) % PVM.memSize; if (ch == '\n') // no field could be found { Error("Missing address", codeLen); } else // unpack it and store { PVM.mem[codeLen] = src.ReadInt(); if (src.Error()) { Error("Bad address", codeLen); } } break; case PVM.nul: // unrecognized mnemonic Console.Write(mnemonic); Error(" - Invalid opcode", codeLen); break; default: // no address needed break; } if (!src.EOL()) { src.ReadLn(); // skip comments } codeLen = (codeLen + 1) % PVM.memSize; } } while (!src.EOF()); for (int i = codeLen; i < stkBase; i++) // fill with invalid OpCodes { PVM.mem[i] = PVM.nul; } return(okay); }
} // 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
public static bool Assemble(string sourceName) { string mnemonic; // mnemonic for matching char quote; // string delimiter src = new InFile(sourceName); if (src.OpenError()) { Console.WriteLine("Could not open input file\n"); System.Environment.Exit(1); } Console.WriteLine("Assembling code ... \n"); codeLen = 0; stkBase = PVM.memSize - 1; okay = true; do { SkipLabel(); // ignore labels at start of line if (!src.EOF()) // we must have a line { mnemonic = ReadMnemonic(); // unpack mnemonic if (mnemonic == null) { continue; // ignore directives } int op = PVM.OpCode(mnemonic); // look it up PVM.mem[codeLen] = op; // store in memory switch (op) { case PVM.prns: // requires a string address field do { quote = src.ReadChar(); // search for opening quote character }while (quote != '"' && quote != '\'' && quote != '\n'); codeLen = (codeLen + 1) % PVM.memSize; PVM.mem[codeLen] = stkBase - 1; // pointer to start of string if (quote == '\n') { Error("Missing string address", codeLen); } else // stack string in literal pool { ch = src.ReadChar(); while (ch != quote && ch != '\n') { if (ch == '\\') { ch = src.ReadChar(); if (ch == '\n') // closing quote missing { Error("Malformed string", codeLen); } switch (ch) { case 't': ch = '\t'; break; case 'n': ch = '\n'; break; case '\"': ch = '\"'; break; case '\'': ch = '\''; break; default: break; } } stkBase--; PVM.mem[stkBase] = ch; ch = src.ReadChar(); } if (ch == '\n') // closing quote missing { Error("Malformed string", codeLen); } } stkBase--; PVM.mem[stkBase] = 0; // terminate string with nul break; case PVM.brn: // all require numeric address field case PVM.bze: case PVM.dsp: case PVM.lda: case PVM.ldc: case PVM.ldl: case PVM.stl: codeLen = (codeLen + 1) % PVM.memSize; if (ch == '\n') // no field could be found { Error("Missing address", codeLen); } else // unpack it and store { PVM.mem[codeLen] = src.ReadInt(); if (src.Error()) { Error("Bad address", codeLen); } } break; case PVM.nul: // unrecognized mnemonic Console.Write(mnemonic); Error(" - Invalid opcode", codeLen); break; default: // no address needed break; } if (!src.EOL()) { src.ReadLn(); // skip comments } codeLen = (codeLen + 1) % PVM.memSize; } } while (!src.EOF()); for (int i = codeLen; i < stkBase; i++) // fill with invalid OpCodes { PVM.mem[i] = PVM.nul; } return(okay); }
} // 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;