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 }