1 /** 2 Copyright: Copyright (c) 2013-2014 Andrey Penechko. 3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 8 module emulator.dcpu.emulator; 9 10 import std.conv : to; 11 import std.stdio; 12 13 import emulator.dcpu.dcpu; 14 import emulator.dcpu.devices.idevice; 15 import emulator.utils.undoproxy; 16 17 public import emulator.dcpu.execution; 18 public import emulator.dcpu.instruction; 19 import emulator.dcpu.emulationstats; 20 21 //@safe nothrow: 22 23 /// Does actual interpreting of DCPU-16 24 public class Emulator(CpuType) 25 { 26 CpuType dcpu; /// data storage: memory, registers. 27 EmulationStatistics stats; 28 29 void attachDevice(IDevice!CpuType device) 30 { 31 dcpu.attachDevice(device); 32 device.attachEmulator(this); 33 } 34 35 void loadProgram(ushort[] binary) 36 { 37 ushort size = binary.length & 0xFFFF; 38 39 dcpu.mem.observableArray[0..size*2] = cast(ubyte[])binary[0..size]; 40 } 41 42 /// Performs next instruction 43 void step(ulong numInstructions = 1) 44 { 45 foreach(_; 0..numInstructions) 46 { 47 ulong initialCycles = dcpu.regs.cycles; 48 49 Instruction instr = dcpu.fetchNext(); 50 51 dcpu.execute(instr); 52 53 handleInterrupt(dcpu); 54 55 ulong diff = dcpu.regs.cycles - initialCycles; 56 57 // Update statistics 58 stats.onInstructionDone(instr, diff); 59 60 // Update devices 61 dcpu.updateQueue.onTick(diff); 62 dcpu.regs.instructions = dcpu.regs.instructions + 1; 63 64 // Commit changes to undo stack 65 dcpu.regs.commitFrame(dcpu.regs.instructions); 66 dcpu.mem.commitFrame(dcpu.regs.instructions); 67 68 foreach(IUndoable device; dcpu.devices.values) 69 { 70 device.commitFrame(dcpu.regs.instructions); 71 } 72 } 73 } 74 75 void unstep(ulong numInstructions = 1) 76 { 77 ulong initialCycles = dcpu.regs.cycles; 78 79 // Undo 80 dcpu.regs.undoFrames(numInstructions); 81 dcpu.mem.undoFrames(numInstructions); 82 foreach(IUndoable device; dcpu.devices.values) 83 { 84 device.undoFrames(numInstructions); 85 } 86 87 // Update statistics 88 Instruction instr = dcpu.fetchNext(); 89 stats.onInstructionUndone(instr, initialCycles - dcpu.regs.cycles); 90 } 91 92 // Tries to do cyclesToStep cycles of dcpu. 93 // Returns actual cycles done. 94 ulong stepCycles(ulong cyclesToStep) 95 { 96 ulong initialCycles = dcpu.regs.cycles; 97 98 while(dcpu.regs.cycles - initialCycles < cyclesToStep) 99 { 100 step(); 101 } 102 103 return dcpu.regs.cycles - initialCycles; 104 } 105 106 // Tries to undo cyclesToStep cycles of dcpu. 107 // Returns actual cycles undone. 108 ulong unstepCycles(ulong cyclesToStep) 109 { 110 ulong initialCycles = dcpu.regs.cycles; 111 112 while(initialCycles - dcpu.regs.cycles < cyclesToStep && dcpu.regs.cycles > 0) 113 { 114 unstep(1); 115 } 116 117 if (dcpu.regs.cycles == 0) 118 { 119 dcpu.isRunning = false; 120 } 121 122 return initialCycles - dcpu.regs.cycles; 123 } 124 125 /// Resets dcpu state and interpreter state to their initial state. 126 void reset() 127 { 128 foreach(IUndoable device; dcpu.devices.values) 129 { 130 device.discardUndoStack(); 131 device.discardFrame(); 132 } 133 dcpu.reset(); 134 stats.reset(); 135 } 136 137 size_t undoStackSize() @property 138 { 139 size_t result; 140 141 result += dcpu.regs.undoStackSize; 142 result += dcpu.mem.undoStackSize; 143 foreach(IUndoable device; dcpu.devices.values) 144 { 145 result += device.undoStackSize; 146 } 147 148 return result; 149 } 150 }