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,];