View Javadoc

1   /*
2    * $Id: LongInstruction.java,v 1.18 2005/10/25 21:40:54 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 org.zmpp.base.MemoryReadAccess;
26  
27  
28  /***
29   * This class represents instructions of type LONG, 2OP.
30   * 
31   * @author Wei-ju Wu
32   * @version 1.0
33   */
34  public class LongInstruction extends AbstractInstruction {
35  
36    /***
37     * List of opcodes. See Z-Machine Standards document 1.0 for
38     * explanations.
39     */
40    public static final int OP_JE                 = 0x01;
41    public static final int OP_JL                 = 0x02;
42    public static final int OP_JG                 = 0x03;
43    public static final int OP_DEC_CHK            = 0x04;
44    public static final int OP_INC_CHK            = 0x05;
45    public static final int OP_JIN                = 0x06;
46    public static final int OP_TEST               = 0x07;
47    public static final int OP_OR                 = 0x08;
48    public static final int OP_AND                = 0x09;
49    public static final int OP_TEST_ATTR          = 0x0a;
50    public static final int OP_SET_ATTR           = 0x0b;
51    public static final int OP_CLEAR_ATTR         = 0x0c;
52    public static final int OP_STORE              = 0x0d;
53    public static final int OP_INSERT_OBJ         = 0x0e;
54    public static final int OP_LOADW              = 0x0f;
55    public static final int OP_LOADB              = 0x10;
56    public static final int OP_GET_PROP           = 0x11;
57    public static final int OP_GET_PROP_ADDR      = 0x12;
58    public static final int OP_GET_NEXT_PROP      = 0x13;
59    public static final int OP_ADD                = 0x14;
60    public static final int OP_SUB                = 0x15;
61    public static final int OP_MUL                = 0x16;
62    public static final int OP_DIV                = 0x17;
63    public static final int OP_MOD                = 0x18;
64    
65    /***
66     * The operand count.
67     */
68    private OperandCount operandCount;
69    
70    /***
71     * Constructor.
72     * 
73     * @param machineState a reference to a MachineState object
74     * @param opcode the instruction's opcode
75     */
76    public LongInstruction(Machine machineState, int opcode) {
77      
78      super(machineState, opcode);
79      this.operandCount = OperandCount.C2OP;
80    }
81    
82    /***
83     * Constructor.
84     * 
85     * @param machineState the machine state
86     * @param operandCount the operand count
87     * @param opcode the opcode
88     */
89    public LongInstruction(Machine machineState,
90        OperandCount operandCount, int opcode) {
91      
92      super(machineState, opcode);
93      this.operandCount = operandCount;
94    }
95    
96    /***
97     * {@inheritDoc}
98     */
99    public void execute() {
100    
101     switch (getOpcode()) {
102     
103       case OP_JE:
104         je();
105         break;
106       case OP_JL:
107         jl();
108         break;
109       case OP_JG:
110         jg();
111         break;
112       case OP_JIN:
113         jin();
114         break;
115       case OP_DEC_CHK:
116         decChk();
117         break;
118       case OP_INC_CHK:
119         incChk();
120         break;
121       case OP_TEST:
122         test();
123         break;
124       case OP_OR:
125         or();
126         break;
127       case OP_AND:
128         and();
129         break;
130       case OP_TEST_ATTR:
131         testAttr();
132         break;
133       case OP_SET_ATTR:
134         setAttr();
135         break;
136       case OP_CLEAR_ATTR:
137         clearAttr();
138         break;        
139       case OP_STORE:
140         store();
141         break;
142       case OP_INSERT_OBJ:
143         insertObj();
144         break;
145       case OP_LOADW:
146         loadw();
147         break;        
148       case OP_LOADB:
149         loadb();
150         break;        
151       case OP_GET_PROP:
152         getProp();
153         break;        
154       case OP_GET_PROP_ADDR:
155         getPropAddr();
156         break;        
157       case OP_GET_NEXT_PROP:
158         getNextProp();
159         break;        
160       case OP_ADD:
161         add();
162         break;
163       case OP_SUB:
164         sub();
165         break;
166       case OP_MUL:
167         mul();
168         break;
169       case OP_DIV:
170         div();
171         break;
172       case OP_MOD:
173         mod();
174         break;
175       default:
176         throwInvalidOpcode();
177     }
178   }
179   
180   /***
181    * {@inheritDoc}
182    */
183   public InstructionForm getInstructionForm() { return InstructionForm.LONG; }
184   
185   /***
186    * {@inheritDoc}
187    */
188   public OperandCount getOperandCount() { return operandCount; }
189   
190   /***
191    * {@inheritDoc}
192    */
193   public boolean storesResult() {
194     
195     switch (getOpcode()) {
196       case OP_OR:
197       case OP_AND:
198       case OP_LOADW:
199       case OP_LOADB:
200       case OP_GET_PROP:
201       case OP_GET_PROP_ADDR:
202       case OP_GET_NEXT_PROP:
203       case OP_ADD:
204       case OP_SUB:
205       case OP_MUL:
206       case OP_DIV:
207       case OP_MOD:
208         return true;
209       default:
210         return false;
211     }
212   }
213   
214   /***
215    * {@inheritDoc}
216    */
217   public boolean isBranch() {
218     
219     switch (getOpcode()) {
220       case OP_JE:
221       case OP_JL:
222       case OP_JG:
223       case OP_DEC_CHK:
224       case OP_INC_CHK:
225       case OP_JIN:
226       case OP_TEST:
227       case OP_TEST_ATTR:
228         return true;
229       default:
230         return false;
231     }
232   }
233   
234   private void je() {
235     
236     boolean equalsFollowing = false;
237     short op1 = getValue(0);
238     if (getNumOperands() <= 1) {
239 
240       getMachine().halt("je expects at least two operands, only " +
241                         "one provided");
242     } else {
243       
244       for (int i = 1; i < getNumOperands(); i++) {
245         
246         if (op1 == getValue(i)) {
247           
248           equalsFollowing = true;
249           break;
250         }
251       }
252       branchOnTest(equalsFollowing);
253     }
254   }
255     
256   private void jl() {
257     
258     short op1 = getValue(0);
259     short op2 = getValue(1);
260     branchOnTest(op1 < op2);
261   }
262   
263   private void jg() {
264     
265     short op1 = getValue(0);
266     short op2 = getValue(1);
267     branchOnTest(op1 > op2);
268   }
269   
270   private void jin() {
271     
272     int obj1 = getUnsignedValue(0);
273     int obj2 = getUnsignedValue(1);    
274     ZObject zobj1 = getMachine().getObjectTree().getObject(obj1);
275     branchOnTest(zobj1.getParent() == obj2);
276   }
277   
278   private void decChk() {
279     
280     int varnum = getUnsignedValue(0);
281     short value = getValue(1);
282     short varValue = (short) (getMachine().getVariable(varnum) - 1);
283     
284     getMachine().setVariable(varnum, varValue);
285     branchOnTest(varValue < value);
286   }
287   
288   private void incChk() {
289     
290     int varnum = getUnsignedValue(0);
291     short value = getValue(1);
292     short varValue = (short) (getMachine().getVariable(varnum) + 1);
293     
294     getMachine().setVariable(varnum, varValue);
295     branchOnTest(varValue > value);
296   }
297   
298   private void test() {
299     
300     int op1 = getUnsignedValue(0);
301     int op2 = getUnsignedValue(1);
302     branchOnTest((op1 & op2) == op2);
303   }
304   
305   private void or() {
306     
307     int op1 = getUnsignedValue(0);
308     int op2 = getUnsignedValue(1);
309     storeResult((short) ((op1 | op2) & 0xffff));
310     nextInstruction();
311   }
312   
313   private void and() {
314     
315     int op1 = getUnsignedValue(0);
316     int op2 = getUnsignedValue(1);
317     storeResult((short) ((op1 & op2) & 0xffff));
318     nextInstruction();
319   }
320   
321   private void add() {
322     
323     short op1 = getValue(0);
324     short op2 = getValue(1);
325     storeResult((short) (op1 + op2));
326     nextInstruction();
327   }
328   
329   private void sub() {
330     
331     short op1 = getValue(0);
332     short op2 = getValue(1);
333     storeResult((short) (op1 - op2));
334     nextInstruction();
335   }
336   
337   private void mul() {
338     
339     short op1 = getValue(0);
340     short op2 = getValue(1);
341     storeResult((short)(op1 * op2));
342     nextInstruction();
343   }
344   
345   private void div() {
346     
347     short op1 = getValue(0);
348     short op2 = getValue(1);
349     storeResult((short) (op1 / op2));
350     nextInstruction();
351   }
352   
353   private void mod() {
354     
355     short op1 = getValue(0);
356     short op2 = getValue(1);
357     storeResult((short) (op1 % op2));
358     nextInstruction();
359   }
360   
361   private void testAttr() {
362     
363     int obj = getUnsignedValue(0);
364     int attr = getUnsignedValue(1);
365     ZObject zobj = getMachine().getObjectTree().getObject(obj);
366     branchOnTest(zobj.isAttributeSet(attr));
367   }
368   
369   private void setAttr() {
370     
371     int obj = getUnsignedValue(0);
372     int attr = getUnsignedValue(1);
373     ZObject zobj = getMachine().getObjectTree().getObject(obj);
374     zobj.setAttribute(attr);
375     nextInstruction();
376   }
377   
378   private void clearAttr() {
379     
380     int obj = getUnsignedValue(0);
381     int attr = getUnsignedValue(1);
382     ZObject zobj = getMachine().getObjectTree().getObject(obj);
383     zobj.clearAttribute(attr);
384     nextInstruction();
385   }
386   
387   private void store() {
388     
389     int varnum = getUnsignedValue(0);
390     short value = getValue(1);
391     
392     // Handle stack variable as a special case (standard 1.1)
393     if (varnum == 0) {
394       
395       getMachine().setStackTopElement(value);
396       
397     } else {
398       
399       getMachine().setVariable(varnum, value);
400     }
401     nextInstruction();
402   }
403   
404   private void insertObj() {
405     
406     int obj = getUnsignedValue(0);
407     int dest = getUnsignedValue(1);
408     ObjectTree objectTree = getMachine().getObjectTree();
409     objectTree.insertObject(dest, obj);
410     nextInstruction();
411   }
412   
413   private void loadw() {
414     
415     int arrayAddress = getUnsignedValue(0);
416     int index = getUnsignedValue(1);
417     MemoryReadAccess memaccess = getMachine().getMemoryAccess();
418     storeResult(memaccess.readShort(arrayAddress + 2 * index));
419     nextInstruction();
420   }
421   
422   private void loadb() {
423     
424     int arrayAddress = getUnsignedValue(0);
425     int index = getUnsignedValue(1);
426     MemoryReadAccess memaccess = getMachine().getMemoryAccess();
427     storeResult((short) memaccess.readUnsignedByte(arrayAddress + index));
428     nextInstruction();
429   }
430   
431   private void getProp() {
432     
433     int obj = getUnsignedValue(0);
434     int property = getUnsignedValue(1);
435     ZObject zobj = getMachine().getObjectTree().getObject(obj);
436     int numBytes = zobj.getPropertySize(property);
437     short value;
438     
439     if (!zobj.isPropertyAvailable(property)) {
440      
441       // Retrieve and store default
442       value = getMachine().getObjectTree().getPropertyDefault(property);
443       
444     } else if (numBytes == 1) {
445       
446       value = zobj.getPropertyByte(property, 0);
447       
448     } else {
449       
450       byte byte1 = zobj.getPropertyByte(property, 0);
451       byte byte2 = zobj.getPropertyByte(property, 1);      
452       value = (short) (byte1 << 8 | (byte2 & 0xff));
453     }
454     storeResult(value);
455     nextInstruction();
456   }
457   
458   private void getPropAddr() {
459     
460     int obj = getUnsignedValue(0);
461     int property = getUnsignedValue(1);
462     
463     short value = 0;
464     ZObject zobj = getMachine().getObjectTree().getObject(obj);
465     
466     if (zobj.isPropertyAvailable(property)) {
467       
468       value = (short) (zobj.getPropertyAddress(property) & 0xffff);
469     }
470     storeResult(value);
471     nextInstruction();
472   }
473   
474   private void getNextProp() {
475     
476     int obj = getUnsignedValue(0);
477     int property = getUnsignedValue(1);
478     short value = 0;
479     ZObject zobj = getMachine().getObjectTree().getObject(obj);
480     
481     if (property == 0 || zobj.isPropertyAvailable(property)) {
482       
483       value = (short) (zobj.getNextProperty(property) & 0xffff);
484       storeResult(value);
485       nextInstruction();
486       
487     } else {
488       
489       getMachine().halt("the property [" + property + "] of object [" + obj
490                         + "] does not exist");
491     }
492   }
493 }