View Javadoc

1   /*
2    * $Id: AbstractInstruction.java,v 1.19 2005/11/03 19:06:29 weiju Exp $
3    * 
4    * Created on 03.10.2005
5    * Copyright 2005 by Wei-ju Wu
6    *
7    * This file is part of The Z-machine Preservation Project (ZMPP).
8    *
9    * ZMPP is free software; you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as published by
11   * the Free Software Foundation; either version 2 of the License, or
12   * (at your option) any later version.
13   *
14   * ZMPP is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public License
20   * along with ZMPP; if not, write to the Free Software
21   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
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     //throw new IllegalStateException("illegal opcode: " + getOpcode());
311   }
312   
313   // *********************************************************************
314   // ******** Program flow control
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       // FALSE is defined as 0, TRUE as 1, so simply return the offset
366       // since we do not have negative offsets
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 }