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 }