1 /**
2 Copyright: Copyright (c) 2014 Andrey Penechko.
3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module application;
8 
9 import std.file : read, write, exists;
10 import std.path : setExtension;
11 import std.range;
12 import std.stdio : writeln;
13 import std..string : format;
14 
15 import anchovy.core.input;
16 
17 import anchovy.graphics.windows.glfwwindow;
18 import anchovy.graphics.texture;
19 import anchovy.graphics.bitmap;
20 import anchovy.gui;
21 import anchovy.gui.guirenderer;
22 
23 import anchovy.gui.application.application;
24 
25 import emulator.dcpu.emulator;
26 import emulator.dcpu.disassembler;
27 import emulator.dcpu.memoryanalyzer;
28 import emulator.dcpu.dcpu;
29 import emulator.dcpu.updatequeue;
30 import emulator.dcpu.memoryview;
31 
32 import emulator.dcpu.devices.lem1802;
33 import emulator.dcpu.devices.genericclock;
34 import emulator.dcpu.devices.generickeyboard;
35 import emulator.dcpu.devices.floppydrive;
36 
37 class EmulatorApplication : Application!GlfwWindow
38 {
39 	this(uvec2 windowSize, string caption)
40 	{
41 		super(windowSize, caption);
42 	}
43 
44 	Emulator!Dcpu emulator;
45 	Lem1802!Dcpu monitor;
46 	GenericClock!Dcpu clock;
47 	GenericKeyboard!Dcpu keyboard;
48 	FloppyDrive!Dcpu floppyDrive;
49 	Widget registerView;
50 	MemoryView!Dcpu memoryView;
51 	MemoryAnalyzer!Dcpu memAnalyzer;
52 
53 	bool isRunningForward = true;
54 	string file = "hello.bin";
55 
56 	void swapFileEndian(string filename)
57 	{
58 		import std.bitmanip : swapEndian;
59 		ubyte[] binary = cast(ubyte[])read(filename);
60 		foreach(ref srt; cast(ushort[])binary)
61 		{
62 			srt = swapEndian(srt);
63 		}
64 		write(filename, cast(void[])binary);
65 		reset(null, null);
66 	}
67 
68 	ushort[] loadBinary(string filename)
69 	{
70 		if (!exists(filename)) return [];
71 		ubyte[] binary = cast(ubyte[])read(filename);
72 		assert(binary.length % 2 == 0);
73 		return cast(ushort[])binary;
74 	}
75 
76 	void attachDevices()
77 	{
78 		emulator.dcpu.updateQueue = new UpdateQueue!Dcpu;
79 		emulator.attachDevice(monitor);
80 		emulator.attachDevice(keyboard);
81 		emulator.attachDevice(clock);
82 		emulator.attachDevice(floppyDrive);
83 	}
84 
85 	override void load(in string[] args)
86 	{
87 		if (args.length > 1)
88 		{
89 			file = args[1];
90 			writefln("loading '%s'", file);
91 		}
92 
93 		fpsHelper.limitFps = true;
94 
95 		emulator = new Emulator!Dcpu();
96 		monitor = new Lem1802!Dcpu;
97 		clock = new GenericClock!Dcpu;
98 		keyboard = new GenericKeyboard!Dcpu;
99 		floppyDrive = new FloppyDrive!Dcpu;
100 		floppyDrive.floppy = new Floppy;
101 		memAnalyzer = new MemoryAnalyzer!Dcpu(&emulator.dcpu);
102 		attachDevices();
103 		
104 		emulator.loadProgram(loadBinary(file));
105 
106 		// ----------------------------- Creating widgets -----------------------------
107 		templateManager.parseFile("dcpu.sdl");
108 
109 		auto mainLayer = context.createWidget("mainLayer");
110 		context.addRoot(mainLayer);
111 	
112 		auto monitorWidget = context.getWidgetById("monitor");
113 		auto texture = new Texture(monitor.bitmap, TextureTarget.target2d, TextureFormat.rgba);
114 		monitorWidget.setProperty!("texture")(texture);
115 		monitorWidget.addEventHandler(delegate bool(Widget widget, KeyPressEvent event){
116 			keyboard.onKey(cast(KeyCode)event.keyCode, event.modifiers, true);
117 			return true;
118 		});
119 		monitorWidget.addEventHandler(delegate bool(Widget widget, KeyReleaseEvent event){
120 			keyboard.onKey(cast(KeyCode)event.keyCode, event.modifiers, false);
121 			return true;
122 		});
123 		monitorWidget.addEventHandler(delegate bool(Widget widget, PointerPressEvent event){
124 			return true;
125 		});
126 		monitorWidget.setProperty!"isFocusable"(true);
127 
128 		auto stepButtonHandler = delegate bool(Widget widget, PointerClickEvent event)
129 		{
130 			step(widget.getPropertyAs!("stepSize", int));
131 			return true;
132 		};
133 
134 		context.getWidgetById("unstep").addEventHandler(stepButtonHandler);
135 		context.getWidgetById("unstep10").addEventHandler(stepButtonHandler);
136 		context.getWidgetById("unstep100").addEventHandler(stepButtonHandler);
137 		context.getWidgetById("unstep1000").addEventHandler(stepButtonHandler);
138 		context.getWidgetById("step").addEventHandler(stepButtonHandler);
139 		context.getWidgetById("step10").addEventHandler(stepButtonHandler);
140 		context.getWidgetById("step100").addEventHandler(stepButtonHandler);
141 		context.getWidgetById("step1000").addEventHandler(stepButtonHandler);
142 
143 		auto speedButtonHandler = delegate bool(Widget widget, PointerClickEvent event)
144 		{
145 			setCpuClockSpeed(cast(uint)(widget.getPropertyAs!("speed", int)));
146 			return true;
147 		};
148 
149 		context.getWidgetById("speed10").addEventHandler(speedButtonHandler);
150 		context.getWidgetById("speed100").addEventHandler(speedButtonHandler);
151 		context.getWidgetById("speed1k").addEventHandler(speedButtonHandler);
152 		context.getWidgetById("speed10k").addEventHandler(speedButtonHandler);
153 		context.getWidgetById("speed100k").addEventHandler(speedButtonHandler);
154 		context.getWidgetById("speed500k").addEventHandler(speedButtonHandler);
155 		context.getWidgetById("speed1m").addEventHandler(speedButtonHandler);
156 
157 		auto runBackwardButton = context.getWidgetById("runback");
158 		runBackwardButton.addEventHandler(delegate bool(Widget widget, PointerClickEvent event){runBackward(); return true;});
159 
160 		auto runForwardButton = context.getWidgetById("run");
161 		runForwardButton.addEventHandler(delegate bool(Widget widget, PointerClickEvent event){runForward(); return true;});
162 
163 		auto pauseButton = context.getWidgetById("pause");
164 		pauseButton.addEventHandler(delegate bool(Widget widget, PointerClickEvent event){pause(); return true;});
165 
166 		auto resetButton = context.getWidgetById("reset");
167 		resetButton.addEventHandler(&reset);
168 
169 		auto disassembleButton = context.getWidgetById("disasm");
170 		disassembleButton.addEventHandler(delegate bool(Widget widget, PointerClickEvent event){disassembleMemory(); return true;});
171 
172 		auto swapButton = context.getWidgetById("swap");
173 		swapButton.addEventHandler(delegate bool(Widget widget, PointerClickEvent event){swapFileEndian(file); return true;});
174 
175 		auto statsButton = context.getWidgetById("stats");
176 		statsButton.addEventHandler(delegate bool(Widget widget, PointerClickEvent event){emulator.stats.print(); return true;});
177 
178 		registerView = context.getWidgetById("registerView");
179 		foreach(i; 0..17) context.createWidget("label", registerView);
180 		printRegisters();
181 
182 
183 		memoryView = new MemoryView!Dcpu(&emulator.dcpu);
184 		auto memoryViewWidget = context.getWidgetById("memoryview");
185 		memoryViewWidget.setProperty!("list", List!dstring)(memoryView);
186 		memoryViewWidget.setProperty!("sliderPos")(0.0);
187 
188 		auto collapseZerosCheck = context.getWidgetById("collapseZeros");
189 		memoryView.collapseZeros = collapseZerosCheck.getPropertyAs!("isChecked", bool);
190 		collapseZerosCheck
191 			.property("isChecked")
192 			.valueChanged
193 			.connect((FlexibleObject a, Variant b)
194 			{
195 				memoryView.collapseZeros = b.get!bool;
196 				updateMemoryView();
197 			});
198 
199 		updateMemoryView();
200 
201 		writeln("\n----------------------------- Load end -----------------------------\n");
202 	}
203 
204 	bool reset(Widget widget, PointerClickEvent event)
205 	{
206 		emulator.reset();
207 		attachDevices();
208 		emulator.loadProgram(loadBinary(file));
209 		printRegisters();
210 		updateMemoryView();
211 
212 		return true;
213 	}
214 
215 	void pause()
216 	{
217 		emulator.dcpu.isRunning = false;
218 	}
219 
220 	void runBackward()
221 	{
222 		emulator.dcpu.isRunning = true;
223 		isRunningForward = false;
224 	}
225 
226 	void runForward()
227 	{
228 		emulator.dcpu.isRunning = true;
229 		isRunningForward = true;
230 	}
231 
232 	void step(long numFrames)
233 	{
234 		if (emulator.dcpu.isRunning) return;
235 		
236 		if (numFrames < 0)
237 			emulator.unstep(cast(ulong)(-numFrames));
238 		else
239 			emulator.step(cast(ulong)(numFrames));
240 
241 		printRegisters();
242 		updateMemoryView();
243 	}
244 
245 	void setCpuClockSpeed(uint clockSpeed)
246 	{
247 		emulator.dcpu.clockSpeed = clockSpeed;
248 	}
249 
250 	override void update(double dt)
251 	{
252 		super.update(dt);
253 
254 		if (emulator.dcpu.isRunning)
255 		{
256 			if (isRunningForward)
257 				emulator.stepCycles(emulator.dcpu.clockSpeed / 60);
258 			else
259 				emulator.unstepCycles(emulator.dcpu.clockSpeed / 60);
260 
261 			printRegisters();
262 			updateMemoryView();
263 		}
264 
265 		monitor.updateFrame();
266 		keyboard.updateFrame();
267 	}
268 
269 	void disassembleMemory()
270 	{
271 		memAnalyzer.buildMemoryMap();
272 
273 		auto file = File(file.setExtension("dis.asm"), "w");
274 
275 		file.lockingTextWriter.put(
276 		disassembleSome(cast(ushort[])emulator.dcpu.mem.observableArray[], memAnalyzer.memoryMap, 0, 0)
277 			.joiner("\n").array
278 		);
279 	}
280 
281 	void printRegisters()
282 	{
283 		with(emulator.dcpu)
284 		{
285 			auto lines = registerView.getPropertyAs!("children", Widget[]);
286 		 	lines[ 0]["text"] = format("PC %04x | [PC] %04x", regs.pc, mem[regs.pc]);
287 		 	lines[ 1]["text"] = format("SP %04x | [SP] %04x", regs.sp, mem[regs.sp]);
288 		 	lines[ 2]["text"] = format("EX %04x | [EX] %04x", regs.ex, mem[regs.ex]);
289 		 	lines[ 3]["text"] = format("IA %04x | [IA] %04x", regs.ia, mem[regs.ia]);
290 		 	lines[ 4]["text"] = format(" A %04x | [ A] %04x", regs.a , mem[regs.a ]);
291 		 	lines[ 5]["text"] = format(" B %04x | [ B] %04x", regs.b , mem[regs.b ]);
292 		 	lines[ 6]["text"] = format(" C %04x | [ C] %04x", regs.c , mem[regs.c ]);
293 		 	lines[ 7]["text"] = format(" X %04x | [ X] %04x", regs.x , mem[regs.x ]);
294 		 	lines[ 8]["text"] = format(" Y %04x | [ Y] %04x", regs.y , mem[regs.y ]);
295 		 	lines[ 9]["text"] = format(" Z %04x | [ Z] %04x", regs.z , mem[regs.z ]);
296 		 	lines[10]["text"] = format(" I %04x | [ I] %04x", regs.i , mem[regs.i ]);
297 		 	lines[11]["text"] = format(" J %04x | [ J] %04x", regs.j , mem[regs.j ]);
298 		 	lines[12]["text"] = format("Ticks: %s", regs.cycles);
299 		 	lines[13]["text"] = "Instructions done";
300 		 	lines[14]["text"] = format("%s", regs.instructions);
301 		 	lines[15]["text"] = "Undo size:";
302 		 	lines[16]["text"] = format("%sb", emulator.undoStackSize);
303 		}
304 	}
305 
306 	void updateMemoryView()
307 	{
308 		memoryView.update();
309 		memoryView.listChangedSignal.emit();
310 	}
311 
312 	override void closePressed()
313 	{
314 		isRunning = false;
315 	}
316 }