1
0
mirror of https://github.com/sethm/symon.git synced 2024-12-27 19:30:03 +00:00

First work toward moving to a Swing UI for the simulator.

This commit is contained in:
Seth Morabito 2012-04-22 20:49:18 -07:00
parent b44859e096
commit a1d07bf223
31 changed files with 2765 additions and 2437 deletions

5
.gitignore vendored
View File

@ -1,7 +1,6 @@
*~ *~
*# *#
target target
.classpath
.project
.settings
.DS_Store .DS_Store
.idea
symon.iml

243
pom.xml
View File

@ -1,115 +1,144 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.loomcom.symon</groupId> <groupId>com.loomcom.symon</groupId>
<artifactId>symon</artifactId> <artifactId>symon</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>0.1</version> <version>0.1</version>
<name>symon</name> <name>symon</name>
<url>http://www.loomcom.com/symon</url> <url>http://www.loomcom.com/symon</url>
<repositories> <properties>
<repository> <project.build.sourceEncoding>
<id>jline</id> UTF-8
<name>JLine Project Repository</name> </project.build.sourceEncoding>
<url>http://jline.sourceforge.net/m2repo</url> <project.reporting.outputEncoding>
</repository> UTF-8
</repositories> </project.reporting.outputEncoding>
<dependencies> </properties>
<dependency> <repositories>
<groupId>junit</groupId> <repository>
<artifactId>junit</artifactId> <id>jline</id>
<version>4.7</version> <name>JLine Project Repository</name>
<scope>test</scope> <url>http://jline.sourceforge.net/m2repo</url>
</dependency> </repository>
<dependency> <!-- Loomcom's Maven2 repository -->
<groupId>jline</groupId> <repository>
<artifactId>jline</artifactId> <releases>
<version>0.9.9</version> <enabled>true</enabled>
</dependency> <updatePolicy>always</updatePolicy>
</dependencies> <checksumPolicy>fail</checksumPolicy>
</releases>
<id>com.loomcom</id>
<name>Loom Communications Maven2 Repository</name>
<url>http://www.loomcom.com/maven2</url>
<layout>default</layout>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
<version>0.9.9</version>
</dependency>
<dependency>
<groupId>com.grahamedgecombe.jterminal</groupId>
<artifactId>jterminal</artifactId>
<version>1.0.2-SNAPSHOT</version>
</dependency>
</dependencies>
<build> <build>
<plugins> <plugins>
<!-- Set Java version to Java 1.5 --> <!-- Set Java version to Java 1.5 -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<!-- best lock down version of the plugin too --> <version>2.3.2</version>
<configuration> <!-- best lock down version of the plugin too -->
<source>1.5</source> <configuration>
<target>1.5</target> <source>1.5</source>
</configuration> <target>1.5</target>
</plugin> </configuration>
</plugin>
<!-- Set up Main-Class in the JAR manifest --> <!-- Set up Main-Class in the JAR manifest -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<configuration> <version>2.3.1</version>
<archive> <configuration>
<manifest> <archive>
<mainClass>com.loomcom.symon.Simulator</mainClass> <manifest>
<packageName>com.loomcom.symon</packageName> <mainClass>com.loomcom.symon.MainWindow</mainClass>
</manifest> <packageName>com.loomcom.symon</packageName>
<manifestEntries> </manifest>
<mode>development</mode> <manifestEntries>
<url>${pom.url}</url> <mode>development</mode>
</manifestEntries> <url>${project.url}</url>
</archive> </manifestEntries>
</configuration> </archive>
</plugin> </configuration>
</plugin>
<!-- Cobertura is essential --> <!-- Cobertura is essential -->
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId> <artifactId>cobertura-maven-plugin</artifactId>
<configuration> <version>2.4</version>
<check> <configuration>
<haltOnFailure>false</haltOnFailure> <check>
<regexes> <haltOnFailure>false</haltOnFailure>
<regex> <regexes>
<pattern>com.loomcom.symon.*</pattern> <regex>
<branchRate>90</branchRate> <pattern>com.loomcom.symon.*</pattern>
<lineRate>90</lineRate> <branchRate>90</branchRate>
</regex> <lineRate>90</lineRate>
</regexes> </regex>
</check> </regexes>
<instrumentation> </check>
<includes> <instrumentation>
<include>com/loomcom/symon/*.class</include> <includes>
</includes> <include>com/loomcom/symon/*.class</include>
</instrumentation> </includes>
</configuration> </instrumentation>
<executions> </configuration>
<execution> <executions>
<id>clean</id> <execution>
<phase>pre-site</phase> <id>clean</id>
<goals> <phase>pre-site</phase>
<goal>clean</goal> <goals>
</goals> <goal>clean</goal>
</execution> </goals>
<execution> </execution>
<id>instrument</id> <execution>
<phase>site</phase> <id>instrument</id>
<goals> <phase>site</phase>
<goal>instrument</goal> <goals>
<goal>cobertura</goal> <goal>instrument</goal>
<goal>check</goal> <goal>cobertura</goal>
</goals> <goal>check</goal>
</execution> </goals>
</executions> </execution>
</plugin> </executions>
</plugin>
</plugins> </plugins>
</build> </build>
<reporting> <reporting>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId> <artifactId>cobertura-maven-plugin</artifactId>
</plugin> <version>2.4</version>
</plugins> </plugin>
</reporting> </plugins>
</reporting>
</project> </project>

View File

@ -1,6 +1,7 @@
package com.loomcom.symon; package com.loomcom.symon;
import java.util.*; import java.util.*;
import com.loomcom.symon.devices.*; import com.loomcom.symon.devices.*;
import com.loomcom.symon.exceptions.*; import com.loomcom.symon.exceptions.*;
@ -9,134 +10,140 @@ import com.loomcom.symon.exceptions.*;
*/ */
public class Bus { public class Bus {
// By default, our bus starts at 0, and goes up to 64K // By default, our bus starts at 0, and goes up to 64K
private int startAddress = 0x0000; private int startAddress = 0x0000;
private int endAddress = 0xffff; private int endAddress = 0xffff;
// The CPU // The CPU
private Cpu cpu; private Cpu cpu;
// Ordered list of IO devices. // Ordered list of IO devices.
private SortedSet<Device> devices; private SortedSet<Device> devices;
public Bus(int size) { public Bus(int size) {
this(0, size - 1); this(0, size - 1);
} }
public Bus(int startAddress, int endAddress) { public Bus(int startAddress, int endAddress) {
this.devices = new TreeSet<Device>(); this.devices = new TreeSet<Device>();
this.startAddress = startAddress; this.startAddress = startAddress;
this.endAddress = endAddress; this.endAddress = endAddress;
} }
public int startAddress() { public int startAddress() {
return startAddress; return startAddress;
} }
public int endAddress() { public int endAddress() {
return endAddress; return endAddress;
} }
public void addDevice(Device device) public void addDevice(Device device)
throws MemoryRangeException { throws MemoryRangeException {
// Make sure there's no memory overlap. // Make sure there's no memory overlap.
MemoryRange memRange = device.getMemoryRange(); MemoryRange memRange = device.getMemoryRange();
for (Device d : devices) { for (Device d : devices) {
if (d.getMemoryRange().overlaps(memRange)) { if (d.getMemoryRange().overlaps(memRange)) {
throw new MemoryRangeException("The device being added " + throw new MemoryRangeException("The device being added " +
"overlaps with an existing " + "overlaps with an existing " +
"device."); "device.");
} }
}
// Add the device
device.setBus(this);
devices.add(device);
}
public void addCpu(Cpu cpu) {
cpu.setBus(this);
this.cpu = cpu;
}
/**
* Returns true if the memory map is full, i.e., there are no
* gaps between any IO devices. All memory locations map to some
* device.
*/
public boolean isComplete() {
// Empty maps cannot be complete.
if (devices.isEmpty()) { return false; }
// Loop over devices and ensure they are contiguous.
MemoryRange prev = null;
int i = 0;
int length = devices.size();
for (Device d : devices) {
MemoryRange cur = d.getMemoryRange();
if (i == 0) {
// If the first entry doesn't start at 'startAddress', return false.
if (cur.startAddress() != startAddress) { return false; }
}
if (prev != null && i < length - 1) {
// Otherwise, compare previous map's end against this map's
// endAddress. They must be adjacent!
if (cur.startAddress() - 1 != prev.endAddress()) {
return false;
} }
}
if (i == length - 1) { // Add the device
// If the last entry doesn't end at endAddress, return false; device.setBus(this);
if (cur.endAddress() != endAddress) { return false; } devices.add(device);
}
i++;
prev = cur;
} }
// Must be complete. public void addCpu(Cpu cpu) {
return true; cpu.setBus(this);
} this.cpu = cpu;
public int read(int address) throws MemoryAccessException {
for (Device d : devices) {
MemoryRange range = d.getMemoryRange();
if (range.includes(address)) {
// Compute offset into this device's address space.
int devAddr = address - range.startAddress();
return d.read(devAddr);
}
} }
throw new MemoryAccessException("Read failed! No device at address.");
}
public void write(int address, int value) throws MemoryAccessException { /**
for (Device d : devices) { * Returns true if the memory map is full, i.e., there are no
MemoryRange range = d.getMemoryRange(); * gaps between any IO devices. All memory locations map to some
if (range.includes(address)) { * device.
// Compute offset into this device's address space. */
int devAddr = address - range.startAddress(); public boolean isComplete() {
d.write(devAddr, value); // Empty maps cannot be complete.
return; if (devices.isEmpty()) {
} return false;
}
// Loop over devices and ensure they are contiguous.
MemoryRange prev = null;
int i = 0;
int length = devices.size();
for (Device d : devices) {
MemoryRange cur = d.getMemoryRange();
if (i == 0) {
// If the first entry doesn't start at 'startAddress', return false.
if (cur.startAddress() != startAddress) {
return false;
}
}
if (prev != null && i < length - 1) {
// Otherwise, compare previous map's end against this map's
// endAddress. They must be adjacent!
if (cur.startAddress() - 1 != prev.endAddress()) {
return false;
}
}
if (i == length - 1) {
// If the last entry doesn't end at endAddress, return false;
if (cur.endAddress() != endAddress) {
return false;
}
}
i++;
prev = cur;
}
// Must be complete.
return true;
} }
throw new MemoryAccessException("Write failed! No device at address.");
}
public SortedSet<Device> getDevices() { public int read(int address) throws MemoryAccessException {
// Expose a copy of the device list, not the original for (Device d : devices) {
return new TreeSet<Device>(devices); MemoryRange range = d.getMemoryRange();
} if (range.includes(address)) {
// Compute offset into this device's address space.
public Cpu getCpu() { int devAddr = address - range.startAddress();
return cpu; return d.read(devAddr);
} }
}
public void loadProgram(int... program) throws MemoryAccessException { throw new MemoryAccessException("Read failed! No device at address.");
int address = getCpu().getProgramCounter(); }
int i = 0;
for (int d : program) { public void write(int address, int value) throws MemoryAccessException {
write(address + i++, d); for (Device d : devices) {
MemoryRange range = d.getMemoryRange();
if (range.includes(address)) {
// Compute offset into this device's address space.
int devAddr = address - range.startAddress();
d.write(devAddr, value);
return;
}
}
throw new MemoryAccessException("Write failed! No device at address.");
}
public SortedSet<Device> getDevices() {
// Expose a copy of the device list, not the original
return new TreeSet<Device>(devices);
}
public Cpu getCpu() {
return cpu;
}
public void loadProgram(int... program) throws MemoryAccessException {
int address = getCpu().getProgramCounter();
int i = 0;
for (int d : program) {
write(address + i++, d);
}
} }
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,247 @@
package com.loomcom.symon;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
import com.loomcom.symon.exceptions.SymonException;
import com.loomcom.symon.ui.Console;
import com.loomcom.symon.ui.StatusPane;
import com.loomcom.symon.ui.UiUpdater;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class MainWindow extends JFrame implements ActionListener {
// Hard coded for now.
public static final int PROGRAM_START_ADDRESS = 0x0300;
private final static Logger logger = Logger.getLogger(MainWindow.class.getName());
private final Simulator simulator;
private Thread simulatorThread;
private final Console console;
private final StatusPane statusPane;
private final JButton loadButton;
private final JButton runButton;
private final JButton stepButton;
private final JButton resetButton;
private final JButton quitButton;
private final JFileChooser fileChooser;
private final UiUpdater uiUpdater;
public MainWindow(final Simulator simulator) {
this.setLayout(new BorderLayout());
this.simulator = simulator;
// UI components used for I/O.
this.console = new Console();
this.statusPane = new StatusPane(simulator.getCpu());
JPanel buttonContainer = new JPanel();
JPanel controlsContainer = new JPanel();
// File Chooser
fileChooser = new JFileChooser();
buttonContainer.setLayout(new FlowLayout());
controlsContainer.setLayout(new BorderLayout());
this.loadButton = new JButton("Load");
this.runButton = new JButton("Run");
this.stepButton = new JButton("Step");
this.resetButton = new JButton("Reset");
this.quitButton = new JButton("Quit");
buttonContainer.add(loadButton);
buttonContainer.add(runButton);
buttonContainer.add(stepButton);
buttonContainer.add(resetButton);
buttonContainer.add(quitButton);
controlsContainer.add(buttonContainer, BorderLayout.PAGE_START);
controlsContainer.add(statusPane, BorderLayout.PAGE_END);
console.setBorder(BorderFactory.createBevelBorder(1));
console.setBackground(Color.BLACK);
console.setForeground(Color.WHITE);
console.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
getContentPane().add(console, BorderLayout.CENTER);
getContentPane().add(controlsContainer, BorderLayout.PAGE_END);
quitButton.addActionListener(this);
runButton.addActionListener(this);
stepButton.addActionListener(this);
resetButton.addActionListener(this);
loadButton.addActionListener(this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
console.requestFocus();
// Finally create the UI Updater and hand a reference to the Simulator.
this.uiUpdater = new UiUpdater(this);
// The simulator must always have a reference to the same UI updater.
this.simulator.setUiUpdater(uiUpdater);
}
public void actionPerformed(ActionEvent actionEvent) {
if (actionEvent.getSource() == loadButton) {
handleProgramLoad();
} else if (actionEvent.getSource() == resetButton) {
handleReset();
} else if (actionEvent.getSource() == stepButton) {
handleStep();
} else if (actionEvent.getSource() == quitButton) {
handleQuit();
} else if (actionEvent.getSource() == runButton) {
if (simulator.isRunning()) {
stop();
} else {
start();
}
}
}
private void handleProgramLoad() {
try {
int retVal = fileChooser.showOpenDialog(MainWindow.this);
if (retVal == JFileChooser.APPROVE_OPTION) {
File f = fileChooser.getSelectedFile();
if (f.canRead()) {
long fileSize = f.length();
if (fileSize > simulator.memorySize()) {
throw new IOException("Program will not fit in available memory.");
} else {
byte[] program = new byte[(int) fileSize];
int i = 0;
FileInputStream fis = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
while (dis.available() != 0) {
program[i++] = dis.readByte();
}
// Now load the program at the starting address.
simulator.loadProgram(program, PROGRAM_START_ADDRESS);
// Reset (but don't clear memory, naturally)
simulator.reset();
// Update status and clear the screen
console.reset();
uiUpdater.updateUi();
}
}
}
} catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to read file: " + ex.getMessage());
ex.printStackTrace();
} catch (MemoryAccessException ex) {
logger.log(Level.SEVERE, "Memory access error loading program");
ex.printStackTrace();
}
}
private void handleReset() {
if (simulator.isRunning()) {
stop();
}
try {
logger.log(Level.INFO, "Reset requested. Resetting CPU and clearing memory.");
// Reset and clear memory
simulator.reset();
simulator.clearMemory();
// Clear the console.
console.reset();
// Update status.
uiUpdater.updateUi();
} catch (MemoryAccessException ex) {
logger.log(Level.SEVERE, "Exception during simulator reset: " + ex.getMessage());
ex.printStackTrace();
}
}
private void handleStep() {
try {
simulator.step();
// The simulator is lazy about updating the UI for
// performance reasons, so always request an immediate update after stepping manually.
uiUpdater.updateUi();
} catch (SymonException ex) {
logger.log(Level.SEVERE, "Exception during simulator step: " + ex.getMessage());
ex.printStackTrace();
}
}
private void handleQuit() {
// TODO: Clean up and exit properly
System.exit(0);
}
private void stop() {
// Allow step while the simulator is stopped
simulator.requestStop();
if (simulatorThread != null) {
simulatorThread.interrupt();
simulatorThread = null;
}
}
private void start() {
simulatorThread = new Thread(simulator);
simulatorThread.start();
}
public Console getConsole() {
return this.console;
}
public StatusPane getStatusPane() {
return statusPane;
}
public JButton getRunButton() {
return runButton;
}
public JButton getLoadButton() {
return loadButton;
}
public JButton getStepButton() {
return stepButton;
}
public static void main(String args[]) {
try {
// Create the simulated system
Simulator simulator = new Simulator();
// Create the main UI window
MainWindow app = new MainWindow(simulator);
// Pack and display main window
app.pack();
app.setVisible(true);
// Reset the simulator.
simulator.reset();
} catch (MemoryAccessException e) {
e.printStackTrace();
} catch (MemoryRangeException e) {
e.printStackTrace();
}
}
}

View File

@ -1,490 +1,157 @@
package com.loomcom.symon; package com.loomcom.symon;
import java.io.*; import java.util.logging.*;
import java.util.StringTokenizer;
import com.loomcom.symon.devices.*; import com.loomcom.symon.devices.*;
import com.loomcom.symon.exceptions.*; import com.loomcom.symon.exceptions.*;
import com.loomcom.symon.ui.UiUpdater;
import javax.swing.*;
/** /**
* Main control class for the J6502 Simulator. * 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 { public class Simulator implements Runnable {
/** private final static Logger logger = Logger.getLogger(Simulator.class.getName());
* The CPU itself.
*/
private Cpu cpu;
/** private Bus bus;
* The Bus responsible for routing memory read/write requests to the private Cpu cpu;
* correct IO devices. private Acia acia;
*/ private Memory ram;
private Bus bus; private Memory rom;
private boolean isRunning = false;
/** private int updatesRequested = 0;
* The ACIA, used for charater in/out.
*
* By default, the simulator uses base address c000 for the ACIA.
*/
private Acia acia;
private BufferedReader in; private UiUpdater uiUpdater;
private BufferedWriter out;
/* If true, trace execution of the CPU */ private static final int BUS_BOTTOM = 0x0000;
private boolean trace = false; private static final int BUS_TOP = 0xffff;
private int nextExamineAddress = 0;
private static final int BUS_BOTTOM = 0x0000; private static final int MEMORY_BASE = 0x0000;
private static final int BUS_TOP = 0xffff; private static final int MEMORY_SIZE = 0xc000; // 48 KB
private static final int ACIA_BASE = 0xc000; private static final int ROM_BASE = 0xe000;
private static final int ROM_SIZE = 0x2000; // 8 KB
private static final int MEMORY_BASE = 0x0000; public static final int ACIA_BASE = 0xc000;
private static final int MEMORY_SIZE = 0xc000; // 48 KB
private static final int ROM_BASE = 0xe000; private static final int MAX_REQUESTS_BETWEEN_UPDATES = 25000;
private static final int ROM_SIZE = 0x2000; // 8 KB
public Simulator() throws MemoryRangeException { public Simulator() throws MemoryRangeException {
this.bus = new Bus(BUS_BOTTOM, BUS_TOP); this.acia = new Acia(ACIA_BASE);
this.cpu = new Cpu(); this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.acia = new Acia(ACIA_BASE); 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);
}
bus.addCpu(cpu); public void loadProgram(byte[] program, int startAddress) throws MemoryAccessException {
bus.addDevice(new Memory(MEMORY_BASE, MEMORY_SIZE, false)); cpu.setResetVector(startAddress);
bus.addDevice(acia);
// TODO: This should be read-only memory. Add a method
// to allow one-time initialization of ROM with a loaded
// ROM binary file.
bus.addDevice(new Memory(ROM_BASE, ROM_SIZE, false));
this.in = new BufferedReader(new InputStreamReader(System.in)); int addr = startAddress, i;
this.out = new BufferedWriter(new OutputStreamWriter(System.out)); 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();
}
});
public void run() throws MemoryAccessException, FifoUnderrunException {
try {
greeting();
prompt();
String command = null;
while (!shouldQuit(command = readLine())) {
try { try {
dispatch(command); while (isRunning && !cpu.getBreakFlag()) {
} catch (CommandFormatException ex) { step();
writeLine(ex.getMessage());
}
prompt();
}
writeLine("\nGoodbye!");
} catch (IOException ex) {
System.err.println("Error: " + ex.toString());
System.exit(1);
}
}
/**
* Dispatch the command.
*/
public void dispatch(String commandLine) throws MemoryAccessException,
IOException,
CommandFormatException,
FifoUnderrunException {
Command c = new Command(commandLine);
String cmd = c.getCommand();
if (cmd != null) {
if (cmd.startsWith("ste")) {
doStep(c);
} else if (cmd.startsWith("sta")) {
doGetState();
} else if (cmd.startsWith("se")) {
doSet(c);
} else if (cmd.startsWith("r")) {
doReset();
} else if (cmd.startsWith("e")) {
doExamine(c);
} else if (cmd.startsWith("d")) {
doDeposit(c);
} else if (cmd.startsWith("g")) {
doGo(c);
} else if (cmd.startsWith("h")) {
doHelp(c);
} else if (cmd.startsWith("t")) {
doToggleTrace();
} else if (cmd.startsWith("f")) {
doFill(c);
} else if (cmd.startsWith("l")) {
doLoad(c);
} else {
writeLine("? Type h for help");
}
}
}
public void doHelp(Command c) throws IOException {
writeLine("Symon 6502 Simulator");
writeLine("");
writeLine("All addresses must be in hexadecimal.");
writeLine("Commands may be short or long (e.g. 'e' or 'ex' or 'examine').");
writeLine("Note that 'go' clears the BREAK processor status flag.");
writeLine("");
writeLine("h Show this help file.");
writeLine("e [start] [end] Examine memory at PC, start, or start-end.");
writeLine("d <address> <data> Deposit data into address.");
writeLine("f <start> <end> <data> Fill memory with data.");
writeLine("set {pc,a,x,y} [data] Set register to data value.");
writeLine("load <file> <address> Load binary file at address.");
writeLine("g [address] [steps] Start running at address, or at PC.");
writeLine("step [address] Step once, optionally starting at address.");
writeLine("stat Show CPU state.");
writeLine("reset Reset simulator.");
writeLine("trace Toggle trace.");
writeLine("q (or Control-D) Quit.\n");
}
public void doGetState() throws IOException, MemoryAccessException {
writeLine(cpu.toString());
writeLine("Trace is " + (trace ? "on" : "off"));
}
public void doLoad(Command c) throws IOException,
MemoryAccessException,
CommandFormatException {
if (c.numArgs() != 2) {
throw new CommandFormatException("load <file> <address>");
}
File binFile = new File(c.getArg(0));
int address = stringToWord(c.getArg(1));
if (!binFile.exists()) {
throw new CommandFormatException("File '" + binFile +
"' does not exist.");
}
writeLine("Loading file '" + binFile + "' at address " +
String.format("%04x", address) + "...");
int bytesLoaded = 0;
FileInputStream fis = new FileInputStream(binFile);
try {
int b = 0;
while ((b = fis.read()) > -1 && address <= bus.endAddress()) {
bus.write(address++, b);
bytesLoaded++;
}
} finally {
fis.close();
}
writeLine("Loaded " + bytesLoaded + " (" +
String.format("$%04x", bytesLoaded) + ") bytes");
}
public void doSet(Command c) throws MemoryAccessException,
CommandFormatException {
if (c.numArgs() != 2) {
throw new CommandFormatException("set {a, x, y, pc} <value>");
}
try {
String reg = c.getArg(0).toLowerCase();
String data = c.getArg(1);
if ("a".equals(reg)) {
cpu.setAccumulator(stringToByte(data));
} else if ("x".equals(reg)) {
cpu.setXRegister(stringToByte(data));
} else if ("y".equals(reg)) {
cpu.setYRegister(stringToByte(data));
} else if ("pc".equals(reg)) {
cpu.setProgramCounter(stringToWord(data));
} else {
throw new CommandFormatException("set {a, x, y, pc} <value>");
}
} catch (NumberFormatException ex) {
throw new CommandFormatException("Illegal address");
}
}
public void doExamine(Command c) throws IOException,
MemoryAccessException,
CommandFormatException {
try {
if (c.numArgs() == 2) {
int startAddress = stringToWord(c.getArgs()[0]);
int endAddress = stringToWord(c.getArgs()[1]);
while (startAddress < endAddress) {
StringBuffer line = new StringBuffer();
int numBytes = 0;
line.append(String.format("%04x ", startAddress));
while (numBytes++ < 16 && startAddress <= endAddress) {
line.append(String.format("%02x ", bus.read(startAddress++)));
if (numBytes % 8 == 0) {
line.append(" ");
} }
} } catch (SymonException ex) {
writeLine(line.toString()); logger.log(Level.SEVERE, "Exception in main simulator run thread. Exiting run.");
ex.printStackTrace();
} }
nextExamineAddress = endAddress + 1;
} else if (c.numArgs() == 1) { logger.log(Level.INFO, "Exiting 'run'. BREAK=" + cpu.getBreakBit() + "; RUN_FLAG=" + isRunning);
int address = stringToWord(c.getArgs()[0]); isRunning = false;
writeLine(String.format("%04x %02x", address, bus.read(address)));
nextExamineAddress = address + 1; SwingUtilities.invokeLater(new Runnable() {
} else if (c.numArgs() == 0) { public void run() {
writeLine(String.format("%04x %02x", nextExamineAddress, uiUpdater.simulatorDidStop();
bus.read(nextExamineAddress))); }
nextExamineAddress++; });
} else {
throw new CommandFormatException("e [start [end]]");
}
} catch (NumberFormatException ex) {
throw new CommandFormatException("Illegal Address");
} }
}
public void doDeposit(Command c) throws MemoryAccessException,
CommandFormatException {
if (c.numArgs() != 2) {
throw new CommandFormatException("d [address] [data]");
}
try {
int address = stringToWord(c.getArg(0));
int data = stringToByte(c.getArg(1));
bus.write(address, data);
} catch (NumberFormatException ex) {
throw new CommandFormatException("Illegal Address");
}
}
public void doFill(Command c) throws MemoryAccessException, public void step() throws MemoryAccessException, FifoUnderrunException {
CommandFormatException {
if (c.numArgs() != 3) {
throw new CommandFormatException("f [start] [end] [data]");
}
try {
int start = stringToWord(c.getArg(0));
int end = stringToWord(c.getArg(1));
int data = stringToByte(c.getArg(2));
while (start <= end) {
bus.write(start, data);
start++;
}
} catch (NumberFormatException ex) {
throw new CommandFormatException("Illegal Address");
}
}
public void doStep(Command c) throws IOException,
MemoryAccessException,
FifoUnderrunException,
CommandFormatException {
try {
if (c.numArgs() > 0) {
cpu.setProgramCounter(stringToWord(c.getArg(1)));
}
cpu.step();
writeLine(cpu.toString()); // Always show status after stepping
} catch (NumberFormatException ex) {
throw new CommandFormatException("Illegal Address");
}
}
public void doGo(Command c) throws IOException,
MemoryAccessException,
FifoUnderrunException,
CommandFormatException {
int readChar;
int stepCount = 0;
if (c.numArgs() > 2) {
throw new CommandFormatException("g [address] [steps]");
}
try {
int start = 0;
int steps = -1;
if (c.numArgs() > 0) {
start = stringToWord(c.getArg(0));
} else {
start = cpu.getProgramCounter();
}
if (c.numArgs() == 2) {
steps = stringToWord(c.getArg(1));
}
// Make a gross assumption: Restarting the CPU clears
// the break flag and the IRQ disable flag.
cpu.clearBreakFlag();
cpu.clearIrqDisableFlag();
cpu.setProgramCounter(start);
outer:
while (!cpu.getBreakFlag() && (steps == -1 || steps-- > 0)) {
cpu.step(); cpu.step();
if (trace) {
writeLine(cpu.toString()); // 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());
} }
// Wake up and scan keyboard every 500 steps
if (stepCount++ >= 500) {
// Reset step count
stepCount = 0;
//
// Do output if available.
//
while (acia.hasTxChar()) {
out.write(acia.txRead());
out.flush();
}
//
// Consume input if available.
//
// NOTE: On UNIX systems, System.in.available() returns 0
// until Enter is pressed. So to interrupt we must ALWAYS
// type "^E<enter>". Sucks hard. But such is life.
if (System.in.available() > 0) {
while ((readChar = in.read()) > -1) {
// Keep consuming unless ^E is found.
//
// TODO: This will probably lead to a lot of spurious keyboard
// entry. Gotta keep an eye on that.
//
if (readChar == 0x05) {
break outer;
} else {
// Buffer keyboard input into the simulated ACIA's
// read buffer.
acia.rxWrite(readChar);
}
}
}
// 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;
} }
}
if (!trace) {
writeLine(cpu.toString());
}
} catch (NumberFormatException ex) {
throw new CommandFormatException("Illegal Address");
} }
}
public void doToggleTrace() throws IOException {
this.trace = !trace;
writeLine("Trace is now " + (trace ? "on" : "off"));
}
public void doReset() throws MemoryAccessException {
cpu.reset();
this.trace = false;
}
/**
* Main simulator routine.
*/
public static void main(String[] args) throws MemoryAccessException,
FifoUnderrunException {
try {
new Simulator().run();
} catch (MemoryRangeException ex) {
System.err.println("Error: " + ex.toString());
}
}
/*******************************************************************
* Private
*******************************************************************/
public void load(int address, int[] data)
throws MemoryAccessException {
int i = 0;
for (int d : data) {
bus.write(address + i++, d);
}
}
private int stringToWord(String addrString) {
return Integer.parseInt(addrString, 16) & 0xffff;
}
private int stringToByte(String dataString) {
return Integer.parseInt(dataString, 16) & 0xff;
}
private void greeting() throws IOException {
writeLine("Welcome to the Symon 6502 Simulator. Type 'h' for help.");
}
private void prompt() throws IOException {
out.write("symon> ");
out.flush();
}
private String readLine() throws IOException {
String line = in.readLine();
if (line == null) { return null; }
return line.trim();
}
private void writeLine(String line) throws IOException {
out.write(line);
out.newLine();
out.flush();
}
/**
* Returns true if the line is a quit.
*/
private boolean shouldQuit(String line) {
return (line == null || "q".equals(line.toLowerCase()));
}
/**
* Command line tokenizer class. Given a command line, tokenize
* it and give easy access to the command and its arguments.
*/
public static final class Command {
private String command;
private String[] args;
public Command(String commandLine) {
StringTokenizer st = new StringTokenizer(commandLine);
int numTokens = st.countTokens();
int idx = 0;
args = new String[numTokens > 1 ? numTokens - 1 : 0];
while (st.hasMoreTokens()) {
if (command == null) {
command = st.nextToken();
} else {
args[idx++] = st.nextToken();
}
}
}
public String getCommand() {
return command;
}
public String[] getArgs() {
return args;
}
public String getArg(int argNum) {
if (argNum > args.length - 1) {
return null;
} else {
return args[argNum];
}
}
public int numArgs() {
return args.length;
}
public boolean hasArgs() {
return args.length > 0;
}
}
} }

View File

@ -6,38 +6,43 @@ import com.loomcom.symon.exceptions.*;
public class Memory extends Device { public class Memory extends Device {
private boolean readOnly; private boolean readOnly;
private int[] mem; private int[] mem;
/* Initialize all locations to 0x00 (BRK) */ /* Initialize all locations to 0x00 (BRK) */
private static final int DEFAULT_FILL = 0x00; private static final int DEFAULT_FILL = 0x00;
public Memory(int address, int size, boolean readOnly) public Memory(int address, int size, boolean readOnly)
throws MemoryRangeException { throws MemoryRangeException {
super(address, size, (readOnly ? "RO Memory" : "RW Memory")); super(address, size, (readOnly ? "RO Memory" : "RW Memory"));
this.readOnly = readOnly; this.readOnly = readOnly;
this.mem = new int[size]; this.mem = new int[size];
Arrays.fill(this.mem, DEFAULT_FILL); this.fill(DEFAULT_FILL);
}
public Memory(int address, int size) throws MemoryRangeException {
this(address, size, false);
}
public void write(int address, int data) throws MemoryAccessException {
if (readOnly) {
throw new MemoryAccessException("Cannot write to read-only memory at address " + address);
} else {
this.mem[address] = data;
} }
}
public int read(int address) throws MemoryAccessException { public Memory(int address, int size) throws MemoryRangeException {
return this.mem[address]; this(address, size, false);
} }
public String toString() { public void write(int address, int data) throws MemoryAccessException {
return "Memory: " + getMemoryRange().toString(); if (readOnly) {
} throw new MemoryAccessException("Cannot write to read-only memory at address " + address);
} else {
this.mem[address] = data;
}
}
public int read(int address) throws MemoryAccessException {
return this.mem[address];
}
public void fill(int val) {
Arrays.fill(this.mem, val);
}
public String toString() {
return "Memory: " + getMemoryRange().toString();
}
} }

View File

@ -1,6 +1,6 @@
package com.loomcom.symon.exceptions; package com.loomcom.symon.exceptions;
public class CommandFormatException extends Exception { public class CommandFormatException extends SymonException {
public CommandFormatException(String msg) { public CommandFormatException(String msg) {
super(msg); super(msg);
} }

View File

@ -1,6 +1,6 @@
package com.loomcom.symon.exceptions; package com.loomcom.symon.exceptions;
public class FifoUnderrunException extends Exception { public class FifoUnderrunException extends SymonException {
public FifoUnderrunException(String msg) { public FifoUnderrunException(String msg) {
super(msg); super(msg);
} }

View File

@ -4,7 +4,7 @@ package com.loomcom.symon.exceptions;
* Exception that will be thrown if access to memory or IO cannot be * Exception that will be thrown if access to memory or IO cannot be
* accessed. * accessed.
*/ */
public class MemoryAccessException extends Exception { public class MemoryAccessException extends SymonException {
public MemoryAccessException(String msg) { public MemoryAccessException(String msg) {
super(msg); super(msg);
} }

View File

@ -3,7 +3,7 @@ package com.loomcom.symon.exceptions;
/** /**
* Exception that will be thrown if devices conflict in the IO map. * Exception that will be thrown if devices conflict in the IO map.
*/ */
public class MemoryRangeException extends Exception { public class MemoryRangeException extends SymonException {
public MemoryRangeException(String msg) { public MemoryRangeException(String msg) {
super(msg); super(msg);
} }

View File

@ -0,0 +1,14 @@
package com.loomcom.symon.exceptions;
/**
* Superclass for all symon Exceptions.
*/
public class SymonException extends Exception {
public SymonException(String msg) {
super(msg);
}
public SymonException() {
super();
}
}

View File

@ -0,0 +1,69 @@
package com.loomcom.symon.ui;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import com.grahamedgecombe.jterminal.JTerminal;
/**
* The Console is a simulated 80 column x 24 row VT-100 terminal attached to
* the ACIA of the system. It provides basic keyboard I/O to Symon.
*/
public class Console extends JTerminal implements KeyListener, MouseListener {
// TODO: Pass in io threads, read and write to ACIA.
public Console() {
super();
addKeyListener(this);
addMouseListener(this);
}
public void reset() {
getModel().clear();
getModel().setCursorColumn(0);
getModel().setCursorRow(0);
repaint();
}
public void keyTyped(KeyEvent keyEvent) {
keyEvent.consume();
}
public void keyPressed(KeyEvent keyEvent) {
int keyCode = keyEvent.getKeyCode();
int modifiersMask = keyEvent.getModifiers();
int modifiersExMask = keyEvent.getModifiersEx();
System.out.println("Key Pressed #" + keyEvent.getKeyCode() + " : " +
KeyEvent.getKeyText(keyCode) + " MASK : " +
modifiersMask + " EXT MASK : " +
modifiersExMask
);
keyEvent.consume();
}
public void keyReleased(KeyEvent keyEvent) {
keyEvent.consume();
}
public void mouseClicked(MouseEvent mouseEvent) {
}
public void mousePressed(MouseEvent mouseEvent) {
requestFocus();
mouseEvent.consume();
}
public void mouseReleased(MouseEvent mouseEvent) {
}
public void mouseEntered(MouseEvent mouseEvent) {
}
public void mouseExited(MouseEvent mouseEvent) {
}
}

View File

@ -0,0 +1,207 @@
/**
*
*/
package com.loomcom.symon.ui;
import com.loomcom.symon.Cpu;
import javax.swing.*;
import java.awt.*;
public class StatusPane extends JPanel {
// The CPU to ask for state information.
private final Cpu cpu;
private final ImageIcon carryOn;
private final ImageIcon carryOff;
private final ImageIcon zeroOn;
private final ImageIcon zeroOff;
private final ImageIcon irqOn;
private final ImageIcon irqOff;
private final ImageIcon decimalOn;
private final ImageIcon decimalOff;
private final ImageIcon breakOn;
private final ImageIcon breakOff;
private final ImageIcon overflowOn;
private final ImageIcon overflowOff;
private final ImageIcon negativeOn;
private final ImageIcon negativeOff;
private final JLabel carryFlagLabel;
private final JLabel zeroFlagLabel;
private final JLabel irqDisableFlagLabel;
private final JLabel decimalModeFlagLabel;
private final JLabel breakFlagLabel;
private final JLabel overflowFlagLabel;
private final JLabel negativeFlagLabel;
private final JLabel opcodeLabel;
private final JLabel pcLabel;
private final JLabel aLabel;
private final JLabel xLabel;
private final JLabel yLabel;
private final JLabel stepCountLabel;
private static final int WIDTH = 134;
private static final int HEIGHT = 27;
public StatusPane(Cpu cpu) {
super();
this.cpu = cpu;
Dimension dimensions = new Dimension(WIDTH, HEIGHT);
setMinimumSize(dimensions);
setPreferredSize(dimensions);
setMaximumSize(dimensions);
carryOn = new ImageIcon(this.getClass().getResource("images/C_on.png"));
carryOff = new ImageIcon(this.getClass().getResource("images/C_off.png"));
zeroOn = new ImageIcon(this.getClass().getResource("images/Z_on.png"));
zeroOff = new ImageIcon(this.getClass().getResource("images/Z_off.png"));
irqOn = new ImageIcon(this.getClass().getResource("images/I_on.png"));
irqOff = new ImageIcon(this.getClass().getResource("images/I_off.png"));
decimalOn = new ImageIcon(this.getClass().getResource("images/D_on.png"));
decimalOff = new ImageIcon(this.getClass().getResource("images/D_off.png"));
breakOn = new ImageIcon(this.getClass().getResource("images/B_on.png"));
breakOff = new ImageIcon(this.getClass().getResource("images/B_off.png"));
overflowOn = new ImageIcon(this.getClass().getResource("images/O_on.png"));
overflowOff = new ImageIcon(this.getClass().getResource("images/O_off.png"));
negativeOn = new ImageIcon(this.getClass().getResource("images/N_on.png"));
negativeOff = new ImageIcon(this.getClass().getResource("images/N_off.png"));
// Initialize all to off
carryFlagLabel = new JLabel(carryOff, JLabel.CENTER);
zeroFlagLabel = new JLabel(zeroOff, JLabel.CENTER);
irqDisableFlagLabel = new JLabel(irqOff, JLabel.CENTER);
decimalModeFlagLabel = new JLabel(decimalOff, JLabel.CENTER);
breakFlagLabel = new JLabel(breakOff, JLabel.CENTER);
overflowFlagLabel = new JLabel(overflowOff, JLabel.CENTER);
negativeFlagLabel = new JLabel(negativeOff, JLabel.CENTER);
// Create and add register and address labels
this.opcodeLabel = new JLabel();
this.pcLabel = new JLabel();
this.aLabel = new JLabel();
this.xLabel = new JLabel();
this.yLabel = new JLabel();
this.stepCountLabel = new JLabel();
this.opcodeLabel.setMinimumSize(new Dimension(100, 20));
this.pcLabel.setMinimumSize(new Dimension(80, 20));
this.aLabel.setMinimumSize(new Dimension(60, 20));
this.xLabel.setMinimumSize(new Dimension(60, 20));
this.yLabel.setMinimumSize(new Dimension(60, 20));
this.stepCountLabel.setMinimumSize(new Dimension(120, 20));
this.opcodeLabel.setPreferredSize(new Dimension(100, 20));
this.pcLabel.setPreferredSize(new Dimension(80, 20));
this.aLabel.setPreferredSize(new Dimension(60, 20));
this.xLabel.setPreferredSize(new Dimension(60, 20));
this.yLabel.setPreferredSize(new Dimension(60, 20));
this.stepCountLabel.setPreferredSize(new Dimension(120, 20));
this.setLayout(new FlowLayout());
this.add(negativeFlagLabel);
this.add(overflowFlagLabel);
this.add(breakFlagLabel);
this.add(decimalModeFlagLabel);
this.add(irqDisableFlagLabel);
this.add(zeroFlagLabel);
this.add(carryFlagLabel);
this.add(opcodeLabel);
this.add(pcLabel);
this.add(aLabel);
this.add(xLabel);
this.add(yLabel);
this.add(stepCountLabel);
updateState();
}
public void updateState() {
// Update the Processor Status Flag display
int state = this.cpu.getProcessorStatus();
carryFlagLabel.setIcon(iconForFlag(state, 0));
zeroFlagLabel.setIcon(iconForFlag(state, 1));
irqDisableFlagLabel.setIcon(iconForFlag(state, 2));
decimalModeFlagLabel.setIcon(iconForFlag(state, 3));
breakFlagLabel.setIcon(iconForFlag(state, 4));
overflowFlagLabel.setIcon(iconForFlag(state, 6));
negativeFlagLabel.setIcon(iconForFlag(state, 7));
// Update the register and address displays
opcodeLabel.setText(cpu.getOpcodeStatus());
pcLabel.setText(cpu.getProgramCounterStatus());
aLabel.setText(cpu.getARegisterStatus());
xLabel.setText(cpu.getXRegisterStatus());
yLabel.setText(cpu.getYRegisterStatus());
stepCountLabel.setText(Long.toString(cpu.getStepCounter()));
repaint();
}
private ImageIcon iconForFlag(int state, int flagIndex) {
ImageIcon imageIcon = null;
if ((((state & 0xff) >> flagIndex) & 0x01) == 1) {
switch (flagIndex) {
case 0:
imageIcon = carryOn;
break;
case 1:
imageIcon = zeroOn;
break;
case 2:
imageIcon = irqOn;
break;
case 3:
imageIcon = decimalOn;
break;
case 4:
imageIcon = breakOn;
break;
case 6:
imageIcon = overflowOn;
break;
case 7:
imageIcon = negativeOn;
break;
}
} else {
switch (flagIndex) {
case 0:
imageIcon = carryOff;
break;
case 1:
imageIcon = zeroOff;
break;
case 2:
imageIcon = irqOff;
break;
case 3:
imageIcon = decimalOff;
break;
case 4:
imageIcon = breakOff;
break;
case 6:
imageIcon = overflowOff;
break;
case 7:
imageIcon = negativeOff;
break;
}
}
return imageIcon;
}
}

View File

@ -0,0 +1,63 @@
package com.loomcom.symon.ui;
import com.loomcom.symon.MainWindow;
/**
* Update the console and status display. All direct manipulation of the
* main UI should go through this class. When not called from the Swing
* event dispatch thread, these methods should be called using
* SwingUtilities.invokeLater.
*/
public class UiUpdater {
private final Console console;
private final MainWindow mainWindow;
private final StatusPane statusPane;
private final StringBuffer data;
public UiUpdater(MainWindow mainWindow) {
this.mainWindow = mainWindow;
this.console = mainWindow.getConsole();
this.statusPane = mainWindow.getStatusPane();
this.data = new StringBuffer();
}
public void consoleWrite(int i) {
data.append((char) i);
}
/**
* Callback called by the simulator before exiting its run method.
*/
public void simulatorDidStop() {
mainWindow.getStepButton().setEnabled(true);
mainWindow.getLoadButton().setEnabled(true);
mainWindow.getRunButton().setText("Run");
updateUi();
}
/**
* Callback called by the simulator when entering its run method.
*/
public void simulatorDidStart() {
// Don't allow step while the simulator is running
mainWindow.getStepButton().setEnabled(false);
mainWindow.getLoadButton().setEnabled(false);
// Toggle the state of the run button
mainWindow.getRunButton().setText("Stop");
updateUi();
}
public void updateUi() {
// Update the console with any text
if (data.length() > 0) {
console.getModel().print(data.toString());
console.repaint();
// Clear the buffer
data.delete(0, data.length());
}
// Update the status UI.
statusPane.updateState();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

View File

@ -1,64 +0,0 @@
package com.loomcom.symon;
import org.junit.*;
import static org.junit.Assert.*;
public class CommandTest {
@Test
public void testCommandShouldParseCorrectNumberOfArguments() {
Simulator.Command c;
c = new Simulator.Command("foo");
assertEquals("foo", c.getCommand());
assertEquals(0, c.numArgs());
c = new Simulator.Command("foo bar");
assertEquals("foo", c.getCommand());
assertEquals(1, c.numArgs());
assertEquals("bar", c.getArgs()[0]);
c = new Simulator.Command("foo bar baz quux 0 100");
assertEquals("foo", c.getCommand());
assertEquals(5, c.numArgs());
assertEquals("bar", c.getArgs()[0]);
assertEquals("baz", c.getArgs()[1]);
assertEquals("quux", c.getArgs()[2]);
assertEquals("0", c.getArgs()[3]);
assertEquals("100", c.getArgs()[4]);
}
@Test
public void testCommandShouldIgnoreWhitespaceBetweenTokens() {
Simulator.Command c;
c = new Simulator.Command("foo bar baz");
assertEquals("foo", c.getCommand());
assertEquals(2, c.numArgs());
assertEquals("bar", c.getArgs()[0]);
assertEquals("baz", c.getArgs()[1]);
}
@Test
public void testCommandShouldIgnoreWhitespaceBeforeCommand() {
Simulator.Command c;
c = new Simulator.Command(" foo bar baz");
assertEquals("foo", c.getCommand());
assertEquals(2, c.numArgs());
assertEquals("bar", c.getArgs()[0]);
assertEquals("baz", c.getArgs()[1]);
}
@Test
public void testCommandShouldIgnoreWhitespaceAfterCommand() {
Simulator.Command c;
c = new Simulator.Command("foo bar baz ");
assertEquals("foo", c.getCommand());
assertEquals(2, c.numArgs());
assertEquals("bar", c.getArgs()[0]);
assertEquals("baz", c.getArgs()[1]);
}
}

View File

@ -10,387 +10,409 @@ import com.loomcom.symon.exceptions.*;
*/ */
public class CpuTest extends TestCase { public class CpuTest extends TestCase {
private Cpu cpu; private Cpu cpu;
private Bus bus; private Bus bus;
private Memory mem; private Memory mem;
public CpuTest(String testName) { public CpuTest(String testName) {
super(testName); super(testName);
}
public static Test suite() {
return new TestSuite(CpuTest.class);
}
public void setUp() throws MemoryRangeException, MemoryAccessException {
this.cpu = new Cpu();
this.bus = new Bus(0x0000, 0xffff);
this.mem = new Memory(0x0000, 0x10000);
bus.addCpu(cpu);
bus.addDevice(mem);
// All test programs start at 0x0200;
bus.write(0xfffc, 0x00);
bus.write(0xfffd, 0x02);
cpu.reset();
}
public void testReset() {
assertEquals(0, cpu.getAccumulator());
assertEquals(0, cpu.getXRegister());
assertEquals(0, cpu.getYRegister());
assertEquals(0x0200, cpu.getProgramCounter());
assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag());
}
public void testStack() throws MemoryAccessException {
cpu.stackPush(0x13);
assertEquals(0x13, cpu.stackPop());
cpu.stackPush(0x12);
assertEquals(0x12, cpu.stackPop());
for (int i = 0x00; i <= 0xff; i++) {
cpu.stackPush(i);
} }
for (int i = 0xff; i >= 0x00; i--) { public static Test suite() {
assertEquals(i, cpu.stackPop()); return new TestSuite(CpuTest.class);
} }
} public void setUp() throws MemoryRangeException, MemoryAccessException {
this.cpu = new Cpu();
this.bus = new Bus(0x0000, 0xffff);
this.mem = new Memory(0x0000, 0x10000);
bus.addCpu(cpu);
bus.addDevice(mem);
public void testStackPush() throws MemoryAccessException { // All test programs start at 0x0200;
assertEquals(0xff, cpu.getStackPointer()); bus.write(0xfffc, 0x00);
assertEquals(0x00, bus.read(0x1ff)); bus.write(0xfffd, 0x02);
cpu.stackPush(0x06); cpu.reset();
assertEquals(0xfe, cpu.getStackPointer()); }
assertEquals(0x06, bus.read(0x1ff));
cpu.stackPush(0x05); public void testReset() {
assertEquals(0xfd, cpu.getStackPointer()); assertEquals(0, cpu.getAccumulator());
assertEquals(0x06, bus.read(0x1ff)); assertEquals(0, cpu.getXRegister());
assertEquals(0x05, bus.read(0x1fe)); assertEquals(0, cpu.getYRegister());
assertEquals(0x0200, cpu.getProgramCounter());
assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag());
}
cpu.stackPush(0x04); public void testStack() throws MemoryAccessException {
assertEquals(0xfc, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
cpu.stackPush(0x03); cpu.stackPush(0x13);
assertEquals(0xfb, cpu.getStackPointer()); assertEquals(0x13, cpu.stackPop());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
cpu.stackPush(0x02); cpu.stackPush(0x12);
assertEquals(0xfa, cpu.getStackPointer()); assertEquals(0x12, cpu.stackPop());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
assertEquals(0x02, bus.read(0x1fb));
cpu.stackPush(0x01); for (int i = 0x00; i <= 0xff; i++) {
assertEquals(0xf9, cpu.getStackPointer()); cpu.stackPush(i);
assertEquals(0x06, bus.read(0x1ff)); }
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
assertEquals(0x02, bus.read(0x1fb));
assertEquals(0x01, bus.read(0x1fa));
}
public void testStackPushWrapsAroundToStackTop() throws MemoryAccessException { for (int i = 0xff; i >= 0x00; i--) {
cpu.setStackPointer(0x01); assertEquals(i, cpu.stackPop());
}
cpu.stackPush(0x01); }
assertEquals(0x01, bus.read(0x101));
assertEquals(0x00, cpu.getStackPointer());
cpu.stackPush(0x02); public void testStackPush() throws MemoryAccessException {
assertEquals(0x02, bus.read(0x100)); assertEquals(0xff, cpu.getStackPointer());
assertEquals(0xff, cpu.getStackPointer()); assertEquals(0x00, bus.read(0x1ff));
cpu.stackPush(0x03); cpu.stackPush(0x06);
assertEquals(0x03, bus.read(0x1ff)); assertEquals(0xfe, cpu.getStackPointer());
assertEquals(0xfe, cpu.getStackPointer()); assertEquals(0x06, bus.read(0x1ff));
}
cpu.stackPush(0x05);
assertEquals(0xfd, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
cpu.stackPush(0x04);
assertEquals(0xfc, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
cpu.stackPush(0x03);
assertEquals(0xfb, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
cpu.stackPush(0x02);
assertEquals(0xfa, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
assertEquals(0x02, bus.read(0x1fb));
cpu.stackPush(0x01);
assertEquals(0xf9, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
assertEquals(0x02, bus.read(0x1fb));
assertEquals(0x01, bus.read(0x1fa));
}
public void testStackPushWrapsAroundToStackTop() throws MemoryAccessException {
cpu.setStackPointer(0x01);
cpu.stackPush(0x01);
assertEquals(0x01, bus.read(0x101));
assertEquals(0x00, cpu.getStackPointer());
cpu.stackPush(0x02);
assertEquals(0x02, bus.read(0x100));
assertEquals(0xff, cpu.getStackPointer());
cpu.stackPush(0x03);
assertEquals(0x03, bus.read(0x1ff));
assertEquals(0xfe, cpu.getStackPointer());
}
public void testStackPop() throws MemoryAccessException { public void testStackPop() throws MemoryAccessException {
bus.write(0x1ff, 0x06); bus.write(0x1ff, 0x06);
bus.write(0x1fe, 0x05); bus.write(0x1fe, 0x05);
bus.write(0x1fd, 0x04); bus.write(0x1fd, 0x04);
bus.write(0x1fc, 0x03); bus.write(0x1fc, 0x03);
bus.write(0x1fb, 0x02); bus.write(0x1fb, 0x02);
bus.write(0x1fa, 0x01); bus.write(0x1fa, 0x01);
cpu.setStackPointer(0xf9); cpu.setStackPointer(0xf9);
assertEquals(0x01, cpu.stackPop()); assertEquals(0x01, cpu.stackPop());
assertEquals(0xfa, cpu.getStackPointer()); assertEquals(0xfa, cpu.getStackPointer());
assertEquals(0x02, cpu.stackPop()); assertEquals(0x02, cpu.stackPop());
assertEquals(0xfb, cpu.getStackPointer()); assertEquals(0xfb, cpu.getStackPointer());
assertEquals(0x03, cpu.stackPop()); assertEquals(0x03, cpu.stackPop());
assertEquals(0xfc, cpu.getStackPointer()); assertEquals(0xfc, cpu.getStackPointer());
assertEquals(0x04, cpu.stackPop()); assertEquals(0x04, cpu.stackPop());
assertEquals(0xfd, cpu.getStackPointer()); assertEquals(0xfd, cpu.getStackPointer());
assertEquals(0x05, cpu.stackPop()); assertEquals(0x05, cpu.stackPop());
assertEquals(0xfe, cpu.getStackPointer()); assertEquals(0xfe, cpu.getStackPointer());
assertEquals(0x06, cpu.stackPop()); assertEquals(0x06, cpu.stackPop());
assertEquals(0xff, cpu.getStackPointer()); assertEquals(0xff, cpu.getStackPointer());
} }
public void testStackPopWrapsAroundToStackBottom() throws MemoryAccessException { public void testStackPopWrapsAroundToStackBottom() throws MemoryAccessException {
bus.write(0x1ff, 0x0f); // top of stack bus.write(0x1ff, 0x0f); // top of stack
bus.write(0x100, 0xf0); // bottom of stack bus.write(0x100, 0xf0); // bottom of stack
bus.write(0x101, 0xf1); bus.write(0x101, 0xf1);
bus.write(0x102, 0xf2); bus.write(0x102, 0xf2);
cpu.setStackPointer(0xfe); cpu.setStackPointer(0xfe);
assertEquals(0x0f, cpu.stackPop()); assertEquals(0x0f, cpu.stackPop());
assertEquals(0xff, cpu.getStackPointer()); assertEquals(0xff, cpu.getStackPointer());
assertEquals(0xf0, cpu.stackPop()); assertEquals(0xf0, cpu.stackPop());
assertEquals(0x00, cpu.getStackPointer()); assertEquals(0x00, cpu.getStackPointer());
assertEquals(0xf1, cpu.stackPop()); assertEquals(0xf1, cpu.stackPop());
assertEquals(0x01, cpu.getStackPointer()); assertEquals(0x01, cpu.getStackPointer());
assertEquals(0xf2, cpu.stackPop()); assertEquals(0xf2, cpu.stackPop());
assertEquals(0x02, cpu.getStackPointer()); assertEquals(0x02, cpu.getStackPointer());
} }
public void testStackPeekDoesNotAlterStackPointer() throws MemoryAccessException { public void testStackPeekDoesNotAlterStackPointer() throws MemoryAccessException {
assertEquals(0x00, cpu.stackPeek()); assertEquals(0x00, cpu.stackPeek());
assertEquals(0xff, cpu.getStackPointer()); assertEquals(0xff, cpu.getStackPointer());
cpu.stackPush(0x01); cpu.stackPush(0x01);
assertEquals(0x01, cpu.stackPeek()); assertEquals(0x01, cpu.stackPeek());
assertEquals(0xfe, cpu.getStackPointer()); assertEquals(0xfe, cpu.getStackPointer());
cpu.stackPush(0x02); cpu.stackPush(0x02);
assertEquals(0x02, cpu.stackPeek()); assertEquals(0x02, cpu.stackPeek());
assertEquals(0xfd, cpu.getStackPointer()); assertEquals(0xfd, cpu.getStackPointer());
cpu.stackPush(0x03); cpu.stackPush(0x03);
assertEquals(0x03, cpu.stackPeek()); assertEquals(0x03, cpu.stackPeek());
assertEquals(0xfc, cpu.getStackPointer()); assertEquals(0xfc, cpu.getStackPointer());
cpu.stackPush(0x04); cpu.stackPush(0x04);
assertEquals(0x04, cpu.stackPeek()); assertEquals(0x04, cpu.stackPeek());
assertEquals(0xfb, cpu.getStackPointer()); assertEquals(0xfb, cpu.getStackPointer());
assertEquals(0x04, cpu.stackPeek()); assertEquals(0x04, cpu.stackPeek());
assertEquals(0xfb, cpu.getStackPointer()); assertEquals(0xfb, cpu.getStackPointer());
assertEquals(0x04, cpu.stackPeek()); assertEquals(0x04, cpu.stackPeek());
assertEquals(0xfb, cpu.getStackPointer()); assertEquals(0xfb, cpu.getStackPointer());
} }
public void testGetProcessorStatus() { public void testGetProcessorStatus() {
// By default, no flags are set. Remember, bit 5 // By default, no flags are set. Remember, bit 5
// is always '1'. // is always '1'.
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
cpu.setCarryFlag(); cpu.setCarryFlag();
assertEquals(0x21, cpu.getProcessorStatus()); assertEquals(0x21, cpu.getProcessorStatus());
cpu.setZeroFlag(); cpu.setZeroFlag();
assertEquals(0x23, cpu.getProcessorStatus()); assertEquals(0x23, cpu.getProcessorStatus());
cpu.setIrqDisableFlag(); cpu.setIrqDisableFlag();
assertEquals(0x27, cpu.getProcessorStatus()); assertEquals(0x27, cpu.getProcessorStatus());
cpu.setDecimalModeFlag(); cpu.setDecimalModeFlag();
assertEquals(0x2f, cpu.getProcessorStatus()); assertEquals(0x2f, cpu.getProcessorStatus());
cpu.setBreakFlag(); cpu.setBreakFlag();
assertEquals(0x3f, cpu.getProcessorStatus()); assertEquals(0x3f, cpu.getProcessorStatus());
cpu.setOverflowFlag(); cpu.setOverflowFlag();
assertEquals(0x7f, cpu.getProcessorStatus()); assertEquals(0x7f, cpu.getProcessorStatus());
cpu.setNegativeFlag(); cpu.setNegativeFlag();
assertEquals(0xff, cpu.getProcessorStatus()); assertEquals(0xff, cpu.getProcessorStatus());
cpu.clearCarryFlag(); cpu.clearCarryFlag();
assertEquals(0xfe, cpu.getProcessorStatus()); assertEquals(0xfe, cpu.getProcessorStatus());
cpu.clearZeroFlag(); cpu.clearZeroFlag();
assertEquals(0xfc, cpu.getProcessorStatus()); assertEquals(0xfc, cpu.getProcessorStatus());
cpu.clearIrqDisableFlag(); cpu.clearIrqDisableFlag();
assertEquals(0xf8, cpu.getProcessorStatus()); assertEquals(0xf8, cpu.getProcessorStatus());
cpu.clearDecimalModeFlag(); cpu.clearDecimalModeFlag();
assertEquals(0xf0, cpu.getProcessorStatus()); assertEquals(0xf0, cpu.getProcessorStatus());
cpu.clearBreakFlag(); cpu.clearBreakFlag();
assertEquals(0xe0, cpu.getProcessorStatus()); assertEquals(0xe0, cpu.getProcessorStatus());
cpu.clearOverflowFlag(); cpu.clearOverflowFlag();
assertEquals(0xa0, cpu.getProcessorStatus()); assertEquals(0xa0, cpu.getProcessorStatus());
cpu.clearNegativeFlag(); cpu.clearNegativeFlag();
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
public void testSetProcessorStatus() { public void testSetProcessorStatus() {
// Default // Default
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag()); assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag()); assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag()); assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY); cpu.setProcessorStatus(0x20 | Cpu.P_CARRY);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag()); assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag()); assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag()); assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE); cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag()); assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag()); assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag()); assertFalse(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO); cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag()); assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag()); assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag()); assertFalse(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO| cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
Cpu.P_OVERFLOW); Cpu.P_OVERFLOW);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag()); assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag()); assertFalse(cpu.getBreakFlag());
assertTrue(cpu.getOverflowFlag()); assertTrue(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO| cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
Cpu.P_OVERFLOW|Cpu.P_BREAK); Cpu.P_OVERFLOW | Cpu.P_BREAK);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag()); assertFalse(cpu.getDecimalModeFlag());
assertTrue(cpu.getBreakFlag()); assertTrue(cpu.getBreakFlag());
assertTrue(cpu.getOverflowFlag()); assertTrue(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO| cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
Cpu.P_OVERFLOW|Cpu.P_BREAK|Cpu.P_DECIMAL); Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_DECIMAL);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
assertTrue(cpu.getDecimalModeFlag()); assertTrue(cpu.getDecimalModeFlag());
assertTrue(cpu.getBreakFlag()); assertTrue(cpu.getBreakFlag());
assertTrue(cpu.getOverflowFlag()); assertTrue(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO| cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
Cpu.P_OVERFLOW|Cpu.P_BREAK|Cpu.P_DECIMAL| Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_DECIMAL |
Cpu.P_IRQ_DISABLE); Cpu.P_IRQ_DISABLE);
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag()); assertTrue(cpu.getZeroFlag());
assertTrue(cpu.getIrqDisableFlag()); assertTrue(cpu.getIrqDisableFlag());
assertTrue(cpu.getDecimalModeFlag()); assertTrue(cpu.getDecimalModeFlag());
assertTrue(cpu.getBreakFlag()); assertTrue(cpu.getBreakFlag());
assertTrue(cpu.getOverflowFlag()); assertTrue(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag()); assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20); cpu.setProcessorStatus(0x20);
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag()); assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag()); assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag()); assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x00); cpu.setProcessorStatus(0x00);
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag()); assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag()); assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag()); assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag()); assertFalse(cpu.getNegativeFlag());
} }
public void testAddress() { public void testAddress() {
assertEquals(0xf1ea, cpu.address(0xea, 0xf1)); assertEquals(0xf1ea, cpu.address(0xea, 0xf1));
assertEquals(0x00ea, cpu.address(0xea, 0x00)); assertEquals(0x00ea, cpu.address(0xea, 0x00));
assertEquals(0xf100, cpu.address(0x00, 0xf1)); assertEquals(0xf100, cpu.address(0x00, 0xf1));
assertEquals(0x1234, cpu.address(0x34, 0x12)); assertEquals(0x1234, cpu.address(0x34, 0x12));
assertEquals(0xffff, cpu.address(0xff, 0xff)); assertEquals(0xffff, cpu.address(0xff, 0xff));
} }
public void testZpxAddress() { public void testZpxAddress() {
cpu.setXRegister(0x00); cpu.setXRegister(0x00);
assertEquals(0x10, cpu.zpxAddress(0x10)); assertEquals(0x10, cpu.zpxAddress(0x10));
cpu.setXRegister(0x10); cpu.setXRegister(0x10);
assertEquals(0x20, cpu.zpxAddress(0x10)); assertEquals(0x20, cpu.zpxAddress(0x10));
cpu.setXRegister(0x25); cpu.setXRegister(0x25);
assertEquals(0x35, cpu.zpxAddress(0x10)); assertEquals(0x35, cpu.zpxAddress(0x10));
cpu.setXRegister(0xf5); cpu.setXRegister(0xf5);
assertEquals(0x05, cpu.zpxAddress(0x10)); assertEquals(0x05, cpu.zpxAddress(0x10));
cpu.setXRegister(0x00); cpu.setXRegister(0x00);
assertEquals(0x80, cpu.zpxAddress(0x80)); assertEquals(0x80, cpu.zpxAddress(0x80));
cpu.setXRegister(0x10); cpu.setXRegister(0x10);
assertEquals(0x90, cpu.zpxAddress(0x80)); assertEquals(0x90, cpu.zpxAddress(0x80));
cpu.setXRegister(0x25); cpu.setXRegister(0x25);
assertEquals(0xa5, cpu.zpxAddress(0x80)); assertEquals(0xa5, cpu.zpxAddress(0x80));
cpu.setXRegister(0x95); cpu.setXRegister(0x95);
assertEquals(0x15, cpu.zpxAddress(0x80)); assertEquals(0x15, cpu.zpxAddress(0x80));
} }
public void testZpyAddress() { public void testZpyAddress() {
cpu.setYRegister(0x00); cpu.setYRegister(0x00);
assertEquals(0x10, cpu.zpyAddress(0x10)); assertEquals(0x10, cpu.zpyAddress(0x10));
cpu.setYRegister(0x10); cpu.setYRegister(0x10);
assertEquals(0x20, cpu.zpyAddress(0x10)); assertEquals(0x20, cpu.zpyAddress(0x10));
cpu.setYRegister(0x25); cpu.setYRegister(0x25);
assertEquals(0x35, cpu.zpyAddress(0x10)); assertEquals(0x35, cpu.zpyAddress(0x10));
cpu.setYRegister(0xf5); cpu.setYRegister(0xf5);
assertEquals(0x05, cpu.zpyAddress(0x10)); assertEquals(0x05, cpu.zpyAddress(0x10));
cpu.setYRegister(0x00); cpu.setYRegister(0x00);
assertEquals(0x80, cpu.zpyAddress(0x80)); assertEquals(0x80, cpu.zpyAddress(0x80));
cpu.setYRegister(0x10); cpu.setYRegister(0x10);
assertEquals(0x90, cpu.zpyAddress(0x80)); assertEquals(0x90, cpu.zpyAddress(0x80));
cpu.setYRegister(0x25); cpu.setYRegister(0x25);
assertEquals(0xa5, cpu.zpyAddress(0x80)); assertEquals(0xa5, cpu.zpyAddress(0x80));
cpu.setYRegister(0x95); cpu.setYRegister(0x95);
assertEquals(0x15, cpu.zpyAddress(0x80)); assertEquals(0x15, cpu.zpyAddress(0x80));
} }
public void testPcStatus() {
cpu.setProgramCounter(0x03fa);
assertEquals("$03FA", cpu.getProgramCounterStatus());
}
public void testOpcodeStatus() throws MemoryAccessException {
// LDA (immediate)
bus.write(0x0200, 0xa9);
bus.write(0x0201, 0xef);
cpu.step();
assertEquals("LDA #$EF", cpu.getOpcodeStatus());
// BRK instruction
bus.write(0x0202, 0x00);
cpu.step();
assertEquals("BRK", cpu.getOpcodeStatus());
// Illegal opcode
cpu.step();
assertEquals("BRK", cpu.getOpcodeStatus());
}
} }