1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.zmpp.vm;
24
25 import org.zmpp.base.MemoryReadAccess;
26 import org.zmpp.vm.AbstractInstruction.InstructionForm;
27 import org.zmpp.vm.AbstractInstruction.OperandCount;
28
29 /***
30 * The instruction decoder decodes an instruction at a specified address.
31 *
32 * @author Wei-ju Wu
33 * @version 1.0
34 */
35 public class InstructionDecoder {
36
37 /***
38 * The memory access object.
39 */
40 private MemoryReadAccess memaccess;
41
42 /***
43 * The machine state object.
44 */
45 private Machine machineState;
46
47 /***
48 * Constructor.
49 *
50 * @param memaccess the memory access object
51 */
52 public InstructionDecoder(Machine machineState,
53 MemoryReadAccess memaccess) {
54
55 this.memaccess = memaccess;
56 this.machineState = machineState;
57 }
58
59 /***
60 * Decode the instruction at the specified address.
61 *
62 * @param instructionAddress the current instruction's address
63 * @return the instruction at the specified address
64 */
65 public AbstractInstruction decodeInstruction(int instructionAddress) {
66
67 AbstractInstruction info = createBasicInstructionInfo(instructionAddress);
68 int currentAddress = extractOperands(info, instructionAddress);
69 if (info.getInstructionForm() == InstructionForm.VARIABLE
70 && info.getOperandCount() == OperandCount.C2OP) {
71
72 AbstractInstruction info2 =
73 new LongInstruction(machineState, OperandCount.VAR, info.getOpcode());
74
75 for (int i = 0; i < info.getNumOperands(); i++) {
76
77 info2.addOperand(info.getOperand(i));
78 }
79 info = info2;
80 }
81 currentAddress = extractStoreVariable(info, currentAddress);
82 currentAddress = extractBranchOffset(info, currentAddress);
83 info.setLength(currentAddress - instructionAddress);
84 return info;
85 }
86
87
88
89
90
91 /***
92 * Create the basic information about the current instruction to be
93 * decoded. The returned object contains the instruction form, the
94 * operand count type and possibly, the opcode, if it is not an extended
95 * opcode.
96 *
97 * @param the instruction's start address
98 * @return a DefaultInstructionInfo object with basic information
99 */
100 private AbstractInstruction createBasicInstructionInfo(int instructionAddress) {
101
102 OperandCount operandCount;
103 int opcode;
104 short firstByte = memaccess.readUnsignedByte(instructionAddress);
105
106
107 if (0x00 <= firstByte && firstByte <= 0x7f) {
108
109 opcode = firstByte & 0x1f;
110 operandCount = OperandCount.C2OP;
111 return new LongInstruction(machineState, opcode);
112
113 } else if (0x80 <= firstByte && firstByte <= 0xbf) {
114
115 opcode = firstByte & 0x0f;
116 operandCount = (firstByte >= 0xb0) ? OperandCount.C0OP :
117 OperandCount.C1OP;
118 if (operandCount == OperandCount.C0OP) {
119
120
121
122 if (opcode == PrintLiteralInstruction.OP_PRINT
123 || opcode == PrintLiteralInstruction.OP_PRINT_RET) {
124
125 return new PrintLiteralInstruction(machineState, opcode, memaccess,
126 instructionAddress);
127 }
128 return new Short0Instruction(machineState, opcode);
129 }
130 else
131 return new Short1Instruction(machineState, opcode);
132
133 } else {
134
135 opcode = firstByte & 0x1f;
136 operandCount = (firstByte >= 0xe0) ? OperandCount.VAR : OperandCount.C2OP;
137 return new VariableInstruction(machineState, operandCount, opcode);
138 }
139 }
140
141 /***
142 * Extracts the operands from the instruction data. At this step of
143 * decoding the some basic information about the instruction is available
144 * and could be used for extraction of parameters.
145 *
146 * @param info the instruction info object to write to
147 * @param instructionAddress the instruction address
148 * @return the current address in decoding
149 */
150 private int extractOperands(AbstractInstruction info, int instructionAddress) {
151
152 int currentAddress = instructionAddress;
153
154 if (info.getInstructionForm() == InstructionForm.SHORT) {
155
156 if (info.getOperandCount() == OperandCount.C1OP) {
157
158 short firstByte = memaccess.readUnsignedByte(instructionAddress);
159
160
161
162
163
164 byte optype = (byte) ((firstByte & 0x30) >> 4);
165
166
167 currentAddress = extractOperand(info, optype, instructionAddress + 1);
168
169 } else {
170
171
172 currentAddress = instructionAddress + 1;
173 }
174 } else if (info.getInstructionForm() == InstructionForm.LONG) {
175
176 short firstByte = memaccess.readUnsignedByte(instructionAddress);
177
178 byte optype1 = ((firstByte & 0x40) > 0) ? Operand.TYPENUM_VARIABLE :
179 Operand.TYPENUM_SMALL_CONSTANT;
180 byte optype2 = ((firstByte & 0x20) > 0) ? Operand.TYPENUM_VARIABLE :
181 Operand.TYPENUM_SMALL_CONSTANT;
182 currentAddress = extractOperand(info, optype1, instructionAddress + 1);
183 currentAddress = extractOperand(info, optype2, currentAddress);
184
185 } else if (info.getInstructionForm() == InstructionForm.VARIABLE){
186
187 short secondByte = memaccess.readUnsignedByte(instructionAddress + 1);
188
189
190 currentAddress = instructionAddress + 2;
191 currentAddress = extractOperandsWithTypeByte(info, secondByte,
192 currentAddress);
193 }
194 return currentAddress;
195 }
196
197 /***
198 * Extract operands for the given optype byte value starting at the given
199 * decoding address. This is outfactored in order to be called at least two
200 * times. The generated operands are added to the specified operand list.
201 *
202 * @param info the InstructionInfo to add to
203 * @param optypeByte the optype byte
204 * @param currentAddress the current decoding address
205 * @return the new decoding address after extracting the operands
206 */
207 private int extractOperandsWithTypeByte(AbstractInstruction info,
208 int optypeByte, int currentAddress) {
209
210 int nextAddress = currentAddress;
211 int oldNumOperands;
212 byte optype = (byte) ((optypeByte >> 6) & 0x03);
213
214 for (int i = 0; i < 4; i++) {
215
216 optype = (byte) ((optypeByte >> ((3 - i) * 2)) & 0x03);
217 oldNumOperands = info.getNumOperands();
218 nextAddress = extractOperand(info, optype, nextAddress);
219 if (info.getNumOperands() == oldNumOperands) break;
220 }
221 return nextAddress;
222 }
223
224 /***
225 * Extracts an operands from the current address with the specified operand
226 * type number. If the type is unknown or OMITTED, no operand will be
227 * added and the returned address will be equal to currentAddress.
228 *
229 * @param info the instruction info to add to
230 * @param optype the operand type number
231 * @param currentAddress the current address in the instruction
232 * @return the next address
233 */
234 private int extractOperand(AbstractInstruction info, byte optype,
235 int currentAddress) {
236
237
238
239
240 int nextAddress = currentAddress;
241 if (optype == Operand.TYPENUM_LARGE_CONSTANT) {
242
243 info.addOperand(new Operand(optype,
244 memaccess.readShort(nextAddress)));
245 nextAddress += 2;
246
247 } else if (optype == Operand.TYPENUM_VARIABLE
248 || optype == Operand.TYPENUM_SMALL_CONSTANT) {
249
250 info.addOperand(new Operand(optype,
251 memaccess.readUnsignedByte(nextAddress)));
252
253 nextAddress += 1;
254 }
255 return nextAddress;
256 }
257
258 /***
259 * Extracts a store variable if this instruction has one.
260 *
261 * @param info the instruction info
262 * @param currentAddress the current address in the decoding
263 * @return the current decoding address after extraction
264 */
265 private int extractStoreVariable(AbstractInstruction info, int currentAddress) {
266
267 if (info.storesResult()) {
268
269 info.setStoreVariable(memaccess.readUnsignedByte(currentAddress));
270 return currentAddress + 1;
271 }
272 return currentAddress;
273 }
274
275 /***
276 * Extracts the branch offset if this instruction is a branch.
277 *
278 * @param info the instruction info object
279 * @param currentAddress the current address in decoding processing
280 * @return the current decoding address after extraction
281 */
282 private int extractBranchOffset(AbstractInstruction info, int currentAddress) {
283
284 if (info.isBranch()) {
285
286 short offsetByte1 = memaccess.readUnsignedByte(currentAddress);
287 info.setBranchIfTrue((offsetByte1 & 0x80) > 0);
288
289
290
291 if ((offsetByte1 & 0x40) > 0) {
292
293 info.setBranchOffset((short) (offsetByte1 & 0x3f));
294 return currentAddress + 1;
295
296 } else {
297
298 short offsetByte2 = memaccess.readUnsignedByte(currentAddress + 1);
299 short offset;
300
301 if ((offsetByte1 & 0x20) != 0) {
302
303 offset = (short)
304 ((0xC000 | ((offsetByte1 << 8) | (offsetByte2 & 0xff))));
305
306 } else {
307
308 offset = (short)
309 (((offsetByte1 & 0x3f) << 8) | (offsetByte2 & 0xff));
310 }
311
312
313 info.setBranchOffset(offset);
314 return currentAddress + 2;
315 }
316 }
317 return currentAddress;
318 }
319 }