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 java.util.ArrayList;
26 import java.util.List;
27
28 /***
29 * This class represents can be considered as a mutable value object, which
30 * basically stores an instruction's information in order to restrict the
31 * Instruction class's responsibility to executing logic.
32 *
33 * This information will be incrementally added by the decoder, therefore
34 * there are setter methods to add information.
35 *
36 * @author Wei-ju Wu
37 * @version 1.0
38 */
39 public abstract class AbstractInstruction implements Instruction {
40
41 /***
42 * The constant for false.
43 */
44 public static final short FALSE = 0;
45
46 /***
47 * The constant for true.
48 */
49 public static final short TRUE = 1;
50
51 /***
52 * The available instruction forms.
53 */
54 public enum InstructionForm { LONG, SHORT, VARIABLE }
55
56 /***
57 * The available operand count types.
58 */
59 public enum OperandCount { C0OP, C1OP, C2OP, VAR }
60
61 /***
62 * The opcode.
63 */
64 private int opcode;
65
66 /***
67 * The operands.
68 */
69 private List<Operand> operands;
70
71 /***
72 * The store variable.
73 */
74 private int storeVariable;
75
76 /***
77 * If this is a branch instruction, this flag indicates whether to branch
78 * if the test condition is false or true.
79 */
80 private boolean branchIfConditionTrue;
81
82 /***
83 * The branch offset.
84 */
85 private short branchOffset;
86
87 /***
88 * The instruction length in bytes.
89 */
90 private int length;
91
92 /***
93 * The machine state.
94 */
95 private Machine machineState;
96
97 /***
98 * Constructor.
99 *
100 * @param machineState a reference to the machine state
101 * @param opcode the opcode
102 */
103 public AbstractInstruction(Machine machineState, int opcode) {
104
105 this.opcode = opcode;
106 this.machineState = machineState;
107 this.operands = new ArrayList<Operand>();
108 this.branchIfConditionTrue = true;
109 }
110
111 /***
112 * Returns the reference to the machine state.
113 *
114 * @return the machine state
115 */
116 protected Machine getMachine() {
117
118 return machineState;
119 }
120
121 /***
122 * Returns the instruction's opcode.
123 *
124 * @return the opcode
125 */
126 public int getOpcode() { return opcode; }
127
128 /***
129 * Returns the instruction's form.
130 *
131 * @return the instruction form
132 */
133 public abstract InstructionForm getInstructionForm();
134
135 /***
136 * Returns the instruction's operand count type.
137 *
138 * @return the operand count type
139 */
140 public abstract OperandCount getOperandCount();
141
142 /***
143 * Returns the operand at the specified position.
144 *
145 * @param operandNum the operand number, starting with 0 as the first operand.
146 * @return the specified operand
147 */
148 public Operand getOperand(int operandNum) {
149
150 return operands.get(operandNum);
151 }
152
153 /***
154 * Returns the number of operands.
155 *
156 * @return the number of operands
157 */
158 public int getNumOperands() {
159
160 return operands.size();
161 }
162
163 /***
164 * Returns the instruction's store variable.
165 *
166 * @return the store variable
167 */
168 public int getStoreVariable() { return storeVariable; }
169
170 /***
171 * Returns the branch offset.
172 *
173 * @return the branch offset
174 */
175 public short getBranchOffset() { return branchOffset; }
176
177 /***
178 * Returns the instruction's length in bytes.
179 *
180 * @return the instruction length
181 */
182 public int getLength() { return length; }
183
184 /***
185 * Sets the instruction's opcode.
186 *
187 * @param opcode the opcode
188 */
189 public void setOpcode(int opcode) { this.opcode = opcode; }
190
191 /***
192 * Adds an operand to this object.
193 *
194 * @param operand the operand to add
195 */
196 public void addOperand(Operand operand) { this.operands.add(operand); }
197
198 /***
199 * Sets the store variable.
200 *
201 * @param var the store variable
202 */
203 public void setStoreVariable(int var) { this.storeVariable = var; }
204
205 /***
206 * Sets the branch offset.
207 *
208 * @param offset the branch offset
209 */
210 public void setBranchOffset(short offset) { this.branchOffset = offset; }
211
212 /***
213 * Sets the branch if condition true flag.
214 *
215 * @param flag the branch if condition true flag
216 */
217 public void setBranchIfTrue(boolean flag) { branchIfConditionTrue = flag; }
218
219 /***
220 * Sets the instruction's length in bytes.
221 *
222 * @param length the length in bytes
223 */
224 public void setLength(int length) { this.length = length; }
225
226 /***
227 * Returns true, if this instruction stores a result, false, otherwise.
228 *
229 * @return true if a result is stored, false otherwise
230 */
231 public boolean storesResult() {
232
233 return false;
234 }
235
236 /***
237 * Returns true, if this instruction is a branch, false, otherwise.
238 *
239 * @return true if branch, false otherwise
240 */
241 public boolean isBranch() {
242
243 return false;
244 }
245
246 /***
247 * Returns true if this is a branch condition and the branch is executed
248 * if the test condition is true, false otherwise.
249 *
250 * @return true if the branch is executed on a true test condition
251 */
252 public boolean branchIfTrue() {
253
254 return branchIfConditionTrue;
255 }
256
257 /***
258 * Converts the specified value into a signed value, depending on the
259 * type of the operand. If the operand is LARGE_CONSTANT or VARIABLE,
260 * the value is treated as a 16 bit signed integer, if it is SMALL_CONSTANT,
261 * it is treated as an 8 bit signed integer.
262 *
263 * @param operandNum the operand number
264 * @return a signed value
265 */
266 public short getValue(int operandNum) {
267
268 Operand operand = getOperand(operandNum);
269 switch (operand.getType()) {
270
271 case VARIABLE:
272 return machineState.getVariable(operand.getValue());
273 case SMALL_CONSTANT:
274 case LARGE_CONSTANT:
275 default:
276 return operand.getValue();
277 }
278 }
279
280 /***
281 * Retrieves the value of the specified operand as an unsigned 16 bit
282 * integer.
283 *
284 * @param operandNum the operand number
285 * @return the value
286 */
287 public int getUnsignedValue(int operandNum) {
288
289 short signedValue = getValue(operandNum);
290 return signedValue & 0xffff;
291 }
292
293 /***
294 * Stores the specified value in the result variable.
295 *
296 * @param value the value to store
297 */
298 protected void storeResult(short value) {
299
300 machineState.setVariable(getStoreVariable(), value);
301 }
302
303 /***
304 * Halt the virtual machine with an error message about this instruction.
305 */
306 protected void throwInvalidOpcode() {
307
308 getMachine().halt("illegal instruction, type: " + getInstructionForm() +
309 " operand count: " + getOperandCount() + " opcode: " + getOpcode());
310
311 }
312
313
314
315
316
317 /***
318 * Advances the program counter to the next instruction.
319 */
320 protected void nextInstruction() {
321
322 Machine machineState = getMachine();
323 machineState.setProgramCounter(machineState.getProgramCounter()
324 + getLength());
325 }
326
327 /***
328 * Performs a branch, depending on the state of the condition flag.
329 * If branchIfConditionTrue is true, the branch will be performed if
330 * condition is true, if branchIfCondition is false, the branch will
331 * be performed if condition is false.
332 *
333 * @param condition the test condition
334 */
335 protected void branchOnTest(boolean condition) {
336
337 boolean test = branchIfConditionTrue ? condition : !condition;
338 if (test) {
339
340 applyBranch(getBranchOffset());
341
342 } else {
343
344 nextInstruction();
345 }
346 }
347
348 /***
349 * Applies a jump by applying the branch formula on the pc given the specified
350 * offset.
351 *
352 * @param offset the offset
353 */
354 private void applyBranch(short offset) {
355
356 Machine machineState = getMachine();
357
358 if (offset >= 2 || offset < 0) {
359
360 int target = machineState.getProgramCounter() + getLength() + offset - 2;
361 machineState.setProgramCounter(target);
362
363 } else {
364
365
366
367 returnFromRoutine(offset);
368 }
369 }
370
371 /***
372 * This function returns from the current routine, setting the return value
373 * into the specified return variable.
374 *
375 * @param returnValue the return value
376 */
377 protected void returnFromRoutine(short returnValue) {
378
379 getMachine().popRoutineContext(returnValue);
380 }
381 }