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 emulator.dcpu.instruction; 8 9 import std.conv : to; 10 import std..string : format; 11 12 import emulator.dcpu.constants; 13 14 struct Instruction 15 { 16 ubyte opcode; 17 ubyte operandA; 18 ubyte operandB; 19 ubyte size; // in words 1-3 20 ubyte operands; // 2, 1, 0 21 ushort pc; 22 ushort[2] nextWords; 23 24 string toString() 25 { 26 if (operands == 2) 27 { 28 auto aNext = nextWordOperands[operandA]; 29 auto bNext = nextWordOperands[operandB]; 30 return format("%04x %s %s %s", pc, basicOpcodeNames[opcode], 31 decodeOperand!false(operandB, nextWords[aNext]), 32 decodeOperand!true(operandA, nextWords[0])); 33 } 34 else 35 { 36 auto aNext = nextWordOperands[operandA]; 37 return format("%04x %s %s", pc, specialOpcodeNames[opcode], 38 decodeOperand!true(operandA, nextWords[0])); 39 } 40 } 41 } 42 43 struct InstructionInfo 44 { 45 bool isConditional; 46 bool isValid; 47 bool modifiesProgramCounter; 48 } 49 50 InstructionInfo instructionInfo(ref Instruction instr) 51 { 52 InstructionInfo info; 53 info.isConditional = isConditionalInstruction(instr); 54 info.isValid = isValidInstruction(instr); 55 //if (instr.operands == 2 && instr.operandB == ) 56 57 return info; 58 } 59 60 bool isValidInstruction(ref Instruction instr) 61 { 62 if(instr.operands == 2) 63 return isValidBasicOpcode[instr.opcode]; 64 else if(instr.operands == 1) 65 return isValidSpecialOpcode[instr.opcode]; 66 else 67 return false; 68 } 69 70 bool isConditionalInstruction(ref Instruction instr) 71 { 72 return instr.operands == 2 && 73 instr.opcode >= IFB && 74 instr.opcode <= IFU; 75 } 76 77 Instruction fetchNext(Cpu)(ref Cpu dcpu) 78 { 79 return fetchAt(dcpu, dcpu.regs.pc); 80 } 81 82 alias MemoryAccessor = ushort delegate(ushort); 83 84 Instruction fetchAt(Cpu)(ref Cpu dcpu, ushort address) 85 { 86 Instruction result; 87 88 ushort pc = address; 89 90 result.pc = pc; 91 ushort instr = dcpu.mem[pc++]; 92 result.nextWords[0] = dcpu.mem[pc++]; 93 result.nextWords[1] = dcpu.mem[pc++]; 94 95 result.opcode = instr & 0b0000000000011111; 96 ++result.size; 97 98 if (result.opcode != 0) 99 { 100 result.operandA = (instr & 0b1111110000000000) >> 10; 101 if (nextWordOperands[result.operandA]) ++result.size; 102 103 result.operandB = (instr & 0b0000001111100000) >> 5; 104 if (nextWordOperands[result.operandB]) ++result.size; 105 106 result.operands = 2; 107 108 return result; 109 } 110 111 result.opcode = (instr & 0b0000001111100000) >> 5; 112 113 if (result.opcode != 0) 114 { 115 result.operandA = (instr & 0b1111110000000000) >> 10; 116 if (nextWordOperands[result.operandA]) ++result.size; 117 118 result.operands = 1; 119 120 return result; 121 } 122 123 result.operands = 0; 124 125 return result; 126 } 127 128 OperandAccess getOperandA(Cpu)(ref Cpu dcpu, ushort operandBits, ref ushort pc, ref ushort sp) 129 { 130 return getOperandValue!true(dcpu, operandBits, pc, sp); 131 } 132 133 OperandAccess getOperandB(Cpu)(ref Cpu dcpu, ushort operandBits, ref ushort pc, ref ushort sp) 134 { 135 return getOperandValue!false(dcpu, operandBits, pc, sp); 136 } 137 138 /// Extracts operand value from a dcpu 139 OperandAccess getOperandValue(bool isA, Cpu)(ref Cpu dcpu, ushort operandBits, ref ushort pc, ref ushort sp) 140 in 141 { 142 assert(operandBits <= 0x3F, "operand must be lower than 0x40"); 143 static if (!isA) 144 assert(operandBits <= 0x1F); 145 } 146 body 147 { 148 with(dcpu) switch(operandBits) 149 { 150 case 0x00: .. case 0x07: // register 151 return dcpu.regAccess(operandBits); 152 case 0x08: .. case 0x0F: // [register] 153 return dcpu.memAccess(regs[operandBits & 7]); 154 case 0x10: .. case 0x17: // [register + next word] 155 return dcpu.memAccess((regs[operandBits & 7] + mem[pc++]) & 0xFFFF); 156 case 0x18: // PUSH / POP 157 static if (isA) 158 return dcpu.memAccess(sp++); 159 else 160 return dcpu.memAccess(--sp); 161 case 0x19: // [SP] / PEEK 162 return dcpu.memAccess(sp); 163 case 0x1a: // [SP + next word] 164 return dcpu.memAccess(cast(ushort)(sp + mem[pc++])); 165 case 0x1b: // SP 166 return dcpu.regAccess(8); 167 case 0x1c: // PC 168 return dcpu.regAccess(9); 169 case 0x1d: // EX 170 return dcpu.regAccess(10); 171 case 0x1e: // [next word] 172 return dcpu.memAccess(mem[pc++]); 173 case 0x1f: // next word 174 return dcpu.memAccess(pc++); 175 default: // 0xffff-0x1e (-1..30) (literal) (only for a) 176 return litAccess(literals[operandBits & 0x1F]); 177 } 178 } 179 180 alias LiteralDecoder = string delegate(ushort); 181 182 string delegate(ushort) plainLitDecoder; 183 184 static this() 185 { 186 plainLitDecoder = delegate string(ushort literal) 187 { 188 return literal > 15 ? format("%#04x", literal) : to!string(literal); 189 }; 190 } 191 192 string decodeOperand(bool isA)(ushort operand, lazy ushort nextWord, LiteralDecoder literalDecoder = plainLitDecoder) 193 { 194 switch(operand) 195 { 196 case 0x00: .. case 0x07: // register 197 return registerNames[operand]; 198 case 0x08: .. case 0x0f: // [register] 199 return "["~registerNames[operand - 0x08]~"]"; 200 case 0x10: .. case 0x17: // [register + next word] 201 return "["~registerNames[operand - 0x10]~" + "~literalDecoder(nextWord)~"]"; 202 case 0x18: // PUSH / POP 203 static if (isA) return "POP"; else return "PUSH"; 204 case 0x19: // [SP] / PEEK 205 return "[SP]"; 206 case 0x1a: // [SP + next word] 207 return "[SP + "~literalDecoder(nextWord)~"]"; 208 case 0x1b: // SP 209 return "SP"; 210 case 0x1c: // PC 211 return "PC"; 212 case 0x1d: // EX 213 return "EX"; 214 case 0x1e: // [next word] 215 return "["~literalDecoder(nextWord)~"]"; 216 case 0x1f: // next word 217 return literalDecoder(nextWord); 218 default: // 0xffff-0x1e (-1..30) (literal) (only for a) 219 return literalDecoder(cast(ushort)(operand - 0x21)); 220 } 221 } 222 223 struct OperandAccess 224 { 225 ushort delegate() get; 226 ushort delegate(ushort) set; 227 } 228 229 OperandAccess memAccess(Cpu)(ref Cpu dcpu, ushort memLocation) 230 { 231 return OperandAccess( 232 {return dcpu.mem[memLocation];}, 233 (ushort value){return dcpu.mem[memLocation] = value;} 234 ); 235 } 236 237 OperandAccess regAccess(Cpu)(ref Cpu dcpu, ushort regLocation) 238 { 239 return OperandAccess( 240 {return dcpu.regs[regLocation];}, 241 (ushort value){return dcpu.regs[regLocation] = value;} 242 ); 243 } 244 245 OperandAccess litAccess(ushort literal) 246 { 247 return OperandAccess( 248 {return literal;}, 249 (ushort value){return literal;} // illegal. Fails silently 250 ); 251 } 252 253 // true if operand can be modified by opcode 254 static bool[64] isOperandRegister = 255 [1,1,1,1,1,1,1,1, // A-J 256 0,0,0,0,0,0,0,0, 257 0,0,0,0,0,0,0,0, 258 0,0,1,1,1,0,0,0, // SP, PC, EX 259 0,0,0,0,0,0,0,0, 260 0,0,0,0,0,0,0,0, 261 0,0,0,0,0,0,0,0, 262 0,0,0,0,0,0,0,0,]; 263 264 static bool[64] isOperandImmediate = 265 [0,0,0,0,0,0,0,0, 266 0,0,0,0,0,0,0,0, 267 0,0,0,0,0,0,0,0, 268 0,0,0,0,0,0,1,1, 269 1,1,1,1,1,1,1,1, 270 1,1,1,1,1,1,1,1, 271 1,1,1,1,1,1,1,1, 272 1,1,1,1,1,1,1,1,]; 273 274 // true if operand can be modified by opcode or 275 // just by getting (like PUSH, POP modifies SP) 276 static bool[64] canOperandModifyRegister = 277 [1,1,1,1,1,1,1,1, // !-J 278 0,0,0,0,0,0,0,0, 279 0,0,0,0,0,0,0,0, 280 1,1,1,1,1,0,0,0, // --SP, SP++, SP, PC, EX 281 0,0,0,0,0,0,0,0, 282 0,0,0,0,0,0,0,0, 283 0,0,0,0,0,0,0,0, 284 0,0,0,0,0,0,0,0,];