/* Interpreter9900.java (c) 2005-2016 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.interpreter; import java.util.HashMap; import java.util.Map; import v9t9.common.asm.IOperand; import v9t9.common.asm.InstTableCommon; import v9t9.common.cpu.AbortedException; import v9t9.common.cpu.CycleCounts; import v9t9.common.cpu.IChangeElement; import v9t9.common.cpu.ICpuState; import v9t9.common.cpu.IExecutor; import v9t9.common.cpu.IInstructionListener; import v9t9.common.cpu.IInterpreter; import v9t9.common.cpu.IOperandChangeElement; import v9t9.common.cpu.MachineOperandState; import v9t9.common.dsr.IDsrManager; import v9t9.common.machine.IMachine; import v9t9.common.memory.IMemoryArea; import v9t9.common.memory.IMemoryDomain; import v9t9.common.memory.IMemoryEntry; import v9t9.common.memory.IMemoryWriteListener; import v9t9.engine.hardware.ICruHandler; import v9t9.machine.ti99.cpu.ChangeBlock9900; import v9t9.machine.ti99.cpu.Changes; import v9t9.machine.ti99.cpu.Cpu9900; import v9t9.machine.ti99.cpu.CpuState9900; import v9t9.machine.ti99.cpu.Inst9900; import v9t9.machine.ti99.cpu.Instruction9900; import v9t9.machine.ti99.cpu.Status9900; import v9t9.machine.ti99.cpu.Changes.AdvancePC; import v9t9.machine.ti99.machine.TI99Machine; import ejs.base.utils.BinaryUtils; import ejs.base.utils.HexUtils; /** * This class interprets 9900 instructions one by one. * * @author ejs */ public class Interpreter9900 implements IInterpreter { final IMachine machine; final ICruHandler cruHandler; final IDsrManager dsrManager; IMemoryDomain memory; // cache of previously parsed change blocks, grouped by memory area // to allow bank support and avoid garbage for Shorts Map<IMemoryArea, ChangeBlock9900[]> parsedChangeBlocks; private Cpu9900 cpu; public Interpreter9900(IMachine machine) { this.machine = machine; if (machine instanceof TI99Machine) { cruHandler = ((TI99Machine) machine).getCruManager(); dsrManager = ((TI99Machine) machine).getDsrManager(); } else { cruHandler = null; dsrManager = null; } this.cpu = (Cpu9900) machine.getCpu(); this.memory = machine.getCpu().getConsole(); memory.addWriteListener(new IMemoryWriteListener() { @Override public void changed(IMemoryEntry entry, int addr, int size, int value) { ChangeBlock9900[] blocks = parsedChangeBlocks.get(entry.getArea()); if (blocks != null) { int index = getCachedChangeIndex(entry, addr); ChangeBlock9900 removed = blocks[index]; if (removed != null) { blocks[index] = null; //System.out.println(removed.inst); } } } }); parsedChangeBlocks = new HashMap<IMemoryArea, ChangeBlock9900[]>(); } protected int getCachedChangeIndex(IMemoryEntry entry, int addr) { return ((addr - entry.getAddr()) & 0xffff) / 2; } protected ChangeBlock9900 getCachedChange(int addr) { IMemoryEntry entry = cpu.getConsole().getEntryAt(addr); ChangeBlock9900[] changes = parsedChangeBlocks.get(entry.getArea()); if (changes == null) return null; int index = getCachedChangeIndex(entry, addr); return changes[index]; } protected void putCachedChange(int addr, ChangeBlock9900 change) { IMemoryEntry entry = cpu.getConsole().getEntryAt(addr); ChangeBlock9900[] changes = parsedChangeBlocks.get(entry.getArea()); int index = getCachedChangeIndex(entry, addr); if (changes == null) { changes = new ChangeBlock9900[entry.getSize() / 2]; parsedChangeBlocks.put(entry.getArea(), changes); } changes[index] = change; } /* (non-Javadoc) * @see v9t9.emulator.runtime.interpreter.Interpreter#dispose() */ @Override public void dispose() { parsedChangeBlocks.clear(); } /* (non-Javadoc) * @see v9t9.emulator.runtime.interpreter.Interpreter#executeChunk(int, v9t9.emulator.runtime.cpu.Executor) */ public void executeChunk(int numinsts, IExecutor executor) { // pretend the realtime and instructionListeners settings don't change often ChangeBlock9900 changes; Object[] listeners = executor.getInstructionListeners().toArray(); if (listeners.length == 0) { while (numinsts-- > 0) { changes = getChangesForPC(); changes.apply(cpu); if (executor.breakAfterExecution(1) || cpu.isIdle()) break; } } else { // slow while (numinsts-- > 0) { changes = createChangesForPC(); for (Object listener : listeners) { if (!((IInstructionListener) listener).preExecute(changes)) { throw new AbortedException(); } } changes.apply(cpu); for (Object listener : listeners) { ((IInstructionListener) listener).executed(changes); } if (executor.breakAfterExecution(1) || cpu.isIdle()) break; } } } protected ChangeBlock9900 getChangesForPC() { short pc = cpu.getState().getPC(); ChangeBlock9900 changes = null; changes = getCachedChange(pc); if (changes == null) { changes = new ChangeBlock9900(cpu, pc); changes.generate(); if (changes.inst.getInst() != Inst9900.Ix) { putCachedChange(pc, changes); } } return changes; } /** Slow version that forcibly recreates info (e.g. for debugger) */ protected ChangeBlock9900 createChangesForPC() { short pc = cpu.getState().getPC(); ChangeBlock9900 changes = new ChangeBlock9900(cpu, pc); changes.generate(); if (changes.inst.getInst() != Inst9900.Ix) { putCachedChange(pc, changes); } return changes; } /* (non-Javadoc) * @see v9t9.engine.interpreter.IInterpreter#reset() */ @Override public void reset() { parsedChangeBlocks.clear(); } public static abstract class BaseInterpret implements IChangeElement { protected final Instruction9900 inst; protected short prevST; protected final MachineOperandState mos1; protected final MachineOperandState mos2; protected final MachineOperandState mos3; public BaseInterpret(Instruction9900 inst, MachineOperandState mos1, MachineOperandState mos2, MachineOperandState mos3) { this.inst = inst; this.mos1 = mos1; this.mos2 = mos2; this.mos3 = mos3; } public BaseInterpret(Instruction9900 inst, MachineOperandState mos1, MachineOperandState mos2) { this(inst, mos1, mos2, null); } public BaseInterpret(Instruction9900 inst, MachineOperandState mos1) { this(inst, mos1, null, null); } public BaseInterpret(Instruction9900 inst) { this(inst, null, null, null); } // @Override // public IChangeElement clone() { // try { // return (IChangeElement) super.clone(); // } catch (CloneNotSupportedException e) { // assert false; // return null; // } // } @Override public String toString() { return getClass().getSimpleName() + ": " + inst.toString(); } protected abstract void doApply(CpuState9900 cpuState, Status9900 status); protected void doRevert(CpuState9900 cpuState, Status9900 status) { } @Override public final void apply(ICpuState cpuState_) { CpuState9900 cpuState = ((CpuState9900) cpuState_); Status9900 status = cpuState.getStatus(); prevST = cpuState.getStatus().get(); // raw value doApply(cpuState, status); } @Override public final void revert(ICpuState cpuState_) { CpuState9900 cpuState = (CpuState9900) cpuState_; cpuState.getStatus().expand(prevST); doRevert(cpuState, cpuState.getStatus()); } } public static abstract class BaseJump implements IOperandChangeElement { protected Instruction9900 inst; protected MachineOperandState mos1; private short prevPC; private ChangeBlock9900 changes; public BaseJump(ChangeBlock9900 changes, Instruction9900 inst, MachineOperandState mos1) { this.changes = changes; this.inst = inst; this.mos1 = mos1; } // @Override // public IChangeElement clone() { // try { // return (IChangeElement) super.clone(); // } catch (CloneNotSupportedException e) { // assert false; // return null; // } // } @Override public String toString() { return getClass().getSimpleName() + ": " + inst.toString(); } protected abstract boolean test(Status9900 status); @Override public final void apply(ICpuState cpuState_) { CpuState9900 cpuState = ((CpuState9900) cpuState_); Status9900 status = cpuState.getStatus(); prevPC = cpuState.getPC(); changes.counts.addExecute(8); if (test(status)) { changes.counts.addExecute(2); cpuState.setPC((short) (inst.pc + mos1.value)); } } @Override public final void revert(ICpuState cpuState_) { cpuState_.setPC(prevPC); } @Override public String format(IOperand op, boolean preExecute) { if (op != mos1.mop) return null; if (preExecute) { CpuState9900 cpuState = ((CpuState9900) changes.cpuState); Status9900 status = cpuState.getStatus(); if (test(status)) { String target = HexUtils.toHex4(changes.inst.pc + mos1.value); return ">" + target; } else { return "<< no jump >>"; } } return ""; } } public static void appendInterpret(final Cpu9900 cpu, final ChangeBlock9900 changes, Instruction9900 inst, MachineOperandState mos1, MachineOperandState mos2, MachineOperandState mos3) { final ICruHandler cruHandler = (cpu != null && cpu.getMachine() instanceof TI99Machine) ? ((TI99Machine) cpu.getMachine()).getCruManager() : null; switch (inst.getInst()) { case InstTableCommon.Idata: break; case Inst9900.Ili: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value = mos2.value; status.set_LAE(mos1.value); } }); break; case Inst9900.Iai: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_ADD_LAECO(mos1.value, mos2.value); mos1.value += mos2.value; } }); break; case Inst9900.Iandi: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value &= mos2.value; status.set_LAE(mos1.value); } }); break; case Inst9900.Iori: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value |= mos2.value; status.set_LAE(mos1.value); } }); break; case Inst9900.Ici: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_CMP(mos1.value, mos2.value); } }); break; case Inst9900.Istwp: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value = cpuState.getWP(); } }); break; case Inst9900.Istst: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value = cpuState.getStatus().get(); } }); break; case Inst9900.Ilwpi: changes.push(new BaseInterpret(inst, mos1) { short prevWP; @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { prevWP = cpuState.getWP(); cpuState.setWP(mos1.value); } @Override protected void doRevert(CpuState9900 cpuState, Status9900 status) { super.doRevert(cpuState, status); cpuState.setWP(prevWP); } }); break; case Inst9900.Ilimi: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { cpuState.getStatus().setIntMask(mos1.value); } }); break; case Inst9900.Iidle: changes.push(new BaseInterpret(inst) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { cpu.setIdle(true); } @Override protected void doRevert(CpuState9900 cpuState, Status9900 status) { super.doRevert(cpuState, status); cpu.setIdle(false); } }); break; case Inst9900.Irset: changes.push(new BaseInterpret(inst) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { //cpu.rset(); // TODO } }); break; case Inst9900.Irtwp: changes.push(new Changes.RestoreContext()); break; case Inst9900.Ickon: changes.push(new BaseInterpret(inst) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { // TODO } }); break; case Inst9900.Ickof: changes.push(new BaseInterpret(inst) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { // TODO } }); break; case Inst9900.Ilrex: changes.push(new BaseInterpret(inst) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { // TODO } }); break; case Inst9900.Iblwp: changes.push(new Changes.Blwp(mos1)); break; case Inst9900.Ib: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { cpuState.setPC(mos1.value); } }); break; case Inst9900.Ix: { // placeholder for replacement instruction, after this one final int pos = changes.getCount() + 1; changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { ChangeBlock9900 newBlock = new ChangeBlock9900(changes.cpu, inst.pc, mos1.value); if (newBlock.inst.getSize() > 2) newBlock.push(new AdvancePC(changes, newBlock.inst.getSize() - 2)); newBlock.appendOperandFetch(); newBlock.appendInstructionExecute(); newBlock.appendFlush(); changes.instrFetchCycles += newBlock.instrFetchCycles; changes.insert(pos, newBlock); } }); break; } case Inst9900.Iclr: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value = 0; } }); break; case Inst9900.Ineg: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value = (short) -mos1.value; status.set_LAEO(mos1.value); } }); break; case Inst9900.Iinv: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value = (short) ~mos1.value; status.set_LAE(mos1.value); } }); break; case Inst9900.Iinc: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_ADD_LAECO(mos1.value, (short) 1); mos1.value ++; } }); break; case Inst9900.Iinct: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_ADD_LAECO(mos1.value, (short) 2); mos1.value += 2; } }); break; case Inst9900.Idec: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_ADD_LAECO(mos1.value, (short) -1); mos1.value --; } }); break; case Inst9900.Idect: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_ADD_LAECO(mos1.value, (short) -2); mos1.value -= 2; } }); break; case Inst9900.Ibl: changes.push(new BaseInterpret(inst, mos1) { int prevR11; @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { prevR11 = cpuState.readWorkspaceRegister(11); cpuState.writeWorkspaceRegister(11, cpuState.getPC()); cpuState.setPC(mos1.value); } @Override protected void doRevert(CpuState9900 cpuState, Status9900 status) { super.doRevert(cpuState, status); cpuState.setRegister(11, prevR11); } }); break; case Inst9900.Iswpb: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value = (short) (mos1.value >> 8 & 0xff | mos1.value << 8 & 0xff00); } }); break; case Inst9900.Iseto: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos1.value = -1; } }); break; case Inst9900.Iabs: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_LAEO(mos1.value); if ((mos1.value & 0x8000) != 0) { mos1.value = (short) -mos1.value; changes.counts.addExecute(14); } else { changes.counts.addExecute(12); } } }); break; case Inst9900.Isra: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_SHIFT_RIGHT_C(mos1.value, mos2.value); mos1.value = (short) (mos1.value >> mos2.value); status.set_LAE(mos1.value); countShifts(changes.counts, mos2.value, mos2.mop.isRegister()); } }); break; case Inst9900.Isrl: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_SHIFT_RIGHT_C(mos1.value, mos2.value); mos1.value = (short) ((mos1.value & 0xffff) >> mos2.value); status.set_LAE(mos1.value); countShifts(changes.counts, mos2.value, mos2.mop.isRegister()); } }); break; case Inst9900.Isla: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_SHIFT_LEFT_CO(mos1.value, mos2.value); mos1.value = (short) (mos1.value << mos2.value); status.set_LAE(mos1.value); countShifts(changes.counts, mos2.value, mos2.mop.isRegister()); } }); break; case Inst9900.Isrc: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_SHIFT_RIGHT_C(mos1.value, mos2.value); mos1.value = (short) ((mos1.value & 0xffff) >> mos2.value | (mos1.value & 0xffff) << 16 - mos2.value); status.set_LAE(mos1.value); countShifts(changes.counts, mos2.value, mos2.mop.isRegister()); } }); break; case Inst9900.Ijmp: changes.push(new Changes.JumpPC(changes, mos1.value - 2, 10)); break; case Inst9900.Ijlt: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return status.isLT(); } }); break; case Inst9900.Ijle: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return status.isLE(); } }); break; case Inst9900.Ijeq: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return status.isEQ(); } }); break; case Inst9900.Ijhe: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return status.isHE(); } }); break; case Inst9900.Ijgt: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return status.isGT(); } }); break; case Inst9900.Ijne: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return status.isNE(); } }); break; case Inst9900.Ijnc: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return !status.isC(); } }); break; case Inst9900.Ijoc: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return status.isC(); } }); break; case Inst9900.Ijno: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return !status.isO(); } }); break; case Inst9900.Ijl: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return status.isL(); } }); break; case Inst9900.Ijh: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { return status.isH(); } }); break; case Inst9900.Ijop: changes.push(new BaseJump(changes, inst, mos1) { protected boolean test(Status9900 status) { // jump on ODD parity return (status.isP()); } }); break; case Inst9900.Isbo: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { if (cruHandler != null) cruHandler.writeBits((mos1.value<<1), 1, 1); } }); break; case Inst9900.Isbz: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { if (cruHandler != null) cruHandler.writeBits((mos1.value<<1), 0, 1); } }); break; case Inst9900.Itb: changes.push(new BaseInterpret(inst, mos1) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { if (cruHandler != null) { mos1.value = (short) cruHandler.readBits((mos1.value<<1), 1); } status.set_E(mos1.value == 1); } }); break; case Inst9900.Icoc: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos2.value = (short) (mos1.value & mos2.value); status.set_E(mos1.value == mos2.value); } }); break; case Inst9900.Iczc: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos2.value = (short) (mos1.value & ~mos2.value); status.set_E(mos1.value == mos2.value); } }); break; case Inst9900.Ixor: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos2.value ^= mos1.value; status.set_LAE(mos2.value); } }); break; case Inst9900.Ixop: changes.push(new Changes.Xop(mos1, mos2)); break; case Inst9900.Impy: changes.push(new BaseInterpret(inst, mos1, mos2, mos3) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { int val = (mos1.value & 0xffff) * (mos2.value & 0xffff); mos3.value = (short) val; mos2.value = (short) (val >> 16); } }); break; case Inst9900.Idiv: changes.push(new BaseInterpret(inst, mos1, mos2, mos3) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { if ((mos1.value & 0xffff) > (mos2.value & 0xffff)) { int low = mos3.value & 0xffff; long dval = ((mos2.value & 0xffff) << 16 | (low & 0xffff)) & 0xffffffffL; try { mos2.value = (short) (dval / (mos1.value & 0xffff)); mos3.value = (short) (dval % (mos1.value & 0xffff)); status.set_O(false); int a; int b; a = mos2.value & 0xffff; b = mos3.value & 0xffff; changes.counts.addExecute(92 + + BinaryUtils.maxBitNumber((a << 16) | b)); } catch (ArithmeticException e) { status.set_O(true); changes.counts.addExecute(16); } } else { status.set_O(true); changes.counts.addExecute(16); } } }); break; case Inst9900.Ildcr: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { int cruAddr = cpuState.readWorkspaceRegister(12); if (cruHandler != null) cruHandler.writeBits(cruAddr, mos1.value, mos2.value); changes.counts.addExecute(20 + 2 * mos2.value); } }); break; case Inst9900.Istcr: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { int cruAddr = cpuState.readWorkspaceRegister(12); if (cruHandler != null) mos1.value = (short) cruHandler.readBits(cruAddr, mos2.value); int disp = mos2.value; if (disp > 0 && disp <= 7) changes.counts.addExecute(42); else if (disp == 8) changes.counts.addExecute(44); else if (disp > 0 && disp <= 15) changes.counts.addExecute(58); else /* disp == 0 or 16 */ changes.counts.addExecute(60); status.set_LAE(mos1.value); } }); break; case Inst9900.Iszc: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos2.value &= ~mos1.value; status.set_LAE(mos2.value); } }); break; case Inst9900.Iszcb: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos2.value &= ~mos1.value; status.set_BYTE_LAEP((byte) mos2.value); } }); break; case Inst9900.Is: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_SUB_LAECO(mos2.value, mos1.value); mos2.value -= mos1.value; } }); break; case Inst9900.Isb: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_SUB_BYTE_LAECOP((byte) mos2.value, (byte) mos1.value); mos2.value -= mos1.value; } }); break; case Inst9900.Ic: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_CMP(mos1.value, mos2.value); } }); break; case Inst9900.Icb: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_BYTE_CMP((byte) mos1.value, (byte) mos2.value); } }); break; case Inst9900.Ia: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_ADD_LAECO(mos2.value, mos1.value); mos2.value += mos1.value; } }); break; case Inst9900.Iab: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { status.set_ADD_BYTE_LAECOP((byte) mos2.value, (byte) mos1.value); mos2.value += mos1.value; } }); break; case Inst9900.Imov: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos2.value = mos1.value; status.set_LAE(mos2.value); } }); break; case Inst9900.Imovb: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos2.value = mos1.value; status.set_BYTE_LAEP((byte) mos2.value); } }); break; case Inst9900.Isoc: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos2.value |= mos1.value; status.set_LAE(mos2.value); } }); break; case Inst9900.Isocb: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { mos2.value |= mos1.value; status.set_BYTE_LAEP((byte) mos2.value); } }); break; case InstTableCommon.Idsr: changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { IDsrManager dsrManager = null; if (cpu.getMachine() instanceof TI99Machine) dsrManager = ((TI99Machine) cpu.getMachine()).getDsrManager(); if (dsrManager != null) dsrManager.handleDSR(cpuState, changes); } }); break; case InstTableCommon.Iticks: { changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { int count = cpu.getTickCount(); mos1.value = (short) (count >> 16); mos2.value = (short) (count & 0xffff); } }); break; } case InstTableCommon.Idbg: { changes.push(new BaseInterpret(inst, mos1, mos2) { @Override protected void doApply(CpuState9900 cpuState, Status9900 status) { if (cpu != null) cpu.addDebugCount(mos1.value == 0 ? 1 : -1); } @Override protected void doRevert(CpuState9900 cpuState, Status9900 status) { super.doRevert(cpuState, status); if (cpu != null) cpu.addDebugCount(mos1.value == 0 ? -1 : 1); } }); break; } default: throw new IllegalStateException(); } } /** * @param counts * @param value * @param register */ protected static void countShifts(CycleCounts counts, short value, boolean register) { counts.addExecute(2 * value); if (!register) { counts.addExecute(12); } else { counts.addExecute(20); } } }