1 module dcpu16.emulator; 2 3 import std.string: format; 4 import std.exception: enforce; 5 6 version = CPUDebuggingMethods; 7 8 alias Memory = ushort[]; 9 10 class Computer 11 { 12 import dcpu16.emulator.cpu; 13 import dcpu16.emulator.idevice; 14 15 Memory mem; 16 CPU cpu; 17 IDevice[] devices; 18 19 this() pure 20 { 21 mem.length = 0x10000; 22 cpu = CPU(this); 23 } 24 25 void attachDevice(IDevice dev) pure 26 { 27 devices ~= dev; 28 29 enforce(devices.length <= short.max); 30 } 31 32 void reset() 33 { 34 cpu.reset(); 35 36 foreach(ref m; mem) m = 0; 37 foreach(ref d; devices) d.reset(); 38 } 39 40 void load(in ubyte[] from, bool wrongEndianness) 41 { 42 import std.bitmanip; 43 44 enforce(from.length * 2 <= mem.length); 45 enforce(from.length % 2 == 0); 46 47 for(ushort i = 0; i < from.length; i+=2) 48 { 49 ubyte[2] d = from[i .. i+2]; 50 ushort temp = wrongEndianness 51 ? bigEndianToNative!ushort(d) 52 : littleEndianToNative!ushort(d); 53 54 mem[i/2] = temp; 55 } 56 } 57 58 void load(in ushort[] from) pure 59 { 60 assert(from.length <= mem.length); 61 62 mem[0 .. from.length] = from; 63 } 64 65 string memDump(ushort from) const 66 { 67 string ret; 68 69 foreach(w; mem[from .. from+16]) 70 ret ~= format("%04x ", w); 71 72 return ret; 73 } 74 75 string machineState() const pure 76 { 77 import dcpu16.asm_.decode: explainInstruction; 78 79 return format("Asm: %s\nInstruction: %s\n%s\n%s", 80 explainInstruction(mem, cpu.regs.PC, cpu.getCurrInstruction), 81 cpu.getCurrInstruction.toString, 82 cpu.regsToString, 83 cpu.intQueueDump, 84 ); 85 } 86 } 87 88 unittest 89 { 90 auto c = new Computer(); 91 c.mem[0 .. 3] = 92 [ 93 0x8801, // set a, 1 94 0x9002, // add a, 3 :loop 95 0x8b81, // set pc, loop 96 ]; 97 98 assert(c.mem[0] != 0, "First instruction is null"); 99 100 c.cpu.reset; 101 102 foreach(i; 0 .. 16) 103 c.cpu.step; 104 105 assert(c.cpu.regs.A == 0x19); 106 assert(c.cpu.regs.PC == 0x02); 107 108 c.reset; 109 assert(c.mem[0] == 0); 110 } 111 112 unittest 113 { 114 auto comp = new Computer(); 115 comp.load = 116 [ 117 // high-nerd.dasm16 118 0x7c41, 0x01f4, // SET C, 500 ; C = 500 119 0x7c42, 0x01f3, // ADD C, 499 ; C = 999 120 0x7c43, 0x0063, // SUB C, 99 ; C = 900 121 0x8c44, // MUL C, 2 ; C = 1800 122 0x8001, // SET A, 0xFFFF ; C = 1800, A = 0xFFFF 123 0x0803, // SUB A, C ; C = 1800, A = 0xF8F8 (FIXME: wtf?! 0xffff - 1800 == 0xf8f7) 124 0x0041, // SET C, A ; C = -1800, A = 0xF8F8 125 0x8401, // SET A, 0 ; C = -1800 126 0x8c45, // MLI C, 2 ; C = -3600 127 0x9047, // DVI C, 3 ; C = -1200 (0xFB50) 128 ]; 129 130 comp.cpu.reset; 131 132 with(comp.cpu) 133 with(regs) 134 { 135 import std.conv: to; 136 137 step; assert(c == 500, c.to!string); 138 step; assert(c == 999, c.to!string); 139 step; assert(c == 900, c.to!string); 140 step; assert(c == 1800, c.to!string); 141 step; assert(c == 1800 && A == 0xFFFF); 142 step; // SUB A, C 143 assert(c == 1800); 144 assert(A == 0xF8F7); // FIXME: should be 0xf8f8, see SUB comment above 145 A = 0xf8f8; 146 step; 147 assert(c == cast(ushort) -1800); 148 assert(A == 0xF8F8); 149 step; assert(c == cast(ushort) -1800); 150 step; assert(c == cast(ushort) -3600); 151 step; assert(c == cast(ushort) -1200); 152 } 153 }