/* * $Id: ExtendedInstruction.java 536 2008-02-19 06:03:27Z weiju $ * * Created on 10/03/2005 * Copyright 2005-2008 by Wei-ju Wu * This file is part of The Z-machine Preservation Project (ZMPP). * * ZMPP is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ZMPP is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZMPP. If not, see <http://www.gnu.org/licenses/>. */ package org.zmpp.instructions; import java.awt.Dimension; import org.zmpp.base.Memory; import org.zmpp.vm.Machine; import org.zmpp.vm.PortableGameState; import org.zmpp.vm.ScreenModel; public class ExtendedInstruction extends AbstractInstruction { /** * Constructor. * * @param machine a Machine object * @param opcode the opcode */ public ExtendedInstruction(Machine machine, int opcode) { super(machine, opcode); } /** * {@inheritDoc} */ public InstructionForm getInstructionForm() { return InstructionForm.VARIABLE; } /** * {@inheritDoc} */ public OperandCount getOperandCount() { return OperandCount.EXT; } /** * {@inheritDoc} */ protected InstructionStaticInfo getStaticInfo() { return ExtendedStaticInfo.getInstance(); } /** * {@inheritDoc} */ public void doInstruction() { switch (getOpcode()) { case ExtendedStaticInfo.OP_SAVE: save(); break; case ExtendedStaticInfo.OP_RESTORE: restore(); break; case ExtendedStaticInfo.OP_LOG_SHIFT: log_shift(); break; case ExtendedStaticInfo.OP_ART_SHIFT: art_shift(); break; case ExtendedStaticInfo.OP_SET_FONT: set_font(); break; case ExtendedStaticInfo.OP_SAVE_UNDO: save_undo(); break; case ExtendedStaticInfo.OP_RESTORE_UNDO: restore_undo(); break; case ExtendedStaticInfo.OP_PRINT_UNICODE: print_unicode(); break; case ExtendedStaticInfo.OP_CHECK_UNICODE: check_unicode(); break; case ExtendedStaticInfo.OP_MOUSE_WINDOW: mouse_window(); break; case ExtendedStaticInfo.OP_PICTURE_DATA: picture_data(); break; case ExtendedStaticInfo.OP_DRAW_PICTURE: draw_picture(); break; case ExtendedStaticInfo.OP_ERASE_PICTURE: erase_picture(); break; case ExtendedStaticInfo.OP_MOVE_WINDOW: move_window(); break; case ExtendedStaticInfo.OP_WINDOW_SIZE: window_size(); break; case ExtendedStaticInfo.OP_WINDOW_STYLE: window_style(); break; case ExtendedStaticInfo.OP_SET_MARGINS: set_margins(); break; case ExtendedStaticInfo.OP_GET_WIND_PROP: get_wind_prop(); break; case ExtendedStaticInfo.OP_PICTURE_TABLE: picture_table(); break; case ExtendedStaticInfo.OP_PUT_WIND_PROP: put_wind_prop(); break; case ExtendedStaticInfo.OP_PUSH_STACK: push_stack(); break; case ExtendedStaticInfo.OP_POP_STACK: pop_stack(); break; case ExtendedStaticInfo.OP_READ_MOUSE: read_mouse(); break; case ExtendedStaticInfo.OP_SCROLL_WINDOW: scroll_window(); break; default: throwInvalidOpcode(); break; } } private void save_undo() { // Target PC offset is two because of the extra opcode byte and // operand type byte compared to the 0OP instruction final int pc = getMachine().getCpu().getProgramCounter() + 3; final boolean success = getMachine().save_undo(pc); storeResult((short) (success ? TRUE : FALSE)); nextInstruction(); } private void restore_undo() { final PortableGameState gamestate = getMachine().restore_undo(); if (gamestate == null) { storeResult((short) FALSE); } else { final int storevar = gamestate.getStoreVariable(getMachine()); getCpu().setVariable(storevar, (short) RESTORE_TRUE); } } private void art_shift() { short number = getValue(0); final short places = getValue(1); number = (short) ((places >= 0) ? number << places : number >> (-places)); storeResult(number); nextInstruction(); } private void log_shift() { short number = getValue(0); final short places = getValue(1); number = (short) ((places >= 0) ? number << places : number >>> (-places)); storeResult(number); nextInstruction(); } private void set_font() { final int previousFont = getMachine().getScreen().setFont(getValue(0)); storeResult((short) previousFont); nextInstruction(); } private void save() { // Saving to tables is not supported yet, this is the standard save feature // Offset is 3 because there are two opcode bytes + 1 optype byte before // the actual store var byte saveToStorage(getMachine().getCpu().getProgramCounter() + 3); } private void restore() { // Reading from tables is not supported yet, this is the standard // restore feature restoreFromStorage(); } private void print_unicode() { final char zchar = (char) getUnsignedValue(0); getMachine().getOutput().printZsciiChar(zchar, false); nextInstruction(); } private void check_unicode() { // always return true, set bit 0 for can print and bit 1 for // can read storeResult((short) 3); nextInstruction(); } private void mouse_window() { getMachine().getScreen6().setMouseWindow(getValue(0)); nextInstruction(); } private void picture_data() { final int picnum = getUnsignedValue(0); final int array = getUnsignedValue(1); boolean result = false; if (picnum == 0) { writePictureFileInfo(array); // branch if any pictures are available: this information is only // available in the 1.1 spec result = getMachine().getPictureManager().getNumPictures() > 0; } else { final Dimension picdim = getMachine().getPictureManager().getPictureSize(picnum); if (picdim != null) { final Memory memory = getMachine().getGameData().getMemory(); memory.writeUnsignedShort(array, picdim.height); memory.writeUnsignedShort(array + 2, picdim.width); result = true; } } branchOnTest(result); } private void writePictureFileInfo(final int array) { final Memory memory = getMachine().getGameData().getMemory(); memory.writeUnsignedShort(array, getMachine().getPictureManager().getNumPictures()); memory.writeUnsignedShort(array + 2, getMachine().getPictureManager().getRelease()); } private void draw_picture() { final int picnum = getUnsignedValue(0); int x = 0, y = 0; if (getNumOperands() > 1) { y = getUnsignedValue(1); } if (getNumOperands() > 2) { x = getUnsignedValue(2); } getMachine().getScreen6().getSelectedWindow().drawPicture( getMachine().getPictureManager().getPicture(picnum), y, x); nextInstruction(); } private void erase_picture() { final int picnum = getUnsignedValue(0); int x = 1, y = 1; if (getNumOperands() > 1) { y = getUnsignedValue(1); } if (getNumOperands() > 2) { x = getUnsignedValue(2); } getMachine().getScreen6().getSelectedWindow().erasePicture( getMachine().getPictureManager().getPicture(picnum), y, x); nextInstruction(); } private void move_window() { getMachine().getScreen6().getWindow(getUnsignedValue(0)).move( getUnsignedValue(1), getUnsignedValue(2)); nextInstruction(); } private void window_size() { final int window = getValue(0); final int height = getValue(1); final int width = getValue(2); //System.out.printf("@window_size %d %d %d\n", window, height, width); getMachine().getScreen6().getWindow(window).setSize(height, width); nextInstruction(); } private void window_style() { int operation = 0; if (getNumOperands() > 2) { operation = getUnsignedValue(2); } getWindow(getValue(0)).setStyle(getUnsignedValue(1), operation); nextInstruction(); } private void set_margins() { int window = ScreenModel.CURRENT_WINDOW; if (getNumOperands() == 3) { window = getValue(2); } getWindow(window).setMargins(getUnsignedValue(0), getUnsignedValue(1)); nextInstruction(); } private void get_wind_prop() { int window = getValue(0); int propnum = getUnsignedValue(1); short result; result = (short) getWindow(window).getProperty(propnum); storeResult(result); nextInstruction(); } private void put_wind_prop() { int window = getValue(0); int propnum = getUnsignedValue(1); short value = getValue(2); getWindow(window).putProperty(propnum, value); nextInstruction(); } private void picture_table() { // @picture_table is a no-op, because all pictures are held in memory // anyways nextInstruction(); } private void pop_stack() { int numItems = getUnsignedValue(0); int stack = 0; if (getNumOperands() == 2) { stack = getUnsignedValue(1); } if (stack == 0) { // pop from system stack for (int i = 0; i < numItems; i++) { getCpu().getVariable(0); } } else { // pop from user stack for (int i = 0; i < numItems; i++) { getCpu().popUserStack(stack); } } nextInstruction(); } private void push_stack() { short value = getValue(0); int stack = 0; if (getNumOperands() == 2) { stack = getUnsignedValue(1); } boolean ok = true; if (stack == 0) { getCpu().setVariable(0, value); } else { ok = getCpu().pushUserStack(stack, value); } branchOnTest(ok); } private void scroll_window() { getWindow(getValue(0)).scroll(getValue(1)); nextInstruction(); } private void read_mouse() { int array = getUnsignedValue(0); getMachine().getScreen6().readMouse(array); nextInstruction(); } }