First work toward moving to a Swing UI for the simulator.
5
.gitignore
vendored
@ -1,7 +1,6 @@
|
||||
*~
|
||||
*#
|
||||
target
|
||||
.classpath
|
||||
.project
|
||||
.settings
|
||||
.DS_Store
|
||||
.idea
|
||||
symon.iml
|
||||
|
243
pom.xml
@ -1,115 +1,144 @@
|
||||
<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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.loomcom.symon</groupId>
|
||||
<artifactId>symon</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.1</version>
|
||||
<name>symon</name>
|
||||
<url>http://www.loomcom.com/symon</url>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jline</id>
|
||||
<name>JLine Project Repository</name>
|
||||
<url>http://jline.sourceforge.net/m2repo</url>
|
||||
</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>
|
||||
</dependencies>
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.loomcom.symon</groupId>
|
||||
<artifactId>symon</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.1</version>
|
||||
<name>symon</name>
|
||||
<url>http://www.loomcom.com/symon</url>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>
|
||||
UTF-8
|
||||
</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>
|
||||
UTF-8
|
||||
</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jline</id>
|
||||
<name>JLine Project Repository</name>
|
||||
<url>http://jline.sourceforge.net/m2repo</url>
|
||||
</repository>
|
||||
<!-- Loomcom's Maven2 repository -->
|
||||
<repository>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
<updatePolicy>always</updatePolicy>
|
||||
<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>
|
||||
<plugins>
|
||||
<!-- Set Java version to Java 1.5 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<!-- best lock down version of the plugin too -->
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Set Java version to Java 1.5 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<!-- best lock down version of the plugin too -->
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Set up Main-Class in the JAR manifest -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.loomcom.symon.Simulator</mainClass>
|
||||
<packageName>com.loomcom.symon</packageName>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<mode>development</mode>
|
||||
<url>${pom.url}</url>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Set up Main-Class in the JAR manifest -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.3.1</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.loomcom.symon.MainWindow</mainClass>
|
||||
<packageName>com.loomcom.symon</packageName>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<mode>development</mode>
|
||||
<url>${project.url}</url>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Cobertura is essential -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<check>
|
||||
<haltOnFailure>false</haltOnFailure>
|
||||
<regexes>
|
||||
<regex>
|
||||
<pattern>com.loomcom.symon.*</pattern>
|
||||
<branchRate>90</branchRate>
|
||||
<lineRate>90</lineRate>
|
||||
</regex>
|
||||
</regexes>
|
||||
</check>
|
||||
<instrumentation>
|
||||
<includes>
|
||||
<include>com/loomcom/symon/*.class</include>
|
||||
</includes>
|
||||
</instrumentation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>clean</id>
|
||||
<phase>pre-site</phase>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>instrument</id>
|
||||
<phase>site</phase>
|
||||
<goals>
|
||||
<goal>instrument</goal>
|
||||
<goal>cobertura</goal>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Cobertura is essential -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<check>
|
||||
<haltOnFailure>false</haltOnFailure>
|
||||
<regexes>
|
||||
<regex>
|
||||
<pattern>com.loomcom.symon.*</pattern>
|
||||
<branchRate>90</branchRate>
|
||||
<lineRate>90</lineRate>
|
||||
</regex>
|
||||
</regexes>
|
||||
</check>
|
||||
<instrumentation>
|
||||
<includes>
|
||||
<include>com/loomcom/symon/*.class</include>
|
||||
</includes>
|
||||
</instrumentation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>clean</id>
|
||||
<phase>pre-site</phase>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>instrument</id>
|
||||
<phase>site</phase>
|
||||
<goals>
|
||||
<goal>instrument</goal>
|
||||
<goal>cobertura</goal>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>cobertura-maven-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
</project>
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.loomcom.symon.devices.*;
|
||||
import com.loomcom.symon.exceptions.*;
|
||||
|
||||
@ -9,134 +10,140 @@ import com.loomcom.symon.exceptions.*;
|
||||
*/
|
||||
public class Bus {
|
||||
|
||||
// By default, our bus starts at 0, and goes up to 64K
|
||||
private int startAddress = 0x0000;
|
||||
private int endAddress = 0xffff;
|
||||
// The CPU
|
||||
private Cpu cpu;
|
||||
// Ordered list of IO devices.
|
||||
private SortedSet<Device> devices;
|
||||
// By default, our bus starts at 0, and goes up to 64K
|
||||
private int startAddress = 0x0000;
|
||||
private int endAddress = 0xffff;
|
||||
// The CPU
|
||||
private Cpu cpu;
|
||||
// Ordered list of IO devices.
|
||||
private SortedSet<Device> devices;
|
||||
|
||||
public Bus(int size) {
|
||||
this(0, size - 1);
|
||||
}
|
||||
public Bus(int size) {
|
||||
this(0, size - 1);
|
||||
}
|
||||
|
||||
public Bus(int startAddress, int endAddress) {
|
||||
this.devices = new TreeSet<Device>();
|
||||
this.startAddress = startAddress;
|
||||
this.endAddress = endAddress;
|
||||
}
|
||||
public Bus(int startAddress, int endAddress) {
|
||||
this.devices = new TreeSet<Device>();
|
||||
this.startAddress = startAddress;
|
||||
this.endAddress = endAddress;
|
||||
}
|
||||
|
||||
public int startAddress() {
|
||||
return startAddress;
|
||||
}
|
||||
public int startAddress() {
|
||||
return startAddress;
|
||||
}
|
||||
|
||||
public int endAddress() {
|
||||
return endAddress;
|
||||
}
|
||||
public int endAddress() {
|
||||
return endAddress;
|
||||
}
|
||||
|
||||
public void addDevice(Device device)
|
||||
public void addDevice(Device device)
|
||||
throws MemoryRangeException {
|
||||
// Make sure there's no memory overlap.
|
||||
MemoryRange memRange = device.getMemoryRange();
|
||||
for (Device d : devices) {
|
||||
if (d.getMemoryRange().overlaps(memRange)) {
|
||||
throw new MemoryRangeException("The device being added " +
|
||||
"overlaps with an existing " +
|
||||
"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;
|
||||
// Make sure there's no memory overlap.
|
||||
MemoryRange memRange = device.getMemoryRange();
|
||||
for (Device d : devices) {
|
||||
if (d.getMemoryRange().overlaps(memRange)) {
|
||||
throw new MemoryRangeException("The device being added " +
|
||||
"overlaps with an existing " +
|
||||
"device.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == length - 1) {
|
||||
// If the last entry doesn't end at endAddress, return false;
|
||||
if (cur.endAddress() != endAddress) { return false; }
|
||||
}
|
||||
|
||||
i++;
|
||||
prev = cur;
|
||||
// Add the device
|
||||
device.setBus(this);
|
||||
devices.add(device);
|
||||
}
|
||||
|
||||
// Must be complete.
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
public void addCpu(Cpu cpu) {
|
||||
cpu.setBus(this);
|
||||
this.cpu = cpu;
|
||||
}
|
||||
throw new MemoryAccessException("Read failed! No device at address.");
|
||||
}
|
||||
|
||||
public void write(int address, int value) 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();
|
||||
d.write(devAddr, value);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
// 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() {
|
||||
// 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);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
247
src/main/java/com/loomcom/symon/MainWindow.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,490 +1,157 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.*;
|
||||
|
||||
import com.loomcom.symon.devices.*;
|
||||
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 {
|
||||
|
||||
/**
|
||||
* The CPU itself.
|
||||
*/
|
||||
private Cpu cpu;
|
||||
private final static Logger logger = Logger.getLogger(Simulator.class.getName());
|
||||
|
||||
/**
|
||||
* The Bus responsible for routing memory read/write requests to the
|
||||
* correct IO devices.
|
||||
*/
|
||||
private Bus bus;
|
||||
private Bus bus;
|
||||
private Cpu cpu;
|
||||
private Acia acia;
|
||||
private Memory ram;
|
||||
private Memory rom;
|
||||
private boolean isRunning = false;
|
||||
|
||||
/**
|
||||
* The ACIA, used for charater in/out.
|
||||
*
|
||||
* By default, the simulator uses base address c000 for the ACIA.
|
||||
*/
|
||||
private Acia acia;
|
||||
private int updatesRequested = 0;
|
||||
|
||||
private BufferedReader in;
|
||||
private BufferedWriter out;
|
||||
private UiUpdater uiUpdater;
|
||||
|
||||
/* If true, trace execution of the CPU */
|
||||
private boolean trace = false;
|
||||
private int nextExamineAddress = 0;
|
||||
private static final int BUS_BOTTOM = 0x0000;
|
||||
private static final int BUS_TOP = 0xffff;
|
||||
|
||||
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 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;
|
||||
private static final int MEMORY_SIZE = 0xc000; // 48 KB
|
||||
public 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 MAX_REQUESTS_BETWEEN_UPDATES = 25000;
|
||||
|
||||
public Simulator() throws MemoryRangeException {
|
||||
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
|
||||
this.cpu = new Cpu();
|
||||
this.acia = new Acia(ACIA_BASE);
|
||||
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);
|
||||
}
|
||||
|
||||
bus.addCpu(cpu);
|
||||
bus.addDevice(new Memory(MEMORY_BASE, MEMORY_SIZE, false));
|
||||
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));
|
||||
public void loadProgram(byte[] program, int startAddress) throws MemoryAccessException {
|
||||
cpu.setResetVector(startAddress);
|
||||
|
||||
this.in = new BufferedReader(new InputStreamReader(System.in));
|
||||
this.out = new BufferedWriter(new OutputStreamWriter(System.out));
|
||||
}
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
public void run() throws MemoryAccessException, FifoUnderrunException {
|
||||
try {
|
||||
greeting();
|
||||
prompt();
|
||||
String command = null;
|
||||
while (!shouldQuit(command = readLine())) {
|
||||
try {
|
||||
dispatch(command);
|
||||
} catch (CommandFormatException ex) {
|
||||
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(" ");
|
||||
while (isRunning && !cpu.getBreakFlag()) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
writeLine(line.toString());
|
||||
} catch (SymonException ex) {
|
||||
logger.log(Level.SEVERE, "Exception in main simulator run thread. Exiting run.");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
nextExamineAddress = endAddress + 1;
|
||||
} else if (c.numArgs() == 1) {
|
||||
int address = stringToWord(c.getArgs()[0]);
|
||||
writeLine(String.format("%04x %02x", address, bus.read(address)));
|
||||
nextExamineAddress = address + 1;
|
||||
} else if (c.numArgs() == 0) {
|
||||
writeLine(String.format("%04x %02x", nextExamineAddress,
|
||||
bus.read(nextExamineAddress)));
|
||||
nextExamineAddress++;
|
||||
} else {
|
||||
throw new CommandFormatException("e [start [end]]");
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new CommandFormatException("Illegal Address");
|
||||
|
||||
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 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,
|
||||
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 step() throws MemoryAccessException, FifoUnderrunException {
|
||||
|
||||
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();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,38 +6,43 @@ import com.loomcom.symon.exceptions.*;
|
||||
|
||||
public class Memory extends Device {
|
||||
|
||||
private boolean readOnly;
|
||||
private int[] mem;
|
||||
private boolean readOnly;
|
||||
private int[] mem;
|
||||
|
||||
/* Initialize all locations to 0x00 (BRK) */
|
||||
private static final int DEFAULT_FILL = 0x00;
|
||||
/* Initialize all locations to 0x00 (BRK) */
|
||||
private static final int DEFAULT_FILL = 0x00;
|
||||
|
||||
public Memory(int address, int size, boolean readOnly)
|
||||
throws MemoryRangeException {
|
||||
super(address, size, (readOnly ? "RO Memory" : "RW Memory"));
|
||||
this.readOnly = readOnly;
|
||||
this.mem = new int[size];
|
||||
Arrays.fill(this.mem, 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 Memory(int address, int size, boolean readOnly)
|
||||
throws MemoryRangeException {
|
||||
super(address, size, (readOnly ? "RO Memory" : "RW Memory"));
|
||||
this.readOnly = readOnly;
|
||||
this.mem = new int[size];
|
||||
this.fill(DEFAULT_FILL);
|
||||
}
|
||||
}
|
||||
|
||||
public int read(int address) throws MemoryAccessException {
|
||||
return this.mem[address];
|
||||
}
|
||||
public Memory(int address, int size) throws MemoryRangeException {
|
||||
this(address, size, false);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Memory: " + getMemoryRange().toString();
|
||||
}
|
||||
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 {
|
||||
return this.mem[address];
|
||||
}
|
||||
|
||||
public void fill(int val) {
|
||||
Arrays.fill(this.mem, val);
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
return "Memory: " + getMemoryRange().toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package com.loomcom.symon.exceptions;
|
||||
|
||||
public class CommandFormatException extends Exception {
|
||||
public class CommandFormatException extends SymonException {
|
||||
public CommandFormatException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.loomcom.symon.exceptions;
|
||||
|
||||
public class FifoUnderrunException extends Exception {
|
||||
public class FifoUnderrunException extends SymonException {
|
||||
public FifoUnderrunException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ package com.loomcom.symon.exceptions;
|
||||
* Exception that will be thrown if access to memory or IO cannot be
|
||||
* accessed.
|
||||
*/
|
||||
public class MemoryAccessException extends Exception {
|
||||
public class MemoryAccessException extends SymonException {
|
||||
public MemoryAccessException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package com.loomcom.symon.exceptions;
|
||||
/**
|
||||
* 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) {
|
||||
super(msg);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
69
src/main/java/com/loomcom/symon/ui/Console.java
Normal 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) {
|
||||
}
|
||||
}
|
207
src/main/java/com/loomcom/symon/ui/StatusPane.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
63
src/main/java/com/loomcom/symon/ui/UiUpdater.java
Normal 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();
|
||||
}
|
||||
}
|
BIN
src/main/java/com/loomcom/symon/ui/images/B_off.png
Normal file
After Width: | Height: | Size: 191 B |
BIN
src/main/java/com/loomcom/symon/ui/images/B_on.png
Normal file
After Width: | Height: | Size: 191 B |
BIN
src/main/java/com/loomcom/symon/ui/images/C_off.png
Normal file
After Width: | Height: | Size: 196 B |
BIN
src/main/java/com/loomcom/symon/ui/images/C_on.png
Normal file
After Width: | Height: | Size: 196 B |
BIN
src/main/java/com/loomcom/symon/ui/images/D_off.png
Normal file
After Width: | Height: | Size: 192 B |
BIN
src/main/java/com/loomcom/symon/ui/images/D_on.png
Normal file
After Width: | Height: | Size: 192 B |
BIN
src/main/java/com/loomcom/symon/ui/images/I_off.png
Normal file
After Width: | Height: | Size: 183 B |
BIN
src/main/java/com/loomcom/symon/ui/images/I_on.png
Normal file
After Width: | Height: | Size: 183 B |
BIN
src/main/java/com/loomcom/symon/ui/images/N_off.png
Normal file
After Width: | Height: | Size: 198 B |
BIN
src/main/java/com/loomcom/symon/ui/images/N_on.png
Normal file
After Width: | Height: | Size: 198 B |
BIN
src/main/java/com/loomcom/symon/ui/images/O_off.png
Normal file
After Width: | Height: | Size: 192 B |
BIN
src/main/java/com/loomcom/symon/ui/images/O_on.png
Normal file
After Width: | Height: | Size: 192 B |
BIN
src/main/java/com/loomcom/symon/ui/images/Z_off.png
Normal file
After Width: | Height: | Size: 198 B |
BIN
src/main/java/com/loomcom/symon/ui/images/Z_on.png
Normal file
After Width: | Height: | Size: 198 B |
@ -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]);
|
||||
}
|
||||
|
||||
}
|
@ -10,387 +10,409 @@ import com.loomcom.symon.exceptions.*;
|
||||
*/
|
||||
public class CpuTest extends TestCase {
|
||||
|
||||
private Cpu cpu;
|
||||
private Bus bus;
|
||||
private Memory mem;
|
||||
private Cpu cpu;
|
||||
private Bus bus;
|
||||
private Memory mem;
|
||||
|
||||
public CpuTest(String 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);
|
||||
public CpuTest(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
for (int i = 0xff; i >= 0x00; i--) {
|
||||
assertEquals(i, cpu.stackPop());
|
||||
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);
|
||||
|
||||
public void testStackPush() throws MemoryAccessException {
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x00, bus.read(0x1ff));
|
||||
// All test programs start at 0x0200;
|
||||
bus.write(0xfffc, 0x00);
|
||||
bus.write(0xfffd, 0x02);
|
||||
|
||||
cpu.stackPush(0x06);
|
||||
assertEquals(0xfe, cpu.getStackPointer());
|
||||
assertEquals(0x06, bus.read(0x1ff));
|
||||
cpu.reset();
|
||||
}
|
||||
|
||||
cpu.stackPush(0x05);
|
||||
assertEquals(0xfd, cpu.getStackPointer());
|
||||
assertEquals(0x06, bus.read(0x1ff));
|
||||
assertEquals(0x05, bus.read(0x1fe));
|
||||
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());
|
||||
}
|
||||
|
||||
cpu.stackPush(0x04);
|
||||
assertEquals(0xfc, cpu.getStackPointer());
|
||||
assertEquals(0x06, bus.read(0x1ff));
|
||||
assertEquals(0x05, bus.read(0x1fe));
|
||||
assertEquals(0x04, bus.read(0x1fd));
|
||||
public void testStack() throws MemoryAccessException {
|
||||
|
||||
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(0x13);
|
||||
assertEquals(0x13, cpu.stackPop());
|
||||
|
||||
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(0x12);
|
||||
assertEquals(0x12, cpu.stackPop());
|
||||
|
||||
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));
|
||||
}
|
||||
for (int i = 0x00; i <= 0xff; i++) {
|
||||
cpu.stackPush(i);
|
||||
}
|
||||
|
||||
public void testStackPushWrapsAroundToStackTop() throws MemoryAccessException {
|
||||
cpu.setStackPointer(0x01);
|
||||
for (int i = 0xff; i >= 0x00; i--) {
|
||||
assertEquals(i, cpu.stackPop());
|
||||
}
|
||||
|
||||
cpu.stackPush(0x01);
|
||||
assertEquals(0x01, bus.read(0x101));
|
||||
assertEquals(0x00, cpu.getStackPointer());
|
||||
}
|
||||
|
||||
cpu.stackPush(0x02);
|
||||
assertEquals(0x02, bus.read(0x100));
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
public void testStackPush() throws MemoryAccessException {
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x00, bus.read(0x1ff));
|
||||
|
||||
cpu.stackPush(0x03);
|
||||
assertEquals(0x03, bus.read(0x1ff));
|
||||
assertEquals(0xfe, cpu.getStackPointer());
|
||||
}
|
||||
cpu.stackPush(0x06);
|
||||
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 {
|
||||
bus.write(0x1ff, 0x06);
|
||||
bus.write(0x1fe, 0x05);
|
||||
bus.write(0x1fd, 0x04);
|
||||
bus.write(0x1fc, 0x03);
|
||||
bus.write(0x1fb, 0x02);
|
||||
bus.write(0x1fa, 0x01);
|
||||
cpu.setStackPointer(0xf9);
|
||||
public void testStackPop() throws MemoryAccessException {
|
||||
bus.write(0x1ff, 0x06);
|
||||
bus.write(0x1fe, 0x05);
|
||||
bus.write(0x1fd, 0x04);
|
||||
bus.write(0x1fc, 0x03);
|
||||
bus.write(0x1fb, 0x02);
|
||||
bus.write(0x1fa, 0x01);
|
||||
cpu.setStackPointer(0xf9);
|
||||
|
||||
assertEquals(0x01, cpu.stackPop());
|
||||
assertEquals(0xfa, cpu.getStackPointer());
|
||||
assertEquals(0x01, cpu.stackPop());
|
||||
assertEquals(0xfa, cpu.getStackPointer());
|
||||
|
||||
assertEquals(0x02, cpu.stackPop());
|
||||
assertEquals(0xfb, cpu.getStackPointer());
|
||||
assertEquals(0x02, cpu.stackPop());
|
||||
assertEquals(0xfb, cpu.getStackPointer());
|
||||
|
||||
assertEquals(0x03, cpu.stackPop());
|
||||
assertEquals(0xfc, cpu.getStackPointer());
|
||||
assertEquals(0x03, cpu.stackPop());
|
||||
assertEquals(0xfc, cpu.getStackPointer());
|
||||
|
||||
assertEquals(0x04, cpu.stackPop());
|
||||
assertEquals(0xfd, cpu.getStackPointer());
|
||||
assertEquals(0x04, cpu.stackPop());
|
||||
assertEquals(0xfd, cpu.getStackPointer());
|
||||
|
||||
assertEquals(0x05, cpu.stackPop());
|
||||
assertEquals(0xfe, cpu.getStackPointer());
|
||||
assertEquals(0x05, cpu.stackPop());
|
||||
assertEquals(0xfe, cpu.getStackPointer());
|
||||
|
||||
assertEquals(0x06, cpu.stackPop());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
}
|
||||
assertEquals(0x06, cpu.stackPop());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
}
|
||||
|
||||
public void testStackPopWrapsAroundToStackBottom() throws MemoryAccessException {
|
||||
bus.write(0x1ff, 0x0f); // top of stack
|
||||
bus.write(0x100, 0xf0); // bottom of stack
|
||||
bus.write(0x101, 0xf1);
|
||||
bus.write(0x102, 0xf2);
|
||||
public void testStackPopWrapsAroundToStackBottom() throws MemoryAccessException {
|
||||
bus.write(0x1ff, 0x0f); // top of stack
|
||||
bus.write(0x100, 0xf0); // bottom of stack
|
||||
bus.write(0x101, 0xf1);
|
||||
bus.write(0x102, 0xf2);
|
||||
|
||||
cpu.setStackPointer(0xfe);
|
||||
cpu.setStackPointer(0xfe);
|
||||
|
||||
assertEquals(0x0f, cpu.stackPop());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x0f, cpu.stackPop());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
|
||||
assertEquals(0xf0, cpu.stackPop());
|
||||
assertEquals(0x00, cpu.getStackPointer());
|
||||
assertEquals(0xf0, cpu.stackPop());
|
||||
assertEquals(0x00, cpu.getStackPointer());
|
||||
|
||||
assertEquals(0xf1, cpu.stackPop());
|
||||
assertEquals(0x01, cpu.getStackPointer());
|
||||
assertEquals(0xf1, cpu.stackPop());
|
||||
assertEquals(0x01, cpu.getStackPointer());
|
||||
|
||||
assertEquals(0xf2, cpu.stackPop());
|
||||
assertEquals(0x02, cpu.getStackPointer());
|
||||
}
|
||||
assertEquals(0xf2, cpu.stackPop());
|
||||
assertEquals(0x02, cpu.getStackPointer());
|
||||
}
|
||||
|
||||
public void testStackPeekDoesNotAlterStackPointer() throws MemoryAccessException {
|
||||
assertEquals(0x00, cpu.stackPeek());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
public void testStackPeekDoesNotAlterStackPointer() throws MemoryAccessException {
|
||||
assertEquals(0x00, cpu.stackPeek());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
|
||||
cpu.stackPush(0x01);
|
||||
assertEquals(0x01, cpu.stackPeek());
|
||||
assertEquals(0xfe, cpu.getStackPointer());
|
||||
cpu.stackPush(0x01);
|
||||
assertEquals(0x01, cpu.stackPeek());
|
||||
assertEquals(0xfe, cpu.getStackPointer());
|
||||
|
||||
cpu.stackPush(0x02);
|
||||
assertEquals(0x02, cpu.stackPeek());
|
||||
assertEquals(0xfd, cpu.getStackPointer());
|
||||
cpu.stackPush(0x02);
|
||||
assertEquals(0x02, cpu.stackPeek());
|
||||
assertEquals(0xfd, cpu.getStackPointer());
|
||||
|
||||
cpu.stackPush(0x03);
|
||||
assertEquals(0x03, cpu.stackPeek());
|
||||
assertEquals(0xfc, cpu.getStackPointer());
|
||||
cpu.stackPush(0x03);
|
||||
assertEquals(0x03, cpu.stackPeek());
|
||||
assertEquals(0xfc, cpu.getStackPointer());
|
||||
|
||||
cpu.stackPush(0x04);
|
||||
assertEquals(0x04, cpu.stackPeek());
|
||||
assertEquals(0xfb, cpu.getStackPointer());
|
||||
assertEquals(0x04, cpu.stackPeek());
|
||||
assertEquals(0xfb, cpu.getStackPointer());
|
||||
assertEquals(0x04, cpu.stackPeek());
|
||||
assertEquals(0xfb, cpu.getStackPointer());
|
||||
}
|
||||
cpu.stackPush(0x04);
|
||||
assertEquals(0x04, cpu.stackPeek());
|
||||
assertEquals(0xfb, cpu.getStackPointer());
|
||||
assertEquals(0x04, cpu.stackPeek());
|
||||
assertEquals(0xfb, cpu.getStackPointer());
|
||||
assertEquals(0x04, cpu.stackPeek());
|
||||
assertEquals(0xfb, cpu.getStackPointer());
|
||||
}
|
||||
|
||||
public void testGetProcessorStatus() {
|
||||
// By default, no flags are set. Remember, bit 5
|
||||
// is always '1'.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
cpu.setCarryFlag();
|
||||
assertEquals(0x21, cpu.getProcessorStatus());
|
||||
cpu.setZeroFlag();
|
||||
assertEquals(0x23, cpu.getProcessorStatus());
|
||||
cpu.setIrqDisableFlag();
|
||||
assertEquals(0x27, cpu.getProcessorStatus());
|
||||
cpu.setDecimalModeFlag();
|
||||
assertEquals(0x2f, cpu.getProcessorStatus());
|
||||
cpu.setBreakFlag();
|
||||
assertEquals(0x3f, cpu.getProcessorStatus());
|
||||
cpu.setOverflowFlag();
|
||||
assertEquals(0x7f, cpu.getProcessorStatus());
|
||||
cpu.setNegativeFlag();
|
||||
assertEquals(0xff, cpu.getProcessorStatus());
|
||||
public void testGetProcessorStatus() {
|
||||
// By default, no flags are set. Remember, bit 5
|
||||
// is always '1'.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
cpu.setCarryFlag();
|
||||
assertEquals(0x21, cpu.getProcessorStatus());
|
||||
cpu.setZeroFlag();
|
||||
assertEquals(0x23, cpu.getProcessorStatus());
|
||||
cpu.setIrqDisableFlag();
|
||||
assertEquals(0x27, cpu.getProcessorStatus());
|
||||
cpu.setDecimalModeFlag();
|
||||
assertEquals(0x2f, cpu.getProcessorStatus());
|
||||
cpu.setBreakFlag();
|
||||
assertEquals(0x3f, cpu.getProcessorStatus());
|
||||
cpu.setOverflowFlag();
|
||||
assertEquals(0x7f, cpu.getProcessorStatus());
|
||||
cpu.setNegativeFlag();
|
||||
assertEquals(0xff, cpu.getProcessorStatus());
|
||||
|
||||
cpu.clearCarryFlag();
|
||||
assertEquals(0xfe, cpu.getProcessorStatus());
|
||||
cpu.clearZeroFlag();
|
||||
assertEquals(0xfc, cpu.getProcessorStatus());
|
||||
cpu.clearIrqDisableFlag();
|
||||
assertEquals(0xf8, cpu.getProcessorStatus());
|
||||
cpu.clearDecimalModeFlag();
|
||||
assertEquals(0xf0, cpu.getProcessorStatus());
|
||||
cpu.clearBreakFlag();
|
||||
assertEquals(0xe0, cpu.getProcessorStatus());
|
||||
cpu.clearOverflowFlag();
|
||||
assertEquals(0xa0, cpu.getProcessorStatus());
|
||||
cpu.clearNegativeFlag();
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
}
|
||||
cpu.clearCarryFlag();
|
||||
assertEquals(0xfe, cpu.getProcessorStatus());
|
||||
cpu.clearZeroFlag();
|
||||
assertEquals(0xfc, cpu.getProcessorStatus());
|
||||
cpu.clearIrqDisableFlag();
|
||||
assertEquals(0xf8, cpu.getProcessorStatus());
|
||||
cpu.clearDecimalModeFlag();
|
||||
assertEquals(0xf0, cpu.getProcessorStatus());
|
||||
cpu.clearBreakFlag();
|
||||
assertEquals(0xe0, cpu.getProcessorStatus());
|
||||
cpu.clearOverflowFlag();
|
||||
assertEquals(0xa0, cpu.getProcessorStatus());
|
||||
cpu.clearNegativeFlag();
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
public void testSetProcessorStatus() {
|
||||
// Default
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
public void testSetProcessorStatus() {
|
||||
// Default
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.setProcessorStatus(0x20|Cpu.P_CARRY);
|
||||
cpu.setProcessorStatus(0x20 | Cpu.P_CARRY);
|
||||
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE);
|
||||
cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE);
|
||||
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
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.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
|
||||
Cpu.P_OVERFLOW);
|
||||
cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
|
||||
Cpu.P_OVERFLOW);
|
||||
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
|
||||
Cpu.P_OVERFLOW|Cpu.P_BREAK);
|
||||
cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
|
||||
Cpu.P_OVERFLOW | Cpu.P_BREAK);
|
||||
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertTrue(cpu.getBreakFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertTrue(cpu.getBreakFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
|
||||
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
|
||||
Cpu.P_OVERFLOW|Cpu.P_BREAK|Cpu.P_DECIMAL);
|
||||
cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
|
||||
Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_DECIMAL);
|
||||
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertTrue(cpu.getDecimalModeFlag());
|
||||
assertTrue(cpu.getBreakFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertTrue(cpu.getDecimalModeFlag());
|
||||
assertTrue(cpu.getBreakFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
|
||||
Cpu.P_OVERFLOW|Cpu.P_BREAK|Cpu.P_DECIMAL|
|
||||
Cpu.P_IRQ_DISABLE);
|
||||
cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
|
||||
Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_DECIMAL |
|
||||
Cpu.P_IRQ_DISABLE);
|
||||
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getIrqDisableFlag());
|
||||
assertTrue(cpu.getDecimalModeFlag());
|
||||
assertTrue(cpu.getBreakFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getIrqDisableFlag());
|
||||
assertTrue(cpu.getDecimalModeFlag());
|
||||
assertTrue(cpu.getBreakFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.setProcessorStatus(0x20);
|
||||
cpu.setProcessorStatus(0x20);
|
||||
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.setProcessorStatus(0x00);
|
||||
cpu.setProcessorStatus(0x00);
|
||||
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
public void testAddress() {
|
||||
assertEquals(0xf1ea, cpu.address(0xea, 0xf1));
|
||||
assertEquals(0x00ea, cpu.address(0xea, 0x00));
|
||||
assertEquals(0xf100, cpu.address(0x00, 0xf1));
|
||||
assertEquals(0x1234, cpu.address(0x34, 0x12));
|
||||
assertEquals(0xffff, cpu.address(0xff, 0xff));
|
||||
}
|
||||
public void testAddress() {
|
||||
assertEquals(0xf1ea, cpu.address(0xea, 0xf1));
|
||||
assertEquals(0x00ea, cpu.address(0xea, 0x00));
|
||||
assertEquals(0xf100, cpu.address(0x00, 0xf1));
|
||||
assertEquals(0x1234, cpu.address(0x34, 0x12));
|
||||
assertEquals(0xffff, cpu.address(0xff, 0xff));
|
||||
}
|
||||
|
||||
public void testZpxAddress() {
|
||||
cpu.setXRegister(0x00);
|
||||
assertEquals(0x10, cpu.zpxAddress(0x10));
|
||||
cpu.setXRegister(0x10);
|
||||
assertEquals(0x20, cpu.zpxAddress(0x10));
|
||||
cpu.setXRegister(0x25);
|
||||
assertEquals(0x35, cpu.zpxAddress(0x10));
|
||||
cpu.setXRegister(0xf5);
|
||||
assertEquals(0x05, cpu.zpxAddress(0x10));
|
||||
public void testZpxAddress() {
|
||||
cpu.setXRegister(0x00);
|
||||
assertEquals(0x10, cpu.zpxAddress(0x10));
|
||||
cpu.setXRegister(0x10);
|
||||
assertEquals(0x20, cpu.zpxAddress(0x10));
|
||||
cpu.setXRegister(0x25);
|
||||
assertEquals(0x35, cpu.zpxAddress(0x10));
|
||||
cpu.setXRegister(0xf5);
|
||||
assertEquals(0x05, cpu.zpxAddress(0x10));
|
||||
|
||||
cpu.setXRegister(0x00);
|
||||
assertEquals(0x80, cpu.zpxAddress(0x80));
|
||||
cpu.setXRegister(0x10);
|
||||
assertEquals(0x90, cpu.zpxAddress(0x80));
|
||||
cpu.setXRegister(0x25);
|
||||
assertEquals(0xa5, cpu.zpxAddress(0x80));
|
||||
cpu.setXRegister(0x95);
|
||||
assertEquals(0x15, cpu.zpxAddress(0x80));
|
||||
}
|
||||
cpu.setXRegister(0x00);
|
||||
assertEquals(0x80, cpu.zpxAddress(0x80));
|
||||
cpu.setXRegister(0x10);
|
||||
assertEquals(0x90, cpu.zpxAddress(0x80));
|
||||
cpu.setXRegister(0x25);
|
||||
assertEquals(0xa5, cpu.zpxAddress(0x80));
|
||||
cpu.setXRegister(0x95);
|
||||
assertEquals(0x15, cpu.zpxAddress(0x80));
|
||||
}
|
||||
|
||||
public void testZpyAddress() {
|
||||
cpu.setYRegister(0x00);
|
||||
assertEquals(0x10, cpu.zpyAddress(0x10));
|
||||
cpu.setYRegister(0x10);
|
||||
assertEquals(0x20, cpu.zpyAddress(0x10));
|
||||
cpu.setYRegister(0x25);
|
||||
assertEquals(0x35, cpu.zpyAddress(0x10));
|
||||
cpu.setYRegister(0xf5);
|
||||
assertEquals(0x05, cpu.zpyAddress(0x10));
|
||||
public void testZpyAddress() {
|
||||
cpu.setYRegister(0x00);
|
||||
assertEquals(0x10, cpu.zpyAddress(0x10));
|
||||
cpu.setYRegister(0x10);
|
||||
assertEquals(0x20, cpu.zpyAddress(0x10));
|
||||
cpu.setYRegister(0x25);
|
||||
assertEquals(0x35, cpu.zpyAddress(0x10));
|
||||
cpu.setYRegister(0xf5);
|
||||
assertEquals(0x05, cpu.zpyAddress(0x10));
|
||||
|
||||
cpu.setYRegister(0x00);
|
||||
assertEquals(0x80, cpu.zpyAddress(0x80));
|
||||
cpu.setYRegister(0x10);
|
||||
assertEquals(0x90, cpu.zpyAddress(0x80));
|
||||
cpu.setYRegister(0x25);
|
||||
assertEquals(0xa5, cpu.zpyAddress(0x80));
|
||||
cpu.setYRegister(0x95);
|
||||
assertEquals(0x15, cpu.zpyAddress(0x80));
|
||||
}
|
||||
cpu.setYRegister(0x00);
|
||||
assertEquals(0x80, cpu.zpyAddress(0x80));
|
||||
cpu.setYRegister(0x10);
|
||||
assertEquals(0x90, cpu.zpyAddress(0x80));
|
||||
cpu.setYRegister(0x25);
|
||||
assertEquals(0xa5, cpu.zpyAddress(0x80));
|
||||
cpu.setYRegister(0x95);
|
||||
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());
|
||||
}
|
||||
|
||||
}
|