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 }