jace/src/main/java/jace/core/CPU.java

167 lines
5.3 KiB
Java

/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace.core;
import jace.config.ConfigurableField;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* CPU is a vague abstraction of a CPU. It is defined as something which can be
* debugged or traced. It has a program counter which can be incremented or
* change. Most importantly, it is a device which does something on every clock tick.
* Subclasses should implement "executeOpcode" rather than override the tick method.
* Created on January 4, 2007, 7:27 PM
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/
public abstract class CPU extends Device {
/**
* Creates a new instance of CPU
*/
public CPU() {
}
@Override
public String getShortName() {
return "cpu";
}
private Debugger debugger = null;
@ConfigurableField(name = "Enable trace to STDOUT", shortName = "trace")
public boolean trace = false;
public boolean isTraceEnabled() {
return trace;
}
public void setTraceEnabled(boolean t) {
trace = t;
}
@ConfigurableField(name = "Trace length", shortName = "traceSize", description = "Number of most recent trace lines to keep for debugging errors. Zero == disabled")
public int traceLength = 0;
private ArrayList<String> traceLog = new ArrayList<>();
public boolean isLogEnabled() {
return (traceLength > 0);
}
public void log(String line) {
if (!isLogEnabled()) {
return;
}
while (traceLog.size() >= traceLength) {
traceLog.remove(0);
}
traceLog.add(line);
}
public void dumpTrace() {
Computer.pause();
ArrayList<String> newLog = new ArrayList<>();
ArrayList<String> log = traceLog;
traceLog = newLog;
Computer.resume();
System.out.println("Most recent " + traceLength + " instructions:");
log.stream().forEach((s) -> {
System.out.println(s);
});
traceLog.clear();
}
public void setDebug(Debugger d) {
debugger = d;
suspend();
}
public void clearDebug() {
debugger = null;
resume();
}
//@ConfigurableField(name="Program Counter")
public int programCounter = 0;
public int getProgramCounter() {
return programCounter;
}
public void setProgramCounter(int programCounter) {
this.programCounter = 0x00FFFF & programCounter;
}
public void incrementProgramCounter(int amount) {
this.programCounter += amount;
this.programCounter = 0x00FFFF & this.programCounter;
}
/**
* Process a single tick of the main processor clock. Either we're waiting
* to execute the next instruction, or the next instruction is ready to go
*/
@Override
public void tick() {
try {
if (debugger != null) {
if (!debugger.isActive() && debugger.hasBreakpoints()) {
debugger.getBreakpoints().stream().filter((i) -> (i == getProgramCounter())).forEach((_item) -> {
debugger.setActive(true);
});
}
if (debugger.isActive()) {
debugger.updateStatus();
if (!debugger.takeStep()) {
// If the debugger is active and we aren't ready for the next step, sleep and exit
// Without the sleep, this would constitute a very rapid-fire loop and would eat
// an unnecessary amount of CPU.
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(CPU.class.getName()).log(Level.SEVERE, null, ex);
}
return;
}
}
}
} catch (Throwable t) {
// ignore
}
executeOpcode();
}
/*
* Execute the current opcode at the current program counter
*@return number of cycles to wait until next command can be executed
*/
protected abstract void executeOpcode();
public abstract void reset();
public abstract void generateInterrupt();
abstract public void pushPC();
@Override
public void attach() {
}
@Override
public void detach() {
}
}