/*
BaseCruChip.java
(c) 2010-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.engine.hardware;
import java.io.PrintWriter;
import org.apache.log4j.Logger;
import ejs.base.properties.IProperty;
import ejs.base.settings.ISettingSection;
import ejs.base.settings.Logging;
import v9t9.common.cpu.ICpu;
import v9t9.common.hardware.ICruChip;
import v9t9.common.machine.IMachine;
/**
* CRU handlers for the F99 machine.
* @author ejs
*/
public class BaseCruChip implements ICruChip {
static final Logger logger = Logger.getLogger(BaseCruChip.class);
IMachine machine;
/** intlevel for peripheral interrupt */
public int intExt = 1;
/** intlevel for VDP interrupt */
public int intVdp = 2;
/** intlevel for clock interrupt */
public int intClock = 3;
protected int crukeyboardcol;
/** Set to prevent reading alpha lock */
protected boolean alphaLockMask;
protected int clockRegister;
protected int clockDecrementerRegister;
protected int clockReadRegister;
protected boolean suppressClockInterrupts;
protected boolean clockmode;
/** Enabled interrupt mask. Any request to trigger an interrupt not
* enabled in this mask will be ignored.
*/
protected int enabledIntMask;
/** Pending interrupts fired by hardware. */
protected int pendingInts;
/** Currently active interrupts, for which we expect an interrupt handler to
* run and acknowledge. */
protected int currentInts;
protected boolean intreq;
private final int intCount;
private int prevClockRegister;
private IProperty dumpFullInstructions;
public BaseCruChip(IMachine machine, int intCount) {
this.machine = machine;
this.intCount = intCount;
dumpFullInstructions = machine.getSettings().get(ICpu.settingDumpFullInstructions);
reset();
}
protected void log(String msg) {
PrintWriter pw = Logging.getLog(dumpFullInstructions);
if (pw != null)
pw.println("[CRU] " + msg);
}
public void reset() {
enabledIntMask = 0;
intreq = false;
ic = 0xf;
// XXX: reset software pins
currentInts = 0;
pendingInts = 0;
clockRegister = 0;
clockmode = false;
prevCycles = machine.getCpu().getCurrentCycleCount();
}
public int getClockRate() {
if (clockRegister != 0)
return (3000000 / clockRegister / 64);
return 0;
}
protected void resetClock() {
if (clockRegister != prevClockRegister) {
if (clockRegister != 0) {
log("new clock register: " + clockRegister+"; rate = " +
getClockRate() + " Hz");
logger.info("new clock register: " + clockRegister+"; rate = " +
getClockRate() + " Hz");
}
prevClockRegister = clockRegister;
prevCycles = machine.getCpu().getTotalCurrentCycleCount();
}
clockDecrementerRegister = clockRegister;
}
public boolean isInterruptWaiting() {
return intreq;
}
public byte getInterruptLevel() {
return ic;
}
/** When PIN_INTREQ set, the interrupt level (IC* bits on the TMS9900). */
private byte ic;
private long prevCycles;
public void pollForPins(ICpu cpu) {
// interrupts not generated in clock mode
if (clockmode) {
return;
}
// while polling, also handle clock if in I/O mode
final int CYCLES_PER_TICK = 64;
if (clockRegister != 0) {
// this decrements once every N cycles
long nowCycles = cpu.getTotalCurrentCycleCount();
while (prevCycles + CYCLES_PER_TICK < nowCycles) {
prevCycles += CYCLES_PER_TICK;
clockReadRegister = --clockDecrementerRegister;
if (clockDecrementerRegister <= 0) {
if ((enabledIntMask & (1 << intClock)) != 0) {
//logger.debug("tick");
if (!suppressClockInterrupts) {
triggerInterrupt(intClock);
// "When the clock interrupt is active, the clock mask must be written
// to clear the interrupt."
suppressClockInterrupts = true;
}
}
//resetClock();
clockDecrementerRegister = clockRegister;
break;
}
}
}
intreq = false;
if ((pendingInts & enabledIntMask) != 0) {
int intlevel;
intlevel = intCount - 1;
while (intlevel != 0 &&
(((pendingInts & enabledIntMask) & (1 << intlevel)) == 0 ||
((currentInts & enabledIntMask) & (1 << intlevel)) != 0)) {
intlevel--;
}
if (intlevel != 0) {
//System.out.println("Requesting interrupt... "+intlevel+"/"+currentints+"/"+currentints);
// this.currentInts |= 1 << intlevel;
// this.pendingInts &= ~(1 << intlevel);
this.intreq = true;
this.ic = (byte) intlevel;
//cpu.irq();
cpu.setInterruptRequest(ic);
}
}
}
/* (non-Javadoc)
* @see v9t9.common.hardware.ICruChip#handledInterrupt()
*/
@Override
public void handledInterrupt() {
intreq = false;
}
/**
Trigger an interrupt, via hardware.
*/
public void triggerInterrupt(int level) {
if ((enabledIntMask & (1 << level)) != 0) {
if ((pendingInts & (1 << level)) == 0) {
pendingInts |= 1 << level;
machine.getCpu().setIdle(false);
//System.out.println("Hardware triggered interrupt... "+level+"/"+currentints);
}
}
}
public void acknowledgeInterrupt(int level) {
if ((currentInts & (1 << level)) != 0) {
currentInts &= ~(1 << level);
machine.getCpu().acknowledgeInterrupt(level);
} else {
//System.out.println(
// "??? acknowledged unset interrupt... "+level+"/"+currentints+"/"+int9901);
}
}
/* (non-Javadoc)
* @see v9t9.common.hardware.ICruChip#handlingInterrupt()
*/
@Override
public void handlingInterrupt() {
currentInts |= 1 << ic;
pendingInts &= ~(1 << ic);
}
public void saveState(ISettingSection section) {
section.put("EnabledInterrupts", enabledIntMask);
section.put("PendingInterrupts", pendingInts);
section.put("CurrentInterrupts", currentInts);
section.put("KeyboardColumn", crukeyboardcol);
section.put("ClockMode", clockmode);
section.put("ClockRegister", clockRegister);
section.put("ClockReadRegister", clockReadRegister);
section.put("ClockDecrementerRegister", clockDecrementerRegister);
section.put("SuppressClockInterrupts", suppressClockInterrupts);
section.put("AlphaLockMask", alphaLockMask);
section.put("IntReq", intreq);
section.put("IC", ic);
}
public void loadState(ISettingSection section) {
if (section == null) {
reset();
return;
}
enabledIntMask = section.getInt("EnabledInterrupts");
pendingInts = section.getInt("PendingInterrupts");
currentInts = section.getInt("CurrentInterrupts");
crukeyboardcol = section.getInt("KeyboardColumn");
clockmode = section.getBoolean("ClockMode");
clockReadRegister = section.getInt("ClockReadRegister");
clockDecrementerRegister = section.getInt("ClockDecrementerRegister");
clockRegister = section.getInt("ClockRegister");
suppressClockInterrupts = section.getBoolean("SuppressClockInterrupts");
alphaLockMask = section.getBoolean("AlphaLockMask");
intreq = section.getBoolean("IntReq");
ic = (byte) section.getInt("IC");
}
/**
* @return the machine
*/
public IMachine getMachine() {
return machine;
}
}