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 }