/* OperandCompiler9900.java (c) 2008-2015 Edward Swartz All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html */ package v9t9.machine.ti99.compiler; import org.apache.bcel.Constants; import org.apache.bcel.generic.ALOAD; import org.apache.bcel.generic.DUP; import org.apache.bcel.generic.DUP_X1; import org.apache.bcel.generic.I2S; import org.apache.bcel.generic.IADD; import org.apache.bcel.generic.IAND; import org.apache.bcel.generic.IFNE; import org.apache.bcel.generic.ILOAD; import org.apache.bcel.generic.ISTORE; import org.apache.bcel.generic.InstructionConstants; import org.apache.bcel.generic.InstructionFactory; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.NOP; import org.apache.bcel.generic.POP; import org.apache.bcel.generic.PUSH; import org.apache.bcel.generic.Type; import v9t9.common.asm.BaseMachineOperand; import v9t9.common.asm.IMachineOperand; import v9t9.common.asm.IOperand; import v9t9.engine.compiler.CompileInfo; import v9t9.machine.ti99.cpu.Inst9900; import v9t9.machine.ti99.cpu.Instruction9900; import v9t9.machine.ti99.cpu.MachineOperand9900; public class OperandCompiler9900 { static boolean hasConstAddr(BaseMachineOperand op, CompileInfo info) { return op.type == MachineOperand9900.OP_ADDR && op.val == 0 && !op.bIsReference && info.memory.hasRomAccess(op.immed) && !info.memory.hasRamAccess(op.immed) && info.memory.isStatic(op.immed); } /** * @return true: has an EA */ public static boolean compileGetEA(MachineOperand9900 op, int eaIndex, CompileInfo info, Instruction9900 ins, int pc) { InstructionList ilist = info.ilist; switch (op.type) { case MachineOperand9900.OP_REG: // Rx info.cycles += 0 * 4; // (short) ((val<<1) + wp); if (!(ins.getInst() == Inst9900.Impy /*&& ins.op2 == this*/) && !(ins.getInst() == Inst9900.Idiv /*&& ins.op2 == this*/) && info.optimizeRegAccess && false) { return false; } // slow mode: treat this as normal memory access OperandCompiler9900.compileGetRegEA(info, op.val); break; case MachineOperand9900.OP_IND: { // *Rx //short ad = (short)((val<<1) + wp); if (info.optimizeRegAccess) { OperandCompiler9900.compileReadRegWord(info, ilist, op.val); } else { OperandCompiler9900.compileGetRegEA(info, op.val); OperandCompiler9900.compileReadWord(info, ilist); // regval } break; } case MachineOperand9900.OP_INC: { // *Rx+ if (info.optimizeRegAccess) { OperandCompiler9900.compileReadRegWordAndInc(info, ilist, op.val, op.byteop ? 1 : 2, false); // &Rxx /* postincrement register */ info.cycles += op.byteop ? 2 : 4; } else { //short ad = (short)((val<<1) + wp); OperandCompiler9900.compileGetRegEA(info, op.val); // &Rxx /* postincrement register */ info.cycles += op.byteop ? 2 : 4; ilist.append(new DUP()); // &Rxx, &Rxx OperandCompiler9900.compileReadWord(info, ilist); // &Rxx, regval ilist.append(new DUP_X1()); // regval, &Rxx, regval ilist.append(new PUSH(info.pgen, op.byteop ? 1 : 2)); ilist.append(new IADD()); ilist.append(new I2S()); // regval, &Rxx, regval+K OperandCompiler9900.compileWriteWord(info, ilist); // regval } break; } case MachineOperand9900.OP_ADDR: { // @>xxxx or @>xxxx(Rx) if (hasConstAddr(op, info)) { return false; } ilist.append(new PUSH(info.pgen, op.immed)); if (op.val != 0) { //ad = (short)((val<<1) + wp); // OperandCompiler.compileGetRegEA(info, val); // Compiler.compileReadWord(info, ilist); // &Rxx, immed, regval if (info.optimizeRegAccess) { OperandCompiler9900.compileReadRegWord(info, ilist, op.val); } else { OperandCompiler9900.compileGetRegEA(info, op.val); OperandCompiler9900.compileReadWord(info, ilist); // regval } ilist.append(new IADD()); ilist.append(new I2S()); // &Rxx, regval+immed ////this.cycles += Instruction.getMemoryCycles(ad); } break; } case MachineOperand9900.OP_OFFS_R12: // offset from R12 if (info.optimizeRegAccess) { return false; } OperandCompiler9900.compileGetRegEA(info, 12); break; case MachineOperand9900.OP_REG0_SHIFT_COUNT: // shift count from R0 if (info.optimizeRegAccess) { return false; } OperandCompiler9900.compileGetRegEA(info, 0); break; case MachineOperand9900.OP_JUMP: // jump target ilist.append(new PUSH(info.pgen, (short)(op.val + pc))); break; case IMachineOperand.OP_NONE: case MachineOperand9900.OP_IMMED: // immediate case MachineOperand9900.OP_CNT: // shift count case MachineOperand9900.OP_STATUS: // status word case MachineOperand9900.OP_INST: default: return false; //ilist.append(new PUSH(info.pgen, 0)); } ilist.append(new ISTORE(eaIndex)); return true; } /** * @param memory * @return true: has value */ public static boolean compileGetValue(MachineOperand9900 op, int valIndex, int eaIndex, CompileInfo info) { InstructionList ilist = info.ilist; switch (op.type) { case MachineOperand9900.OP_REG: // Rx if (info.optimizeRegAccess) { // when optimizing, read directly from WP memory if (op.byteop) { OperandCompiler9900.compileReadRegByte(info, ilist, op.val, op.dest == IOperand.OP_DEST_TRUE); } else { OperandCompiler9900.compileReadRegWord(info, ilist, op.val, op.dest == IOperand.OP_DEST_TRUE); } break; } // fall through case MachineOperand9900.OP_INC: // *Rx+ case MachineOperand9900.OP_IND: // *Rx case MachineOperand9900.OP_ADDR: // @>xxxx or @>xxxx(Rx) if (hasConstAddr(op, info)) { if (op.byteop) { OperandCompiler9900.compileReadAbsByte(info, ilist, op.immed); } else { OperandCompiler9900.compileReadAbsWord(info, ilist, op.immed); } } else { ilist.append(new ILOAD(eaIndex)); if (!op.bIsReference) { if (op.byteop) { OperandCompiler9900.compileReadByte(info, ilist); } else { OperandCompiler9900.compileReadWord(info, ilist); } } } break; case MachineOperand9900.OP_IMMED: // immediate ilist.append(new PUSH(info.pgen, op.immed)); break; case MachineOperand9900.OP_CNT: // shift count ilist.append(new PUSH(info.pgen, op.val)); break; case MachineOperand9900.OP_OFFS_R12: // offset from R12 if (info.optimizeRegAccess) { OperandCompiler9900.compileReadRegWord(info, ilist, 12); } else { ilist.append(new ILOAD(eaIndex)); OperandCompiler9900.compileReadWord(info, ilist); } if (op.val != 0) { ilist.append(new PUSH(info.pgen, op.val)); ilist.append(new IADD()); ilist.append(new I2S()); } break; case MachineOperand9900.OP_REG0_SHIFT_COUNT: // shift count from R0 if (info.optimizeRegAccess) { OperandCompiler9900.compileReadRegWord(info, ilist, 0); } else { ilist.append(new ILOAD(eaIndex)); OperandCompiler9900.compileReadWord(info, ilist); } ilist.append(new PUSH(info.pgen, 0xf)); ilist.append(new IAND()); ilist.append(new I2S()); // if value==0 value=16 InstructionList skip = new InstructionList(); InstructionHandle skipInst = skip.append(new NOP()); ilist.append(new DUP()); // value ilist.append(new IFNE(skipInst)); ilist.append(new POP()); ilist.append(new PUSH(info.pgen, 16)); ilist.append(skip); break; case MachineOperand9900.OP_JUMP: // jump target ilist.append(new ILOAD(eaIndex)); break; case MachineOperand9900.OP_INST: ilist.append(new ILOAD(eaIndex)); OperandCompiler9900.compileReadWord(info, ilist); break; case IMachineOperand.OP_NONE: case MachineOperand9900.OP_STATUS: // status word //TODO: NOTHING -- make sure we don't depend on this default: //ilist.append(new PUSH(info.pgen, 0)); return false; } ilist.append(new ISTORE(valIndex)); return true; } public static void compilePutValue(MachineOperand9900 op, int valIndex, int eaIndex, CompileInfo info) { switch (op.type) { case MachineOperand9900.OP_REG: if (info.optimizeRegAccess) { // write directly to WP memory info.ilist.append(new ILOAD(valIndex)); if (op.byteop) { OperandCompiler9900.compileWriteRegByte(info, info.ilist, op.val, op.dest == IOperand.OP_DEST_TRUE); } else { OperandCompiler9900.compileWriteRegWord(info, info.ilist, op.val, op.dest == IOperand.OP_DEST_TRUE); } break; } // fall through default: info.ilist.append(new ILOAD(eaIndex)); info.ilist.append(new ILOAD(valIndex)); if (op.byteop) { info.ilist.append(InstructionConstants.I2B); OperandCompiler9900.compileWriteByte(info, info.ilist); } else { OperandCompiler9900.compileWriteWord(info, info.ilist); } break; } } /** Read a register */ public static void compileGetRegEA(CompileInfo info, int reg) { InstructionList ilist = info.ilist; if (reg < 0 || reg > 15) { throw new AssertionError("bad register " + reg); } ilist.append(new ILOAD(info.localWp)); if (reg != 0) { ilist.append(new PUSH(info.pgen, reg * 2)); ilist.append(InstructionConstants.IADD); ilist.append(InstructionConstants.I2S); } } /** * Read a word from the given constant address */ public static void compileReadAbsWord(CompileInfo info, InstructionList ilist, short addr) { ilist.append(new PUSH(info.pgen, info.memory.flatReadWord(addr))); } /** * Read a byte from the given constant address */ public static void compileReadAbsByte(CompileInfo info, InstructionList ilist, short addr) { ilist.append(new PUSH(info.pgen, info.memory.flatReadByte(addr))); } private static void compileGetRegAddress(CompileInfo info, InstructionList ilist, int val) { ilist.append(new ILOAD(info.localWpOffset)); if (val != 0) { ilist.append(new PUSH(info.pgen, val)); // short[] index ilist.append(InstructionConstants.IADD); } } public static void compileReadRegByte(CompileInfo info, InstructionList ilist, int val) { compileReadRegByte(info, ilist, val, false); } /** * Read a byte from the given register */ public static void compileReadRegByte(CompileInfo info, InstructionList ilist, int val, boolean saveAddr) { ilist.append(new ALOAD(info.localWpWordMemory)); compileGetRegAddress(info, ilist, val); if (saveAddr) { ilist.append(InstructionConstants.DUP2); } ilist.append(InstructionFactory.createArrayLoad(Type.SHORT)); ilist.append(new PUSH(info.pgen, 8)); ilist.append(InstructionConstants.ISHR); ilist.append(InstructionConstants.I2S); } public static void compileReadRegWord(CompileInfo info, InstructionList ilist, int val) { compileReadRegWord(info, ilist, val, false); } /** * Read a word from the given register * * if saveAddr is true, stack is: wp[] woffs wreg */ public static void compileReadRegWord(CompileInfo info, InstructionList ilist, int val, boolean saveAddr) { // FIXME: add cycles ilist.append(new ALOAD(info.localWpWordMemory)); compileGetRegAddress(info, ilist, val); if (saveAddr) { ilist.append(InstructionConstants.DUP2); } ilist.append(InstructionFactory.createArrayLoad(Type.SHORT)); } public static void compileReadRegWordAndInc(CompileInfo info, InstructionList ilist, int val, int incBy, boolean saveAddr) { ilist.append(new ALOAD(info.localWpWordMemory)); // stack: wp[] compileGetRegAddress(info, ilist, val); // stack: wp[] woffs if (saveAddr) { ilist.append(InstructionConstants.DUP2); } ilist.append(InstructionConstants.DUP2); // stack: wp[] woffs wp[] woffs ilist.append(InstructionFactory.createArrayLoad(Type.SHORT)); // stack: wp[] woffs wreg ilist.append(InstructionConstants.DUP_X2); // stack: wreg wp[] woffs wreg ilist.append(new PUSH(info.pgen, incBy)); ilist.append(InstructionConstants.IADD); // ilist.append(InstructionConstants.I2S); // stack: wreg wp[] woffs wreg+2 ilist.append(InstructionFactory.createArrayStore(Type.SHORT)); // stack: wreg } /** * Write a byte to the given register */ public static void compileWriteRegByte(CompileInfo info, InstructionList ilist, int val, boolean savedAddr) { // move value to high byte ilist.append(new PUSH(info.pgen, 8)); ilist.append(InstructionConstants.ISHL); if (savedAddr) { // stack: wp[], woffs, val<<8 // shove val off ilist.append(new ISTORE(info.localTemp)); ilist.append(InstructionConstants.DUP2); // stack: wp[], woffs, wp[], woffs // read whole reg ilist.append(InstructionFactory.createArrayLoad(Type.SHORT)); // stack: wp[], woffs, wreg // mask and replace ilist.append(new PUSH(info.pgen, 0xff)); ilist.append(InstructionConstants.IAND); ilist.append(new ILOAD(info.localTemp)); // stack: wp[], woffs, wreg, val<<8 ilist.append(InstructionConstants.IOR); // stack: wp[], woffs, wreg' // store back ilist.append(InstructionConstants.I2S); ilist.append(InstructionFactory.createArrayStore(Type.SHORT)); } else { // stack: val<<8 ilist.append(new ALOAD(info.localWpWordMemory)); // stack: val<<8, wp[] ilist.append(InstructionConstants.DUP_X1); // stack: wp[], val<<8, wp[] // get reg offset compileGetRegAddress(info, ilist, val); // stack: wp[], val<<8, wp[], woffs ilist.append(InstructionConstants.DUP_X2); // stack: wp[], woffs, val<<8, wp[], woffs // read whole reg ilist.append(InstructionFactory.createArrayLoad(Type.SHORT)); // stack: wp[], woffs, val<<8, wreg // mask and replace ilist.append(new PUSH(info.pgen, 0xff)); ilist.append(InstructionConstants.IAND); ilist.append(InstructionConstants.IOR); // stack: wp[], woffs, wreg' // store back ilist.append(InstructionConstants.I2S); ilist.append(InstructionFactory.createArrayStore(Type.SHORT)); } } /** * Write a word to the given register */ public static void compileWriteRegWord(CompileInfo info, InstructionList ilist, int val) { compileWriteRegWord(info, ilist, val, false); } public static void compileWriteRegWord(CompileInfo info, InstructionList ilist, int val, boolean savedAddr) { if (savedAddr) { // stack: wp[], woffs, val ilist.append(InstructionFactory.createArrayStore(Type.SHORT)); } else { // stack: val // get reg offset ilist.append(new ALOAD(info.localWpWordMemory)); // stack: val, wp[] ilist.append(InstructionConstants.SWAP); // stack: wp[], val compileGetRegAddress(info, ilist, val); // stack: wp[], val, woffs ilist.append(InstructionConstants.SWAP); // stack: wp[], woffs, val ilist.append(InstructionFactory.createArrayStore(Type.SHORT)); } } /** * Read a word from the address on the stack. */ public static void compileReadWord(CompileInfo info, InstructionList ilist) { ilist.append(new ALOAD(info.localMemory)); ilist.append(InstructionConstants.SWAP); ilist.append(info.ifact.createInvoke(v9t9.common.memory.IMemoryDomain.class .getName(), "readWord", Type.SHORT, new Type[] { Type.INT }, Constants.INVOKEINTERFACE)); } /** * Read a byte from the address on the stack. */ public static void compileReadByte(CompileInfo info, InstructionList ilist) { ilist.append(new ALOAD(info.localMemory)); ilist.append(InstructionConstants.SWAP); ilist.append(info.ifact.createInvoke(v9t9.common.memory.IMemoryDomain.class .getName(), "readByte", Type.BYTE, new Type[] { Type.INT }, Constants.INVOKEINTERFACE)); } /** * Read a word from the variable in addrIndex */ public static void compileReadByte(CompileInfo info, int addrIndex, InstructionList ilist) { ilist.append(new ILOAD(addrIndex)); compileReadByte(info, ilist); } /** * Write a word from the value,address on the stack. */ public static void compileWriteWord(CompileInfo info, InstructionList ilist) { ilist.append(new ALOAD(info.localMemory)); ilist.append(InstructionConstants.DUP_X2); ilist.append(InstructionConstants.POP); ilist.append(info.ifact.createInvoke(v9t9.common.memory.IMemoryDomain.class .getName(), "writeWord", Type.VOID, new Type[] { Type.INT, Type.SHORT }, Constants.INVOKEINTERFACE)); } /** * Write a byte from the value,address on the stack. */ public static void compileWriteByte(CompileInfo info, InstructionList ilist) { ilist.append(new ALOAD(info.localMemory)); ilist.append(InstructionConstants.DUP_X2); ilist.append(InstructionConstants.POP); ilist.append(info.ifact.createInvoke(v9t9.common.memory.IMemoryDomain.class .getName(), "writeByte", Type.VOID, new Type[] { Type.INT, Type.BYTE }, Constants.INVOKEINTERFACE)); } }