1
0
mirror of https://github.com/sethm/symon.git synced 2024-06-01 23:41:27 +00:00

define an interface for supported machines and have the SymonMachine as first implementation. Remove machine-specific definitions out of the Simulator class.

This commit is contained in:
Maik Merten 2014-07-17 19:42:51 +02:00
parent 8f52e1da1e
commit 14e8ea596c
3 changed files with 258 additions and 90 deletions

View File

@ -23,15 +23,13 @@
package com.loomcom.symon;
import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Acia6551;
import com.loomcom.symon.devices.Crtc;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.devices.Via;
import com.loomcom.symon.exceptions.FifoUnderrunException;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
import com.loomcom.symon.exceptions.SymonException;
import com.loomcom.symon.machines.Machine;
import com.loomcom.symon.machines.SymonMachine;
import com.loomcom.symon.ui.*;
import com.loomcom.symon.ui.Console;
@ -53,29 +51,6 @@ import java.util.logging.Logger;
*/
public class Simulator {
// Constants used by the simulated system. These define the memory map.
private static final int BUS_BOTTOM = 0x0000;
private static final int BUS_TOP = 0xffff;
// 32K of RAM from $0000 - $7FFF
private static final int MEMORY_BASE = 0x0000;
private static final int MEMORY_SIZE = 0x8000;
// VIA at $8000-$800F
private static final int VIA_BASE = 0x8000;
// ACIA at $8800-$8803
private static final int ACIA_BASE = 0x8800;
// CRTC at $9000-$9001
private static final int CRTC_BASE = 0x9000;
private static final int VIDEO_RAM_BASE = 0x7000;
// 16KB ROM at $C000-$FFFF
private static final int ROM_BASE = 0xC000;
private static final int ROM_SIZE = 0x4000;
// UI constants
private static final int DEFAULT_FONT_SIZE = 12;
private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE);
@ -94,14 +69,8 @@ public class Simulator {
private final static Logger logger = Logger.getLogger(Simulator.class.getName());
// The simulated peripherals
private final Bus bus;
private final Cpu cpu;
private final Acia acia;
private final Via via;
private final Crtc crtc;
private final Memory ram;
private Memory rom;
// The simulated machine
private Machine machine;
// Number of CPU steps between CRT repaints.
// TODO: Dynamically refresh the value at runtime based on performance figures to reach ~ 30fps.
@ -153,33 +122,8 @@ public class Simulator {
*/
private static final String[] STEPS = {"1", "5", "10", "20", "50", "100"};
public Simulator() throws MemoryRangeException, IOException {
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
this.via = new Via(VIA_BASE);
this.acia = new Acia6551(ACIA_BASE);
this.crtc = new Crtc(CRTC_BASE, ram);
bus.addCpu(cpu);
bus.addDevice(ram);
bus.addDevice(via);
bus.addDevice(acia);
bus.addDevice(crtc);
// TODO: Make this configurable, of course.
File romImage = new File("rom.bin");
if (romImage.canRead()) {
logger.info("Loading ROM image from file " + romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
} else {
logger.info("Default ROM file " + romImage +
" not found, loading empty R/W memory image.");
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
}
bus.addDevice(rom);
public Simulator() throws Exception {
this.machine = new SymonMachine();
}
/**
@ -267,10 +211,10 @@ public class Simulator {
traceLog = new TraceLog();
// Prepare the memory window
memoryWindow = new MemoryWindow(bus);
memoryWindow = new MemoryWindow(machine.getBus());
// Composite Video and 6545 CRTC
videoWindow = new VideoWindow(crtc, 2, 2);
videoWindow = new VideoWindow(machine.getCrtc(), 2, 2);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
@ -313,7 +257,7 @@ public class Simulator {
try {
logger.log(Level.INFO, "Reset requested. Resetting CPU.");
// Reset and clear memory
cpu.reset();
machine.getCpu().reset();
// Clear the console.
console.reset();
// Reset the trace log.
@ -322,7 +266,7 @@ public class Simulator {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Now update the state
statusPane.updateState(cpu);
statusPane.updateState(machine.getCpu());
memoryWindow.updateState();
}
});
@ -344,7 +288,7 @@ public class Simulator {
if (traceLog.isVisible()) {
traceLog.refresh();
}
statusPane.updateState(cpu);
statusPane.updateState(machine.getCpu());
memoryWindow.updateState();
}
});
@ -358,15 +302,15 @@ public class Simulator {
* Perform a single step of the simulated system.
*/
private void step() throws MemoryAccessException {
cpu.step();
machine.getCpu().step();
traceLog.append(cpu.getCpuState());
traceLog.append(machine.getCpu().getCpuState());
// Read from the ACIA and immediately update the console if there's
// output ready.
if (acia.hasTxChar()) {
if (machine.getAcia().hasTxChar()) {
// This is thread-safe
console.print(Character.toString((char) acia.txRead()));
console.print(Character.toString((char) machine.getAcia().txRead()));
console.repaint();
}
@ -374,7 +318,7 @@ public class Simulator {
// TODO: Interrupt handling.
try {
if (console.hasInput()) {
acia.rxWrite((int) console.readInputChar());
machine.getAcia().rxWrite((int) console.readInputChar());
}
} catch (FifoUnderrunException ex) {
logger.severe("Console type-ahead buffer underrun!");
@ -392,7 +336,7 @@ public class Simulator {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Now update the state
statusPane.updateState(cpu);
statusPane.updateState(machine.getCpu());
memoryWindow.updateState();
}
});
@ -407,7 +351,7 @@ public class Simulator {
private void loadProgram(byte[] program, int startAddress) throws MemoryAccessException {
int addr = startAddress, i;
for (i = 0; i < program.length; i++) {
bus.write(addr++, program[i] & 0xff);
machine.getBus().write(addr++, program[i] & 0xff);
}
logger.log(Level.INFO, "Loaded " + i + " bytes at address 0x" +
@ -415,16 +359,16 @@ public class Simulator {
// After loading, be sure to reset and
// Reset (but don't clear memory, naturally)
cpu.reset();
machine.getCpu().reset();
// Reset the stack program counter
cpu.setProgramCounter(preferences.getProgramStartAddress());
machine.getCpu().setProgramCounter(preferences.getProgramStartAddress());
// Immediately update the UI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Now update the state
statusPane.updateState(cpu);
statusPane.updateState(machine.getCpu());
memoryWindow.updateState();
}
});
@ -493,7 +437,7 @@ public class Simulator {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
statusPane.updateState(cpu);
statusPane.updateState(machine.getCpu());
memoryWindow.updateState();
runStopButton.setText("Run");
stepButton.setEnabled(true);
@ -516,7 +460,7 @@ public class Simulator {
* @return True if the run loop should proceed to the next step.
*/
private boolean shouldContinue() {
return isRunning && !(preferences.getHaltOnBreak() && cpu.getInstruction() == 0x00);
return isRunning && !(preferences.getHaltOnBreak() && machine.getCpu().getInstruction() == 0x00);
}
}
@ -536,7 +480,7 @@ public class Simulator {
if (f.canRead()) {
long fileSize = f.length();
if (fileSize > MEMORY_SIZE) {
if (fileSize > machine.getMemorySize()) {
throw new IOException("Program will not fit in available memory.");
} else {
byte[] program = new byte[(int) fileSize];
@ -583,22 +527,19 @@ public class Simulator {
if (romFile.canRead()) {
long fileSize = romFile.length();
if (fileSize != ROM_SIZE) {
throw new IOException("ROM file must be exactly " + String.valueOf(ROM_SIZE) + " bytes.");
if (fileSize != machine.getRomSize()) {
throw new IOException("ROM file must be exactly " + String.valueOf(machine.getRomSize()) + " bytes.");
} else {
if (rom != null) {
// Unload the existing ROM image.
bus.removeDevice(rom);
}
// Load the new ROM image
rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romFile);
bus.addDevice(rom);
Memory rom = Memory.makeROM(machine.getRomBase(), machine.getRomBase() + machine.getRomSize() - 1, romFile);
machine.setRom(rom);
// Now, reset
cpu.reset();
machine.getCpu().reset();
logger.log(Level.INFO, "ROM File `" + romFile.getName() + "' loaded at " +
String.format("0x%04X", ROM_BASE));
String.format("0x%04X", machine.getRomBase()));
}
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2008-2014 Seth J. Morabito <sethm@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.machines;
import com.loomcom.symon.Bus;
import com.loomcom.symon.Cpu;
import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Crtc;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.devices.Via;
import com.loomcom.symon.exceptions.MemoryRangeException;
public interface Machine {
public Bus getBus();
public Cpu getCpu();
public Memory getRam();
public Acia getAcia();
public Via getVia();
public Crtc getCrtc();
public Memory getRom();
public void setRom(Memory rom) throws MemoryRangeException;
public int getRomBase();
public int getRomSize();
public int getMemorySize();
}

View File

@ -0,0 +1,167 @@
/*
* Copyright (c) 2008-2014 Seth J. Morabito <sethm@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.machines;
import com.loomcom.symon.Bus;
import com.loomcom.symon.Cpu;
import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Acia6551;
import com.loomcom.symon.devices.Crtc;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.devices.Via;
import com.loomcom.symon.exceptions.MemoryRangeException;
import java.io.File;
import java.util.logging.Logger;
public class SymonMachine implements Machine {
private final static Logger logger = Logger.getLogger(SymonMachine.class.getName());
// Constants used by the simulated system. These define the memory map.
private static final int BUS_BOTTOM = 0x0000;
private static final int BUS_TOP = 0xffff;
// 32K of RAM from $0000 - $7FFF
private static final int MEMORY_BASE = 0x0000;
private static final int MEMORY_SIZE = 0x8000;
// VIA at $8000-$800F
private static final int VIA_BASE = 0x8000;
// ACIA at $8800-$8803
private static final int ACIA_BASE = 0x8800;
// CRTC at $9000-$9001
private static final int CRTC_BASE = 0x9000;
private static final int VIDEO_RAM_BASE = 0x7000;
// 16KB ROM at $C000-$FFFF
private static final int ROM_BASE = 0xC000;
private static final int ROM_SIZE = 0x4000;
// The simulated peripherals
private final Bus bus;
private final Cpu cpu;
private final Acia acia;
private final Via via;
private final Crtc crtc;
private final Memory ram;
private Memory rom;
public SymonMachine() throws Exception {
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
this.via = new Via(VIA_BASE);
this.acia = new Acia6551(ACIA_BASE);
this.crtc = new Crtc(CRTC_BASE, ram);
bus.addCpu(cpu);
bus.addDevice(ram);
bus.addDevice(via);
bus.addDevice(acia);
bus.addDevice(crtc);
// TODO: Make this configurable, of course.
File romImage = new File("rom.bin");
if (romImage.canRead()) {
logger.info("Loading ROM image from file " + romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
} else {
logger.info("Default ROM file " + romImage +
" not found, loading empty R/W memory image.");
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
}
bus.addDevice(rom);
}
@Override
public Bus getBus() {
return bus;
}
@Override
public Cpu getCpu() {
return cpu;
}
@Override
public Memory getRam() {
return ram;
}
@Override
public Acia getAcia() {
return acia;
}
@Override
public Via getVia() {
return via;
}
@Override
public Crtc getCrtc() {
return crtc;
}
@Override
public Memory getRom() {
return rom;
}
public void setRom(Memory rom) throws MemoryRangeException {
if(this.rom != null) {
bus.removeDevice(this.rom);
}
this.rom = rom;
bus.addDevice(this.rom);
}
@Override
public int getRomBase() {
return ROM_BASE;
}
@Override
public int getRomSize() {
return ROM_SIZE;
}
@Override
public int getMemorySize() {
return MEMORY_SIZE;
}
}