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 module emulator.dcpu.execution;
8 
9 import std.stdio;
10 
11 import emulator.dcpu.dcpu;
12 import emulator.dcpu.devices.idevice;
13 import emulator.dcpu.constants;
14 import emulator.dcpu.disassembler;
15 import emulator.dcpu.instruction;
16 import emulator.utils.undoproxy;
17 
18 void execute(Cpu)(ref Cpu dcpu, ref Instruction instr)
19 {
20 	if (instr.operands == 2)
21 		dcpu.basicInstruction(instr);
22 	else
23 		dcpu.specialInstruction(instr);
24 }
25 
26 /// Performs basic instruction.
27 void basicInstruction(Cpu)(ref Cpu dcpu, ref Instruction instr)
28 {
29 	ushort pc = cast(ushort)(dcpu.regs.pc + 1); // pass opcode
30 	ushort sp = dcpu.regs.sp;
31 
32 	ushort opcode = instr.opcode;
33 
34 	OperandAccess aa = dcpu.getOperandA(instr.operandA, pc, sp); // will increase pc if reads next word
35 
36 	OperandAccess ba = dcpu.getOperandB(instr.operandB, pc, sp); // will increase pc if reads next word
37 
38 	dcpu.regs.pc = pc;
39 	dcpu.regs.sp = sp;
40 
41 	dcpu.regs.cycles = dcpu.regs.cycles + basicCycles[opcode] + nextWordOperands[instr.operandA] + nextWordOperands[instr.operandB];
42 	
43 	ushort a = aa.get();
44 	ushort b = ba.get();
45 
46 	uint result;
47 
48 	with(dcpu) switch (opcode)
49 	{
50 		case 0x00 : assert(false); // Special opcode. Execution never goes here.
51 		case SET: result = a; break;
52 		case ADD: result = b + a; regs.ex = result >> 16; break;
53 		case SUB: result = b - a; regs.ex = (a > b) ? 0xFFFF : 0; break;
54 		case MUL: result = b * a; regs.ex = result >> 16; break;
55 		case MLI: result = cast(short)a * cast(short)b; regs.ex = result >> 16; break;
56 		case DIV: if (a==0){regs.ex = 0; result = 0;}
57 					else {result = b/a; regs.ex = ((b << 16)/a) & 0xFFFF;} break;
58 		case DVI: if (a==0){regs.ex = 0; result = 0;}
59 					else {
60 						result = cast(short)b/cast(short)a;
61 						regs.ex = ((cast(short)b << 16)/cast(short)a) & 0xFFFF;
62 					} break;
63 		case MOD: result = a == 0 ? 0 : b % a; break;
64 		case MDI: result = a == 0 ? 0 : cast(short)b % cast(short)a; break;
65 		case AND: result = a & b; break;
66 		case BOR: result = a | b; break;
67 		case XOR: result = a ^ b; break;
68 		case SHR: result = b >> a; regs.ex = ((b<<16)>>a) & 0xffff; break;
69 		case ASR: result = cast(short)b >>> a;
70 					regs.ex = ((b<<16)>>>a) & 0xffff; break;
71 		case SHL: result = b << a; regs.ex = ((b<<a)>>16) & 0xffff; break;
72 		case IFB: if ((b & a)==0) dcpu.skipIfs(); return;
73 		case IFC: if ((b & a)!=0) dcpu.skipIfs(); return;
74 		case IFE: if (b != a) dcpu.skipIfs(); return;
75 		case IFN: if (b == a) dcpu.skipIfs(); return;
76 		case IFG: if (b <= a) dcpu.skipIfs(); return;
77 		case IFA: if (cast(short)b <= cast(short)a) dcpu.skipIfs(); return;
78 		case IFL: if (b >= a) dcpu.skipIfs(); return;
79 		case IFU: if (cast(short)b >= cast(short)a) dcpu.skipIfs(); return;
80 		case ADX: result = b + a + regs.ex;
81 					regs.ex = (result >> 16) ? 1 : 0; break;
82 		case SBX: result = b - a + regs.ex; regs.ex = 0;
83 					if (ushort over = result >> 16)
84 						regs.ex = (over == 0xFFFF) ? 0xFFFF : 0x0001;
85 					break;
86 		case STI: ba.set(a); regs.i = cast(ushort)(regs.i + 1); regs.j = cast(ushort)(regs.j + 1); return;
87 		case STD: ba.set(a); regs.i = cast(ushort)(regs.i - 1); regs.j = cast(ushort)(regs.j - 1); return;
88 		default: {} //Invalid opcode
89 	}
90 
91 	if (instr.operandB < 0x1F)
92 	{
93 		ba.set(result & 0xFFFF);
94 	}
95 }
96 
97 /// Performs special instruction.
98 void specialInstruction(Cpu)(ref Cpu dcpu, ref Instruction instr)
99 {
100 	ushort opcode = instr.opcode;
101 
102 	ushort pc = cast(ushort)(dcpu.regs.pc + 1);
103 	ushort sp = dcpu.regs.sp;
104 
105 	OperandAccess aa = dcpu.getOperandA(instr.operandA, pc, sp);
106 
107 	dcpu.regs.cycles = dcpu.regs.cycles + specialCycles[opcode] + nextWordOperands[instr.operandA];
108 	dcpu.regs.pc = pc;
109 	dcpu.regs.sp = sp;
110 	
111 	ushort a = aa.get();
112 
113 	with(dcpu) switch (opcode)
114 	{
115 		case JSR: dcpu.push(regs.pc); regs.pc = a; break;
116 		case INT: dcpu.triggerInterrupt(a); break;
117 		case IAG: aa.set(regs.ia); break;
118 		case IAS: regs.ia = a; break;
119 		case RFI: regs.queueInterrupts = false;
120 					regs.a = dcpu.pop();
121 					regs.pc = dcpu.pop();
122 					break;
123 		case IAQ: regs.queueInterrupts = a > 0; break;
124 		case HWN: aa.set(numDevices); break;
125 		case HWQ: dcpu.queryHardwareInfo(a); break;
126 		case HWI: dcpu.sendHardwareInterrupt(a); break;
127 		default : {}
128 	}
129 }
130 
131 /// Pushes value onto stack decreasing reg_sp.
132 void push(Cpu)(ref Cpu dcpu, ushort value)
133 {
134 	dcpu.regs.sp = cast(ushort)(dcpu.regs.sp - 1);
135 	dcpu.mem[dcpu.regs.sp] = value;
136 }
137 
138 /// Pops value from stack increasing reg_sp.
139 ushort pop(Cpu)(ref Cpu dcpu)
140 {
141 	dcpu.regs.sp = cast(ushort)(dcpu.regs.sp + 1);
142 	return dcpu.mem[(dcpu.regs.sp - 1) & 0xFFFF];
143 }
144 
145 /// Sets A, B, C, X, Y registers to information about hardware deviceIndex
146 void queryHardwareInfo(Cpu)(ref Cpu dcpu, ushort deviceIndex)
147 {
148 	if (auto device = deviceIndex in dcpu.devices)
149 	{
150 		dcpu.regs.a = device.hardwareId & 0xFFFF;
151 		dcpu.regs.b = device.hardwareId >> 16;
152 		dcpu.regs.c = device.hardwareVersion;
153 		dcpu.regs.x = device.manufacturer & 0xFFFF;
154 		dcpu.regs.y = device.manufacturer >> 16;
155 	}
156 	else
157 	{
158 		dcpu.regs[0..5] = [0, 0, 0, 0, 0];
159 	}
160 }
161 
162 /// Sends an interrupt to hardware deviceIndex
163 void sendHardwareInterrupt(Cpu)(ref Cpu dcpu, ushort deviceIndex)
164 {
165 	if (auto device = deviceIndex in dcpu.devices)
166 	{
167 		dcpu.regs.cycles = dcpu.regs.cycles + device.handleInterrupt();
168 	}
169 }
170 
171 /// Adds interrupt with message 'message' to dcpu.intQueue or starts burning DCPU if queue grows bigger than 256
172 void triggerInterrupt(Cpu)(ref Cpu dcpu, ushort message)
173 {
174 	if (dcpu.intQueue.isFull)
175 	{
176 		dcpu.isBurning = true;
177 	}
178 	else
179 	{
180 		dcpu.intQueue.pushBack(message);
181 	}
182 }
183 
184 /// Handles interrupt from interrupt queue if reg_ia != 0 && intQueue.length > 0
185 /// Handles interrupts only when interrupt queuing is disabled.
186 /// It may be enabled by interrupt handler or manually in time critical code.
187 void handleInterrupt(Cpu)(ref Cpu dcpu)
188 {
189 	if (dcpu.intQueue.empty || dcpu.regs.queueInterrupts) return;
190 
191 	ushort message = dcpu.intQueue.popFront();
192 
193 	if (dcpu.regs.ia != 0)
194 	{
195 		dcpu.regs.queueInterrupts = true;
196 
197 		push(dcpu, dcpu.regs.pc);
198 		push(dcpu, dcpu.regs.a);
199 
200 		dcpu.regs.pc = dcpu.regs.ia;
201 		dcpu.regs.a = message;
202 	}
203 }
204 
205 /++
206 + Skips instructions if conditional opcode was failed.
207 +
208 + The conditional opcodes take one cycle longer to perform if the test fails.
209 	+ When they skip a conditional instruction, they will skip an additional
210 	+ instruction at the cost of one extra cycle. This continues until a non-
211 	+ conditional instruction has been skipped. This lets you easily chain
212 	+ conditionals. Interrupts are not triggered while the DCPU-16 is skipping.
213 +/
214 void skipIfs(Cpu)(ref Cpu dcpu)
215 {
216 	ushort pc = dcpu.regs.pc;
217 
218 	while ((dcpu.mem[pc] & 0x1F) >= IFB && (dcpu.mem[pc] & 0x1F) <= IFU)
219 	{
220 		dcpu.skip(pc);
221 	}
222 
223 	dcpu.skip(pc);
224 
225 	dcpu.regs.pc = pc;
226 }
227 
228 void skip(Cpu)(ref Cpu dcpu, ref ushort pc)
229 {
230 	ushort instr = dcpu.mem[pc];
231 	ushort opcode = instr & 0x1F;
232 
233 	if (opcode != 0) //basic
234 	{
235 		auto aNext = nextWordOperands[instr >> 10];
236 		auto bNext = nextWordOperands[(instr >> 5) & 0x1F];
237 		
238 		pc += 1 + aNext + bNext;
239 	}
240 	else //special
241 	{
242 		auto aNext = nextWordOperands[instr >> 10];
243 		pc += 1 + aNext;
244 	}
245 
246 	dcpu.regs.inc!"cycles";
247 }