Files
ksymon/src/main/java/com/loomcom/symon/Simulator.java
T

158 lines
4.3 KiB
Java

package com.loomcom.symon;
import java.util.logging.*;
import com.loomcom.symon.devices.*;
import com.loomcom.symon.exceptions.*;
import com.loomcom.symon.ui.UiUpdater;
import javax.swing.*;
/**
* Main entry point and control for the Symon Simulator.
* This class is responsible for creating the UI and starting
* the IO threads that pass data to and from the simulated
* Bus and Cpu.
*/
public class Simulator implements Runnable {
private final static Logger logger = Logger.getLogger(Simulator.class.getName());
private Bus bus;
private Cpu cpu;
private Acia acia;
private Memory ram;
private Memory rom;
private boolean isRunning = false;
private int updatesRequested = 0;
private UiUpdater uiUpdater;
private static final int BUS_BOTTOM = 0x0000;
private static final int BUS_TOP = 0xffff;
private static final int MEMORY_BASE = 0x0000;
private static final int MEMORY_SIZE = 0xc000; // 48 KB
private static final int ROM_BASE = 0xe000;
private static final int ROM_SIZE = 0x2000; // 8 KB
public static final int ACIA_BASE = 0xc000;
private static final int MAX_REQUESTS_BETWEEN_UPDATES = 25000;
public Simulator() throws MemoryRangeException {
this.acia = new Acia(ACIA_BASE);
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_SIZE, false);
// TODO: Load this ROM from a file, naturally!
this.rom = new Memory(ROM_BASE, ROM_SIZE, false);
bus.addCpu(cpu);
bus.addDevice(acia);
bus.addDevice(ram);
bus.addDevice(rom);
}
public void loadProgram(byte[] program, int startAddress) throws MemoryAccessException {
cpu.setResetVector(startAddress);
int addr = startAddress, i;
for (i = 0; i < program.length; i++) {
bus.write(addr++, program[i] & 0xff);
}
logger.log(Level.INFO, "Loaded " + i + " bytes at address 0x" +
Integer.toString(startAddress, 16));
}
public void setUiUpdater(UiUpdater uiUpdater) {
this.uiUpdater = uiUpdater;
}
public boolean isRunning() {
return isRunning;
}
public void requestStop() {
isRunning = false;
}
public void reset() throws MemoryAccessException {
cpu.reset();
}
public void clearMemory() {
ram.fill(0x00);
}
public int getProcessorStatus() {
return cpu.getProcessorStatus();
}
public Cpu getCpu() {
return this.cpu;
}
public long memorySize() {
return MEMORY_SIZE;
}
public void run() {
logger.log(Level.INFO, "Entering 'run' on main Simulator thread");
isRunning = true;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
uiUpdater.simulatorDidStart();
}
});
try {
while (isRunning && !cpu.getBreakFlag()) {
step();
}
} catch (SymonException ex) {
logger.log(Level.SEVERE, "Exception in main simulator run thread. Exiting run.");
ex.printStackTrace();
}
logger.log(Level.INFO, "Exiting 'run'. BREAK=" + cpu.getBreakBit() + "; RUN_FLAG=" + isRunning);
isRunning = false;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
uiUpdater.simulatorDidStop();
}
});
}
public void step() throws MemoryAccessException, FifoUnderrunException {
cpu.step();
// TODO: ACIA interrupt handling. For now, poll ACIA on each step.
// Read from the ACIA and add to the UiUpdater buffer
while (acia.hasTxChar()) {
uiUpdater.consoleWrite(acia.txRead());
}
// This is a very expensive update, and we're doing it without
// a delay, so we don't want to overwhelm the Swing event processing thread
// with requests. Limit the number of ui updates that can be performed.
if (updatesRequested++ > MAX_REQUESTS_BETWEEN_UPDATES) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
uiUpdater.updateUi();
}
});
updatesRequested = 0;
}
}
}