2017-12-28 16:40:15 +00:00
/ *
* 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 {
private static final Logger LOG = Logger . getLogger ( CPU . class . getName ( ) ) ;
public CPU ( Computer computer ) {
super ( computer ) ;
}
@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 ( ) {
2021-11-09 17:20:19 +00:00
whileSuspended ( ( ) - > {
ArrayList < String > newLog = new ArrayList < > ( ) ;
ArrayList < String > oldLog = traceLog ;
traceLog = newLog ;
LOG . log ( Level . INFO , " Most recent {0} instructions: " , traceLength ) ;
oldLog . forEach ( LOG : : info ) ;
oldLog . clear ( ) ;
} ) ;
2017-12-28 16:40:15 +00:00
}
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 ( ) ) {
2021-11-09 17:20:19 +00:00
if ( debugger . getBreakpoints ( ) . contains ( getProgramCounter ( ) ) ) {
2017-12-28 16:40:15 +00:00
debugger . setActive ( true ) ;
2021-11-09 17:20:19 +00:00
}
2017-12-28 16:40:15 +00:00
}
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.
2023-07-03 20:44:23 +00:00
Thread . onSpinWait ( ) ;
2017-12-28 16:40:15 +00:00
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 ( ) {
}
abstract public void JSR ( int pointer ) ;
boolean singleTraceEnabled = false ;
public String lastTrace = " START " ;
public void performSingleTrace ( ) {
singleTraceEnabled = true ;
}
public boolean isSingleTraceEnabled ( ) {
return singleTraceEnabled ;
}
public String getLastTrace ( ) {
return lastTrace ;
}
public void captureSingleTrace ( String trace ) {
lastTrace = trace ;
singleTraceEnabled = false ;
}
abstract public void clearState ( ) ;
}