static unsafe void Main(string[] args) { #if DOTNET2 Console.Title = "Target Virtual Machine"; #endif Console.WriteLine("Target Assembler/Virtual Machine v{0}.", assemblyVersion); Console.WriteLine("Copyright (c) 2006, Peter Wright <*****@*****.**>"); Console.WriteLine(""); Decoder cpu = new Decoder(new Core()); // Parse any arguments the user has specified on the command-line #region argument parsing for (int i = 0; i < args.Length; i++) { if (args[i] != null && args[i].StartsWith("--")) { string arg = args[i].ToLower(); args[i] = null; // Prevent the assembler from processing this instruction switch (arg) { case "--help": case "--usage": Console.WriteLine("Usage:"); Console.WriteLine("vm.exe {options} objectCodeFile {objectCodeFile}"); Console.WriteLine("Options:"); Console.WriteLine("--monitor Toggles the monitor (default {0})", cpu.debug); Console.WriteLine("--break # Sets a breakpoint at addr #"); Console.WriteLine("--haltBreak Toggles breaking on HALT (default {0})", cpu.haltBreak); Console.WriteLine("--noopBreak Toggles breaking on NOOP (default {0})", cpu.noopBreak); Console.WriteLine("--decode Toggles monitor's automatic decoding (default {0})", cpu.autoDecode); Console.WriteLine("--echoasm Toggles assembler's asm echo (default {0})", Assembler.echoAssembledInstructions); Console.WriteLine("--pc # Sets initial PC value to # (default {0})", cpu.vm.PC); Console.WriteLine("--fp # Sets initial FP value to # (default {0})", cpu.vm.FP); Console.WriteLine("--sp # Sets initial SP value to # (default {0})", cpu.vm.SP); Console.WriteLine("--mp # Sets initial MP value to # (default {0})", cpu.vm.MP); Console.WriteLine("--bp # Sets initial BP value to # (default {0})", cpu.vm.BP); Console.WriteLine("--time Toggles performance timing (default {0})", timeExecution); Console.WriteLine("--trace Saves a trace to file f"); Console.WriteLine("--logio Logs all IO to file f"); Console.WriteLine("Option arguments are not error-handled."); return; case "--noopbreak": case "--nb": cpu.noopBreak = !cpu.noopBreak; break; case "--haltbreak": case "--hb": cpu.haltBreak = !cpu.haltBreak; break; case "--pc": cpu.vm.PC = ushort.Parse(args[i + 1]); args[i + 1] = null; // Prevent processing of the next argument break; case "--sp": cpu.vm.SP = ushort.Parse(args[i + 1]); args[i + 1] = null; // Prevent processing of the next argument break; case "--mp": cpu.vm.MP = ushort.Parse(args[i + 1]); args[i + 1] = null; // Prevent processing of the next argument break; case "--fp": cpu.vm.FP = ushort.Parse(args[i + 1]); args[i + 1] = null; // Prevent processing of the next argument break; case "--bp": cpu.vm.BP = ushort.Parse(args[i + 1]); args[i + 1] = null; // Prevent processing of the next argument break; case "--time": timeExecution = !timeExecution; break; case "--monitor": case "--debug": // Display monitor immediately cpu.debug = !cpu.debug; break; case "--logio": case "--lio": // Log IO to disk cpu.startIOLog(args[i + 1]); args[i + 1] = null; break; case "--trace": case "--tracefile": // Save traces to disk cpu.startTrace(args[i + 1]); args[i + 1] = null; break; case "--echoasm": Assembler.echoAssembledInstructions = !Assembler.echoAssembledInstructions; break; case "--break": // Enable the monitor, place a breakpoint at the address specified in the next argument: cpu.debug = true; cpu.addrBreak = ushort.Parse(args[i + 1]); args[i + 1] = null; // Prevent processing of the next argument break; default: Console.WriteLine("Unknown argument: " + args[0]); return; } } } #endregion // Now assemble the files and dump the result into main memory DateTime asmStart = DateTime.Now; Assembler a = new Assembler(args); DateTime asmStop = DateTime.Now; cpu.vm.memory = a.getAssembled(); Console.WriteLine("\nExecuting code..."); // Execute until the CPU is halted DateTime runStart = DateTime.Now; cpu.execute(); DateTime runStop = DateTime.Now; if (timeExecution) { Console.WriteLine("\nTIMING INFORMATION (+ MONITOR TIME)"); TimeSpan tAsm = asmStop - asmStart; TimeSpan tRun = runStop - runStart; Console.WriteLine(" Assembly Duration: {0}", tAsm); Console.WriteLine(" Execution Duration: {0}", tRun); Console.WriteLine(" Instructions: {0}", cpu.ops); Console.WriteLine(" Comparisons: {0}", cpu.cmps); Console.WriteLine(" ops/sec: {0}", Math.Round(cpu.ops / tRun.TotalSeconds)); Console.WriteLine("<PRESS ENTER>"); Console.ReadLine(); } Console.WriteLine("\nTargetVM: Normal Termination."); } // end app
/// <summary>A simple debugging console</summary> public unsafe void monitor() { #region Monitor disappearance conditions if (addrBreak != -1) // If we have been instructed to break at a specific address: { if (vm.PC - 2 != addrBreak) { return; // Do not display the monitor yet } else { addrBreak = -1; } } else if (monitorSkipInstructions != 0) { if (--monitorSkipInstructions > 0) { return; // Do not display the monitor yet } } #endregion #region Handle coexistance with the UI nicely // If the CPU has performed IO since the last execution, ensure we're on a new line if (ioSinceMonitor) { Console.WriteLine(); ioSinceMonitor = false; } #endregion // If the user requested it, automatically display the decode of the instruction if (autoDecode) { Console.WriteLine("\t{1}", (vm.PC - 2), op.ToString()); } // Keep accepting arguments until we receive a terminal command #region Monitor switch while (true) { Console.Write("[{0}] Monitor> ", (vm.PC - 2)); string[] cmds = TargetVM.std.ArgParser.parse(Console.ReadLine()); if (cmds.Length == 0) { return; } switch (cmds[0].ToLower()) { case null: case "": case "c": case "continue": return; case "next": case "n": debug = true; return; case "r": case "run": debug = false; return; case "k": case "skip": // Skip n executions: if (cmds.Length == 2) { monitorSkipInstructions = parseAddr(cmds[1]); Console.WriteLine("Skipping monitor for next {0} instruction(s).", monitorSkipInstructions); return; } else { Console.WriteLine("Target Monitor: skip requires an argument. Example: skip 5"); break; } case "b": case "break": // Break at a specific address if (cmds.Length == 2) { addrBreak = parseAddr(cmds[1]); Console.WriteLine("Setting breakpoint at address {0}.", addrBreak); return; } else { Console.WriteLine("Target Monitor: break requires an argument. Example: break 210"); break; } case "s": case "halt": case "stop": case "exit": case "quit": case "q": // halt execution and terminate closeFiles(); Console.WriteLine("Target Monitor: goodbye."); System.Environment.Exit(0); return; case "search": // Search for the occurrances of n in memory if (cmds.Length == 2) { ushort val = parseValue(cmds[1]); Console.WriteLine("Searching memory..."); for (int i = vm.memory.Length - 1; i != 0; --i) { if (vm.memory[i] == val) { Console.WriteLine("{0}: {1}\t('{2}')", i, val, (char)val); } } Console.WriteLine("Complete."); } else { Console.WriteLine("Target Monitor: search requires one argument."); } break; case "hb": case "haltbreak": // examines / sets haltbreak Console.WriteLine("haltBreak={0}", haltBreak); if (cmds.Length == 2) { haltBreak = parseBool(cmds[1]); Console.WriteLine("haltBreak={0}\tCHANGED", haltBreak); } break; case "nb": case "noopbreak": // examines / sets noopBreak Console.WriteLine("noopBreak={0}", noopBreak); if (cmds.Length == 2) { noopBreak = parseBool(cmds[1]); Console.WriteLine("noopBreak={0}\tCHANGED", noopBreak); } break; case "setsp": vm.SP = parseAddr(cmds[1]); Console.WriteLine("SP={0}\tCHANGED", vm.SP); break; case "setfp": vm.FP = parseAddr(cmds[1]); Console.WriteLine("FP={0}\tCHANGED", vm.FP); break; case "setmp": vm.MP = parseAddr(cmds[1]); Console.WriteLine("MP={0}\tCHANGED", vm.MP); break; case "store": if (cmds.Length == 3) { ushort storeAddr = parseAddr(cmds[1]); ushort storeVal = parseValue(cmds[2]); vm.memory[storeAddr] = storeVal; Console.WriteLine("{0}:\t{1}", storeAddr, storeVal); } else { Console.WriteLine("Target Monitor: store takes 2 arguments (address, value)"); } break; case "push": ushort data = parseAddr(cmds[1]);; vm.push(data); Console.WriteLine("Pushed {0} onto the stack.", data); break; case "pop": if (vm.SP > 0) { Console.WriteLine("Popped {0} from the stack.", vm.pop()); } else { Console.WriteLine("Stack at top of address space. Cannot pop."); } break; case "j": case "jump": if (cmds.Length == 2) { vm.PC = parseAddr(cmds[1]); Console.WriteLine("Jumping to {0}", vm.PC); this.op = vm.getNextInstruction(); // Decode the instruction and execute it instead } else { Console.WriteLine("Target Monitor: jump requires an argument. Example: jump 50"); } break; case "d": case "decode": for (int i = 1; i < cmds.Length; i++) { ushort addr = parseAddr(cmds[i]); CpuInstruction memop = new CpuInstruction(); memop[0] = vm.memory[addr]; memop[1] = vm.memory[addr + 1]; Console.WriteLine("{0}:\t{1}", addr, memop.ToString()); } if (cmds.Length == 1) { Console.WriteLine("{0}\t{1}", (vm.PC - 2), op.ToString()); } break; case "i": case "inspect": for (int i = 1; i < cmds.Length; i++) { ushort addr = parseAddr(cmds[i]); if (signedAccess) { if (addr < vm.memory.Length) { Console.WriteLine("{0}:\t0x{1:X4} == {1}s", addr, (short)vm.memory[addr]); } else { Console.WriteLine("{0}:\tOUT OF BOUNDS"); } } else { if (addr < vm.memory.Length) { Console.WriteLine("{0}:\t0x{1:X4} == {1}u", addr, vm.memory[addr]); } else { Console.WriteLine("{0}:\tOUT OF BOUNDS"); } } } break; case "p": case "peek": ushort pitems = 1; bool peekSigned = signedAccess; if (cmds.Length == 2) { pitems = parseAddr(cmds[1]); } // Check we're not about to read out of the memory bounds if (vm.SP - pitems < 0) { Console.WriteLine("Target Monitor: {0} word{1} back from SP ({2}) is an illegal address.", pitems, (pitems != 1 ? "s" : ""), vm.SP); break; } for (int offset = 1; offset <= pitems; offset++) { ushort w = vm.memory[vm.SP - offset]; // vm.memGetWord(vm.SP - offset); if (peekSigned) { Console.WriteLine("{0}:\t0x{1:X4} == {1}s", vm.SP - offset, (short)w); } else { Console.WriteLine("{0}:\t0x{1:X4} == {1}u", vm.SP - offset, w); } } break; case "g": case "reg": case "registers": case "register": case "calc": // make it easier for the user to think about using reg to calculate values if (cmds.Length == 1) { Hashtable core = vm.coreDump(); foreach (string key in core.Keys) { Console.WriteLine("{0}\t= {1}", key, core[key]); } } else { for (int i = 1; i < cmds.Length; i++) { ushort val = parseAddr(cmds[i]); Console.WriteLine("{0} = {1}", cmds[i].ToUpper(), val); } } break; case "assemble": case "asm": if (cmds.Length >= 2) { // Assemble each file passed in as an argument for (int i = 1; i < cmds.Length; i++) { if (File.Exists(cmds[i])) { Assembler a = new Assembler(vm.memory, cmds[i]); vm.memory = a.getAssembled(); // technically unnecessary // Re-decode this instruction (in case it's changed) vm.PC -= 2; this.op = vm.getNextInstruction(); } else { Console.WriteLine("Arg #{0} Non-existant file {1}", i, cmds[i]); } } } else { Console.WriteLine("Monitor: assemble requires a parameter. Please quote paths with spaces in them."); } break; case "t": case "trace": case "savetrace": if (cmds.Length == 2) { Console.WriteLine("Monitor: Tracing Enabled"); startTrace(cmds[1]); } else { Console.WriteLine("Monitor: trace requires one parameter"); } break; case "logio": case "lio": case "io": case "l": if (cmds.Length == 2) { Console.WriteLine("Monitor: IO Logging Enabled"); startIOLog(cmds[1].Replace('_', ' ')); } else { Console.WriteLine("Monitor: logio requires one parameter. Please quote paths with spaces in them."); } break; case "signed": signedAccess = true; Console.WriteLine("Monitor: memory display set to signed"); break; case "unsigned": signedAccess = false; Console.WriteLine("Monitor: memory display set to unsigned"); break; case "?": case "help": #if DOTNET2 Console.BackgroundColor = ConsoleColor.Blue; Console.ForegroundColor = ConsoleColor.White; #endif Console.WriteLine("TARGET MONITOR - QUICK HELP"); #if DOTNET2 Console.ResetColor(); #endif Console.WriteLine("Copyright (c) 2006, Peter Wright <*****@*****.**>"); Console.WriteLine("Commands that take n can understand 'sp', '0,[fp,1]', etc."); Console.WriteLine("d {n} - Displays a decode of the instruction[s] at n."); Console.WriteLine(" Current instruction displayed if none are"); Console.WriteLine(" specified (decode)"); Console.WriteLine("p [-][n] - Displays the top n items on the stack. (peek)"); Console.WriteLine("i [-]{n} - Displays values stored in memory location[s] n."); Console.WriteLine("j addr - Branches immediately to ADDR. (jump)"); Console.WriteLine("q - Terminates the VM immediately"); Console.WriteLine("c - Resumes execution; monitor state unchanged"); Console.WriteLine(" (continue, <ENTER>)"); Console.WriteLine("r - Resumes execution; monitor disabled"); Console.WriteLine("n - Resumes execution; monitor enabled"); Console.WriteLine("k n - Hides monitor for another n operations. (skip)"); Console.WriteLine("b n - Hides monitor until operation at n. (break)"); Console.WriteLine("g - Displays all registers (registers)"); Console.WriteLine("g {n} - Displays specific register values"); Console.WriteLine("calc {n} - Calculates the address n"); Console.WriteLine("asm f - Assembles file f"); Console.WriteLine("t f - Starts saving trace data to file f"); Console.WriteLine("l f - Logs all IO to file f"); Console.WriteLine("signed - Changes memory display to signed mode"); break; default: Console.WriteLine("Monitor: Unknown command"); break; } } #endregion }