/* Cpu9900.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.cpu; import v9t9.common.asm.IDecompilePhase; import v9t9.common.asm.IInstructionFactory; import v9t9.common.asm.IRawInstructionFactory; import v9t9.common.client.ISettingsHandler; import v9t9.common.cpu.ChangeBlock; import v9t9.common.cpu.IExecutor; import v9t9.common.cpu.IInstructionEffectLabelProvider; import v9t9.common.hardware.ICruChip; import v9t9.common.hardware.IVdpChip; import v9t9.common.machine.IMachine; import v9t9.common.settings.SettingSchema; import v9t9.common.settings.Settings; import v9t9.engine.Dumper; import v9t9.engine.compiler.CodeBlockCompilerStrategy; import v9t9.engine.cpu.CpuBase; import v9t9.engine.cpu.Executor; import v9t9.machine.ti99.asm.HighLevelCodeInfo; import v9t9.machine.ti99.asm.InstructionFactory9900; import v9t9.machine.ti99.asm.RawInstructionFactory9900; import v9t9.machine.ti99.asm.TopDownPhase; import v9t9.machine.ti99.compiler.Compiler9900; import v9t9.machine.ti99.interpreter.Interpreter9900; import ejs.base.properties.IProperty; import ejs.base.settings.ISettingSection; import ejs.base.utils.HexUtils; /** * The 9900 engine. * * @author ejs */ public class Cpu9900 extends CpuBase { public static final SettingSchema settingForceAllIntsToLevel1 = new SettingSchema( ISettingsHandler.MACHINE, "ForceAllIntsToLevel1", Boolean.TRUE); public static final int PIN_INTREQ = 1 << 31; public static final int PIN_LOAD = 1 << 3; public static final int PIN_RESET = 1 << 5; /** When intreq, the interrupt level (IC* bits on the TMS9900). */ private byte ic; public static final int REG_PC = 16; public static final int REG_ST = 17; public static final int REG_WP = 18; public static final int TMS_9900_BASE_CYCLES_PER_SEC = 3000000; /* interrupt pins */ public static final int INTLEVEL_RESET = 0; public static final int INTLEVEL_LOAD = 1; public static final int INTLEVEL_INTREQ = 2; /** * When set, implement TI-99/4A behavior where all interrupts * are perceived as level 1. */ private IProperty forceIcTo1; private final IVdpChip vdp; private Dumper dumper; public Cpu9900(IMachine machine, IVdpChip vdp) { super(machine, new CpuState9900(machine.getConsole())); this.vdp = vdp; this.dumper = new Dumper(Settings.getSettings(machine), settingDumpInstructions, settingDumpFullInstructions); forceIcTo1 = Settings.get(machine, settingForceAllIntsToLevel1); cyclesPerSecond.setInt(TMS_9900_BASE_CYCLES_PER_SEC); } /* (non-Javadoc) * @see v9t9.emulator.runtime.cpu.Cpu#getBaseCyclesPerSec() */ @Override public int getBaseCyclesPerSec() { return TMS_9900_BASE_CYCLES_PER_SEC; } /* (non-Javadoc) * @see v9t9.emulator.runtime.Cpu#resetInterruptRequest() */ public void resetInterruptRequest() { pins &= ~PIN_INTREQ; } /* (non-Javadoc) * @see v9t9.emulator.runtime.Cpu#setInterruptRequest(byte) */ public void setInterruptRequest(byte level) { setPin(PIN_INTREQ); // ic = forceIcTo1.getBoolean() ? 1 : level; } /** * */ public void contextSwitch(short newwp, short newpc) { ((CpuState9900) state).contextSwitch(newwp, newpc); } public void contextSwitch(int addr) { contextSwitch(state.getConsole().readWord(addr), state.getConsole().readWord(addr+2)); } /** * Poll the TMS9901 to see if any interrupts are pending. * @return true if any pending */ public final boolean doCheckInterrupts() { // do not allow interrupts after some instructions if (((CpuState9900) state).areIntsFrozen()) { return false; } vdp.syncVdpInterrupt(machine); ICruChip cruAccess = machine.getCru(); if (cruAccess != null) { //pins &= ~PIN_INTREQ; cruAccess.pollForPins(this); if (cruAccess.isInterruptWaiting()) { ic = forceIcTo1.getBoolean() ? 1 : cruAccess.getInterruptLevel(); if (state.getStatus().getIntMask() >= ic) { pins |= PIN_INTREQ; cruAccess.handlingInterrupt(); return true; } else { //System.out.print('-'); } } } if (((pins & PIN_LOAD + PIN_RESET) != 0)) { //System.out.println("Pins set... " + Integer.toHexString(pins)); return true; } return false; } /* (non-Javadoc) * @see v9t9.emulator.runtime.Cpu#handleInterrupts() */ public final void handleInterrupts() { dumper.info("*** Aborted"); // non-maskable if ((pins & PIN_LOAD) != 0) { // non-maskable // this is ordinarily reset by external hardware, but // we don't yet have a way to scan instruction execution pins &= ~PIN_LOAD; ic = 0; setIdle(false); dumper.info("*** NMI ***"); System.out.println("**** NMI ****"); contextSwitch(0xfffc); cycleCounts.addExecute(22); } else if ((pins & PIN_RESET) != 0) { pins &= ~PIN_RESET; setIdle(false); dumper.info("*** RESET ***"); System.out.println("**** RESET ****"); vdp.readVdpStatus(); state.getStatus().expand((short) 0); contextSwitch(0); cycleCounts.addExecute(26); pins = 0; ic = 0; machine.getExecutor().interpretOneInstruction(); //throw new AbortedException(); } else if ((pins & PIN_INTREQ) != 0) { // System.out.println(System.currentTimeMillis()); if (state.getStatus().getIntMask() >= ic) { // already checked int mask in status // maskable pins &= ~PIN_INTREQ; //System.out.print('='); //interrupts++; contextSwitch(0x4 * ic); cycleCounts.addExecute(22); // no more interrupt until 9901 gives us another ic = 0; setIdle(false); // for now, we need to do this, otherwise the compiled code may check intlevel and immediately ... oh, I dunno machine.getExecutor().interpretOneInstruction(); } else { System.out.print('?'); } } } public void saveState(ISettingSection section) { super.saveState(section); section.put("PC", state.getPC()); section.put("WP", ((CpuState9900) state).getWP()); section.put("status", state.getStatus().flatten()); } public void loadState(ISettingSection section) { if (section == null) { setPin(INTLEVEL_RESET); return; } state.setPC((short) section.getInt("PC")); ((CpuState9900) state).setWP((short) section.getInt("WP")); state.getStatus().expand((short) section.getInt("status")); super.loadState(section); } @Override public String getCurrentStateString() { return "WP=>" + HexUtils.toHex4(((CpuState9900) state).getWP()) + "\t\tST=" +state.getStatus(); } /* (non-Javadoc) * @see v9t9.common.cpu.ICpu#reset() */ @Override public void reset() { //contextSwitch(0); setPin(PIN_RESET); } /* (non-Javadoc) * @see v9t9.emulator.runtime.cpu.Cpu#nmi() */ @Override public void nmi() { setPin(PIN_LOAD); } /* (non-Javadoc) * @see v9t9.emulator.runtime.cpu.Cpu#irq() */ // @Override // public void irq() { // setPin(PIN_INTREQ); // } @Override public boolean shouldDebugCompiledCode(short pc) { return ((pc >= 0x6000 && pc < 0x8000) && Settings.get(this, Compiler9900.settingDumpModuleRomInstructions).getBoolean()); } /** * @return */ public int getWP() { return ((CpuState9900) state).getWP(); } /* (non-Javadoc) * @see v9t9.emulator.runtime.cpu.CpuState#getPC() */ public short getPC() { return state.getPC(); } /* (non-Javadoc) * @see v9t9.emulator.runtime.cpu.CpuState#getST() */ public short getST() { return state.getST(); } /* (non-Javadoc) * @see v9t9.emulator.runtime.cpu.CpuState#setPC(short) */ public void setPC(short pc) { state.setPC(pc); } /* (non-Javadoc) * @see v9t9.emulator.runtime.cpu.CpuState#setST(short) */ public void setST(short st) { state.setST(st); } /** * @param wp */ public void setWP(short wp) { ((CpuState9900) state).setWP(wp); } /* (non-Javadoc) * @see v9t9.engine.cpu.CpuBase#applyCycles() * TODO: this should not depend on vdp */ @Override public void applyCycles() { if (vdp != null) vdp.addCpuCycles(cycleCounts.getTotal()); super.applyCycles(); } /* (non-Javadoc) * @see v9t9.common.cpu.ICpu#applyCycles(int) */ @Override public void applyCycles(int cycles) { if (vdp != null) vdp.addCpuCycles(cycles); super.applyCycles(cycles); } /* (non-Javadoc) * @see v9t9.common.cpu.ICpu#getInstructionFactory() */ @Override public IRawInstructionFactory getRawInstructionFactory() { return RawInstructionFactory9900.INSTANCE; } /* (non-Javadoc) * @see v9t9.common.cpu.ICpu#getInstructionFactory() */ @Override public IInstructionFactory getInstructionFactory() { return InstructionFactory9900.INSTANCE; } /* (non-Javadoc) * @see v9t9.common.cpu.ICpu#createExecutor(v9t9.common.cpu.ICpuMetrics) */ @Override public IExecutor createExecutor() { return new Executor(machine, this, // new Interpreter9900((IMachine) getMachine()), new Interpreter9900((IMachine) getMachine()), new Compiler9900(this), new CodeBlockCompilerStrategy(), new DumpFullReporter9900(this), new DumpReporter9900(this)); } /* (non-Javadoc) * @see v9t9.common.cpu.ICpu#createDecompiler() */ @Override public IDecompilePhase createDecompiler() { return new TopDownPhase(getState(), new HighLevelCodeInfo(getState(), new InstructionFactory9900())); } /* (non-Javadoc) * @see v9t9.common.cpu.ICpu#getInstructionEffectLabelProvider() */ @Override public IInstructionEffectLabelProvider createInstructionEffectLabelProvider() { return new InstructionEffectLabelProvider9900(); } @Override public ChangeBlock createChangeBlock(int pc) { synchronized (machine.getExecutor().getExecutionLock()) { ChangeBlock9900 block = new ChangeBlock9900(this, pc); block.generate(); return block; } } }