Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac5907691f | ||
|
|
fce0dad2a9 | ||
|
|
0c40cd325c | ||
|
|
22a9207dca | ||
|
|
7c2a007928 | ||
|
|
194a13d1ac | ||
|
|
66646116cb | ||
|
|
59c6d8e23b | ||
|
|
36615fc70b | ||
|
|
1a1d503abe | ||
|
|
eac387e472 | ||
|
|
bb82db6672 | ||
|
|
f675a6e462 | ||
|
|
ccae8905b3 | ||
|
|
6b9295e72d | ||
|
|
004087a742 | ||
|
|
7edbb12f68 | ||
|
|
4bfc196b49 | ||
|
|
b1e1e75555 | ||
|
|
da3015defd | ||
|
|
87b5b77dbf |
No files matched your search
@@ -1,15 +1,9 @@
|
|||||||
SYMON - A 6502 System Simulator
|
SYMON - A 6502 System Simulator
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
**NOTE: THIS SOFTWARE IS UNDER ACTIVE DEVELOPMENT. Feedback is welcome!**
|
**Version:** 1.0.0
|
||||||
|
|
||||||
**Version:** 0.9.9.0
|
**Last Updated:** 11 August, 2014
|
||||||
|
|
||||||
**Last Updated:** 26 July, 2014
|
|
||||||
|
|
||||||
Copyright (c) 2014 Seth J. Morabito <web@loomcom.com>
|
|
||||||
|
|
||||||
Portions Copyright (c) 2014 Maik Marten <maikmarten@googlemail.com>
|
|
||||||
|
|
||||||
See the file COPYING for license.
|
See the file COPYING for license.
|
||||||
|
|
||||||
@@ -31,6 +25,9 @@ Klaus Dormann's 6502 Functional Test Suite as of version 0.8.2
|
|||||||
(See [this thread on the 6502.org Forums] (http://forum.6502.org/viewtopic.php?f=2&t=2241)
|
(See [this thread on the 6502.org Forums] (http://forum.6502.org/viewtopic.php?f=2&t=2241)
|
||||||
for more information about this functional test suite).
|
for more information about this functional test suite).
|
||||||
|
|
||||||
|
Symon is under constant, active development. Feedback and patches
|
||||||
|
are always welcome.
|
||||||
|
|
||||||
## 2.0 Requirements
|
## 2.0 Requirements
|
||||||
|
|
||||||
- Java 1.7 or higher
|
- Java 1.7 or higher
|
||||||
@@ -39,8 +36,9 @@ for more information about this functional test suite).
|
|||||||
|
|
||||||
## 3.0 Features
|
## 3.0 Features
|
||||||
|
|
||||||
Symon can simiulate multiple 6502 based architectures. At present, two
|
Symon can simulate multiple 6502 based architectures. At present, three
|
||||||
machines are implemented: Symon (the default), and MULTICOMP.
|
machines are implemented: Symon (the default), MULTICOMP, and a "Simple"
|
||||||
|
machine useful for debugging.
|
||||||
|
|
||||||
### 3.1 Memory Maps
|
### 3.1 Memory Maps
|
||||||
|
|
||||||
@@ -61,6 +59,10 @@ memory.
|
|||||||
- `$E000`--`$FFFF`: 8KB ROM
|
- `$E000`--`$FFFF`: 8KB ROM
|
||||||
- `$FFD0`--`$FFD1`: Motorola 6850 ACIA
|
- `$FFD0`--`$FFD1`: Motorola 6850 ACIA
|
||||||
|
|
||||||
|
### 3.1.3 Simple Memory Map
|
||||||
|
|
||||||
|
- `$0000`--`$FFFF`: 64KB RAM
|
||||||
|
|
||||||
### 3.2 Serial Console and CPU Status
|
### 3.2 Serial Console and CPU Status
|
||||||
|
|
||||||
![Serial Console] (https://github.com/sethm/symon/raw/master/screenshots/console.png)
|
![Serial Console] (https://github.com/sethm/symon/raw/master/screenshots/console.png)
|
||||||
@@ -217,8 +219,16 @@ running.
|
|||||||
|
|
||||||
## 5.0 Revision History
|
## 5.0 Revision History
|
||||||
|
|
||||||
|
- **1.0.0-SNAPSHOT:** 10 August, 2014 - Added "Simple" machine
|
||||||
|
implementation, pure RAM with no IO. Added Klaus Dormann's
|
||||||
|
6502 Functional Tests for further machine verification (these
|
||||||
|
tests must be run in the "Simple" machine).
|
||||||
|
|
||||||
|
- **0.9.9.1:** 27 July, 2014 - Pressing 'Control' while clicking
|
||||||
|
'Reset' now performs a memory clear.
|
||||||
|
|
||||||
- **0.9.9:** 26 July, 2014 - MULTICOMP and multi-machine support
|
- **0.9.9:** 26 July, 2014 - MULTICOMP and multi-machine support
|
||||||
contributed by Maik Marten <maikmarten@googlemail.com>
|
contributed by Maik Merten <maikmerten@googlemail.com>
|
||||||
|
|
||||||
- **0.9.1:** 26 January, 2014 - Support for IRQ and NMI handling.
|
- **0.9.1:** 26 January, 2014 - Support for IRQ and NMI handling.
|
||||||
|
|
||||||
@@ -271,13 +281,20 @@ running.
|
|||||||
|
|
||||||
- Symbolic debugging.
|
- Symbolic debugging.
|
||||||
|
|
||||||
## 7.0 Acknowledgements
|
## 7.0 Copyright and Acknowledgements
|
||||||
|
|
||||||
|
**Copyright (c) 2014 Seth J. Morabito <web@loomcom.com>**
|
||||||
|
|
||||||
|
Portions Copyright (c) 2014 Maik Merten <maikmerten@googlemail.com>
|
||||||
|
|
||||||
|
Additional components used in this project are copyright their respective owners.
|
||||||
|
|
||||||
|
- Enhanced 6502 BASIC Copyright (c) Lee Davison
|
||||||
|
- 6502 Functional Tests Copyright (c) Klaus Dormann
|
||||||
|
- JTerminal Copyright (c) Graham Edgecombe
|
||||||
|
|
||||||
This project would not have been possible without the following resources:
|
This project would not have been possible without the following resources:
|
||||||
|
|
||||||
- [Graham Edgecombe's JTerminal project] (https://github.com/grahamedgecombe/jterminal),
|
|
||||||
which forms the core of Symon's console.
|
|
||||||
|
|
||||||
- [Andrew Jacobs' 6502 Pages] (http://www.obelisk.demon.co.uk/6502/), for
|
- [Andrew Jacobs' 6502 Pages] (http://www.obelisk.demon.co.uk/6502/), for
|
||||||
wonderfully detailed information about the 6502
|
wonderfully detailed information about the 6502
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<groupId>com.loomcom.symon</groupId>
|
<groupId>com.loomcom.symon</groupId>
|
||||||
<artifactId>symon</artifactId>
|
<artifactId>symon</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>0.9.9.0</version>
|
<version>1.0.0</version>
|
||||||
<name>symon</name>
|
<name>symon</name>
|
||||||
<url>http://www.loomcom.com/symon</url>
|
<url>http://www.loomcom.com/symon</url>
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ public class Bus {
|
|||||||
deviceMap.put(priority, deviceSet);
|
deviceMap.put(priority, deviceSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device.setBus(this);
|
||||||
deviceSet.add(device);
|
deviceSet.add(device);
|
||||||
buildDeviceAddressArray();
|
buildDeviceAddressArray();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,10 +55,6 @@ public class Cpu implements InstructionTable {
|
|||||||
public static final int IRQ_VECTOR_L = 0xfffe;
|
public static final int IRQ_VECTOR_L = 0xfffe;
|
||||||
public static final int IRQ_VECTOR_H = 0xffff;
|
public static final int IRQ_VECTOR_H = 0xffff;
|
||||||
|
|
||||||
// The delay in microseconds between steps.
|
|
||||||
// TODO: Make configurable
|
|
||||||
private static final int CLOCK_IN_NS = 1000;
|
|
||||||
|
|
||||||
/* Simulated behavior */
|
/* Simulated behavior */
|
||||||
private static CpuBehavior behavior;
|
private static CpuBehavior behavior;
|
||||||
|
|
||||||
@@ -872,7 +868,7 @@ public class Cpu implements InstructionTable {
|
|||||||
*/
|
*/
|
||||||
private int lsr(int m) {
|
private int lsr(int m) {
|
||||||
setCarryFlag((m & 0x01) != 0);
|
setCarryFlag((m & 0x01) != 0);
|
||||||
return (m >>> 1) & 0xff;
|
return (m & 0xff) >>> 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
package com.loomcom.symon;
|
package com.loomcom.symon;
|
||||||
|
|
||||||
import com.loomcom.symon.machines.MulticompMachine;
|
import com.loomcom.symon.machines.MulticompMachine;
|
||||||
|
import com.loomcom.symon.machines.SimpleMachine;
|
||||||
import com.loomcom.symon.machines.SymonMachine;
|
import com.loomcom.symon.machines.SymonMachine;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
@@ -51,13 +52,15 @@ public class Main {
|
|||||||
machineClass = SymonMachine.class;
|
machineClass = SymonMachine.class;
|
||||||
} else if(machine.equals("multicomp")) {
|
} else if(machine.equals("multicomp")) {
|
||||||
machineClass = MulticompMachine.class;
|
machineClass = MulticompMachine.class;
|
||||||
|
} else if (machine.equals("simple")) {
|
||||||
|
machineClass = SimpleMachine.class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while(true) {
|
while (true) {
|
||||||
if(machineClass == null) {
|
if (machineClass == null) {
|
||||||
Object[] possibilities = {"Symon", "Multicomp"};
|
Object[] possibilities = {"Symon", "Multicomp", "Simple"};
|
||||||
String s = (String)JOptionPane.showInputDialog(
|
String s = (String)JOptionPane.showInputDialog(
|
||||||
null,
|
null,
|
||||||
"Please choose the machine type to be emulated:",
|
"Please choose the machine type to be emulated:",
|
||||||
@@ -68,8 +71,10 @@ public class Main {
|
|||||||
"Symon");
|
"Symon");
|
||||||
|
|
||||||
|
|
||||||
if(s != null && s.equals("Multicomp")) {
|
if (s != null && s.equals("Multicomp")) {
|
||||||
machineClass = MulticompMachine.class;
|
machineClass = MulticompMachine.class;
|
||||||
|
} else if (s != null && s.equals("Simple")) {
|
||||||
|
machineClass = SimpleMachine.class;
|
||||||
} else {
|
} else {
|
||||||
machineClass = SymonMachine.class;
|
machineClass = SymonMachine.class;
|
||||||
}
|
}
|
||||||
@@ -91,13 +96,11 @@ public class Main {
|
|||||||
|
|
||||||
|
|
||||||
Simulator.MAIN_CMD cmd = simulator.waitForCommand();
|
Simulator.MAIN_CMD cmd = simulator.waitForCommand();
|
||||||
if(cmd.equals(Simulator.MAIN_CMD.SELECTMACHINE)) {
|
if (cmd.equals(Simulator.MAIN_CMD.SELECTMACHINE)) {
|
||||||
machineClass = null;
|
machineClass = null;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,8 @@ public class Simulator {
|
|||||||
|
|
||||||
private JButton runStopButton;
|
private JButton runStopButton;
|
||||||
private JButton stepButton;
|
private JButton stepButton;
|
||||||
private JButton resetButton;
|
private JButton softResetButton;
|
||||||
|
private JButton hardResetButton;
|
||||||
private JComboBox<String> stepCountBox;
|
private JComboBox<String> stepCountBox;
|
||||||
|
|
||||||
private JFileChooser fileChooser;
|
private JFileChooser fileChooser;
|
||||||
@@ -137,13 +138,13 @@ public class Simulator {
|
|||||||
*/
|
*/
|
||||||
public void createAndShowUi() throws IOException {
|
public void createAndShowUi() throws IOException {
|
||||||
mainWindow = new JFrame();
|
mainWindow = new JFrame();
|
||||||
mainWindow.setTitle("Symon 6502 Simulator");
|
mainWindow.setTitle("6502 Simulator - " + machine.getName());
|
||||||
mainWindow.setResizable(false);
|
mainWindow.setResizable(false);
|
||||||
mainWindow.getContentPane().setLayout(new BorderLayout());
|
mainWindow.getContentPane().setLayout(new BorderLayout());
|
||||||
|
|
||||||
// UI components used for I/O.
|
// UI components used for I/O.
|
||||||
this.console = new com.loomcom.symon.ui.Console(80, 25, DEFAULT_FONT);
|
this.console = new com.loomcom.symon.ui.Console(80, 25, DEFAULT_FONT);
|
||||||
this.statusPane = new StatusPanel();
|
this.statusPane = new StatusPanel(machine);
|
||||||
|
|
||||||
console.setBorderWidth(CONSOLE_BORDER_WIDTH);
|
console.setBorderWidth(CONSOLE_BORDER_WIDTH);
|
||||||
|
|
||||||
@@ -161,7 +162,8 @@ public class Simulator {
|
|||||||
|
|
||||||
runStopButton = new JButton("Run");
|
runStopButton = new JButton("Run");
|
||||||
stepButton = new JButton("Step");
|
stepButton = new JButton("Step");
|
||||||
resetButton = new JButton("Reset");
|
softResetButton = new JButton("Soft Reset");
|
||||||
|
hardResetButton = new JButton("Hard Reset");
|
||||||
|
|
||||||
stepCountBox = new JComboBox<String>(STEPS);
|
stepCountBox = new JComboBox<String>(STEPS);
|
||||||
stepCountBox.addActionListener(new ActionListener() {
|
stepCountBox.addActionListener(new ActionListener() {
|
||||||
@@ -179,7 +181,8 @@ public class Simulator {
|
|||||||
buttonContainer.add(runStopButton);
|
buttonContainer.add(runStopButton);
|
||||||
buttonContainer.add(stepButton);
|
buttonContainer.add(stepButton);
|
||||||
buttonContainer.add(stepCountBox);
|
buttonContainer.add(stepCountBox);
|
||||||
buttonContainer.add(resetButton);
|
buttonContainer.add(softResetButton);
|
||||||
|
buttonContainer.add(hardResetButton);
|
||||||
|
|
||||||
// Left side - console
|
// Left side - console
|
||||||
consoleContainer.add(console, BorderLayout.CENTER);
|
consoleContainer.add(console, BorderLayout.CENTER);
|
||||||
@@ -207,9 +210,17 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
resetButton.addActionListener(new ActionListener() {
|
softResetButton.addActionListener(new ActionListener() {
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
handleReset();
|
// If this was a CTRL-click, do a hard reset.
|
||||||
|
handleReset(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hardResetButton.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
// If this was a CTRL-click, do a hard reset.
|
||||||
|
handleReset(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -235,7 +246,7 @@ public class Simulator {
|
|||||||
mainWindow.setVisible(true);
|
mainWindow.setVisible(true);
|
||||||
|
|
||||||
console.requestFocus();
|
console.requestFocus();
|
||||||
handleReset();
|
handleReset(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MAIN_CMD waitForCommand() {
|
public MAIN_CMD waitForCommand() {
|
||||||
@@ -268,7 +279,7 @@ public class Simulator {
|
|||||||
/*
|
/*
|
||||||
* Perform a reset.
|
* Perform a reset.
|
||||||
*/
|
*/
|
||||||
private void handleReset() {
|
private void handleReset(boolean isColdReset) {
|
||||||
if (runLoop != null && runLoop.isRunning()) {
|
if (runLoop != null && runLoop.isRunning()) {
|
||||||
runLoop.requestStop();
|
runLoop.requestStop();
|
||||||
runLoop.interrupt();
|
runLoop.interrupt();
|
||||||
@@ -277,17 +288,24 @@ public class Simulator {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
logger.log(Level.INFO, "Reset requested. Resetting CPU.");
|
logger.log(Level.INFO, "Reset requested. Resetting CPU.");
|
||||||
// Reset and clear memory
|
// Reset CPU
|
||||||
machine.getCpu().reset();
|
machine.getCpu().reset();
|
||||||
// Clear the console.
|
// Clear the console.
|
||||||
console.reset();
|
console.reset();
|
||||||
// Reset the trace log.
|
// Reset the trace log.
|
||||||
traceLog.reset();
|
traceLog.reset();
|
||||||
|
// If we're doing a cold reset, clear the memory.
|
||||||
|
if (isColdReset) {
|
||||||
|
Memory mem = machine.getRam();
|
||||||
|
if (mem != null) {
|
||||||
|
mem.fill(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Update status.
|
// Update status.
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
// Now update the state
|
// Now update the state
|
||||||
statusPane.updateState(machine.getCpu());
|
statusPane.updateState();
|
||||||
memoryWindow.updateState();
|
memoryWindow.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -309,7 +327,7 @@ public class Simulator {
|
|||||||
if (traceLog.isVisible()) {
|
if (traceLog.isVisible()) {
|
||||||
traceLog.refresh();
|
traceLog.refresh();
|
||||||
}
|
}
|
||||||
statusPane.updateState(machine.getCpu());
|
statusPane.updateState();
|
||||||
memoryWindow.updateState();
|
memoryWindow.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -329,16 +347,15 @@ public class Simulator {
|
|||||||
|
|
||||||
// Read from the ACIA and immediately update the console if there's
|
// Read from the ACIA and immediately update the console if there's
|
||||||
// output ready.
|
// output ready.
|
||||||
if (machine.getAcia().hasTxChar()) {
|
if (machine.getAcia() != null && machine.getAcia().hasTxChar()) {
|
||||||
// This is thread-safe
|
// This is thread-safe
|
||||||
console.print(Character.toString((char) machine.getAcia().txRead()));
|
console.print(Character.toString((char) machine.getAcia().txRead()));
|
||||||
console.repaint();
|
console.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a key has been pressed, fill the ACIA.
|
// If a key has been pressed, fill the ACIA.
|
||||||
// TODO: Interrupt handling.
|
|
||||||
try {
|
try {
|
||||||
if (console.hasInput()) {
|
if (machine.getAcia() != null && console.hasInput()) {
|
||||||
machine.getAcia().rxWrite((int) console.readInputChar());
|
machine.getAcia().rxWrite((int) console.readInputChar());
|
||||||
}
|
}
|
||||||
} catch (FifoUnderrunException ex) {
|
} catch (FifoUnderrunException ex) {
|
||||||
@@ -356,7 +373,7 @@ public class Simulator {
|
|||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
// Now update the state
|
// Now update the state
|
||||||
statusPane.updateState(machine.getCpu());
|
statusPane.updateState();
|
||||||
memoryWindow.updateState();
|
memoryWindow.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -388,7 +405,7 @@ public class Simulator {
|
|||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
// Now update the state
|
// Now update the state
|
||||||
statusPane.updateState(machine.getCpu());
|
statusPane.updateState();
|
||||||
memoryWindow.updateState();
|
memoryWindow.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -435,7 +452,7 @@ public class Simulator {
|
|||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
statusPane.updateState(machine.getCpu());
|
statusPane.updateState();
|
||||||
memoryWindow.updateState();
|
memoryWindow.updateState();
|
||||||
runStopButton.setText("Run");
|
runStopButton.setText("Run");
|
||||||
stepButton.setEnabled(true);
|
stepButton.setEnabled(true);
|
||||||
@@ -445,7 +462,6 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
menuBar.simulatorDidStop();
|
menuBar.simulatorDidStop();
|
||||||
traceLog.simulatorDidStop();
|
traceLog.simulatorDidStop();
|
||||||
// TODO: Update memory window, if frame is visible.
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -470,7 +486,6 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
// TODO: Error dialogs on failure.
|
|
||||||
try {
|
try {
|
||||||
int retVal = fileChooser.showOpenDialog(mainWindow);
|
int retVal = fileChooser.showOpenDialog(mainWindow);
|
||||||
if (retVal == JFileChooser.APPROVE_OPTION) {
|
if (retVal == JFileChooser.APPROVE_OPTION) {
|
||||||
@@ -479,7 +494,10 @@ public class Simulator {
|
|||||||
long fileSize = f.length();
|
long fileSize = f.length();
|
||||||
|
|
||||||
if (fileSize > machine.getMemorySize()) {
|
if (fileSize > machine.getMemorySize()) {
|
||||||
throw new IOException("Program will not fit in available memory.");
|
throw new IOException("File will not fit in " +
|
||||||
|
"available memory ($" +
|
||||||
|
Integer.toString(machine.getMemorySize(), 16) +
|
||||||
|
" bytes)");
|
||||||
} else {
|
} else {
|
||||||
byte[] program = new byte[(int) fileSize];
|
byte[] program = new byte[(int) fileSize];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -498,13 +516,21 @@ public class Simulator {
|
|||||||
|
|
||||||
// Now load the program at the starting address.
|
// Now load the program at the starting address.
|
||||||
loadProgram(program, preferences.getProgramStartAddress());
|
loadProgram(program, preferences.getProgramStartAddress());
|
||||||
|
// TODO: "Don't Show Again" checkbox
|
||||||
|
JOptionPane.showMessageDialog(mainWindow,
|
||||||
|
"Loaded Successfully At " +
|
||||||
|
String.format("$%04X", preferences.getProgramStartAddress()),
|
||||||
|
"OK",
|
||||||
|
JOptionPane.PLAIN_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.SEVERE, "Unable to read program file: " + ex.getMessage());
|
logger.log(Level.SEVERE, "Unable to read program file: " + ex.getMessage());
|
||||||
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
} catch (MemoryAccessException ex) {
|
} catch (MemoryAccessException ex) {
|
||||||
logger.log(Level.SEVERE, "Memory access error loading program: " + ex.getMessage());
|
logger.log(Level.SEVERE, "Memory access error loading program: " + ex.getMessage());
|
||||||
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -517,7 +543,6 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
// TODO: Error dialogs on failure.
|
|
||||||
try {
|
try {
|
||||||
int retVal = fileChooser.showOpenDialog(mainWindow);
|
int retVal = fileChooser.showOpenDialog(mainWindow);
|
||||||
if (retVal == JFileChooser.APPROVE_OPTION) {
|
if (retVal == JFileChooser.APPROVE_OPTION) {
|
||||||
@@ -538,15 +563,24 @@ public class Simulator {
|
|||||||
|
|
||||||
logger.log(Level.INFO, "ROM File `" + romFile.getName() + "' loaded at " +
|
logger.log(Level.INFO, "ROM File `" + romFile.getName() + "' loaded at " +
|
||||||
String.format("0x%04X", machine.getRomBase()));
|
String.format("0x%04X", machine.getRomBase()));
|
||||||
|
// TODO: "Don't Show Again" checkbox
|
||||||
|
JOptionPane.showMessageDialog(mainWindow,
|
||||||
|
"Loaded Successfully At " +
|
||||||
|
String.format("$%04X", machine.getRomBase()),
|
||||||
|
"OK",
|
||||||
|
JOptionPane.PLAIN_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.SEVERE, "Unable to read ROM file: " + ex.getMessage());
|
logger.log(Level.SEVERE, "Unable to read ROM file: " + ex.getMessage());
|
||||||
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
} catch (MemoryRangeException ex) {
|
} catch (MemoryRangeException ex) {
|
||||||
logger.log(Level.SEVERE, "Memory range error while loading ROM file: " + ex.getMessage());
|
logger.log(Level.SEVERE, "Memory range error while loading ROM file: " + ex.getMessage());
|
||||||
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
} catch (MemoryAccessException ex) {
|
} catch (MemoryAccessException ex) {
|
||||||
logger.log(Level.SEVERE, "Memory access error while loading ROM file: " + ex.getMessage());
|
logger.log(Level.SEVERE, "Memory access error while loading ROM file: " + ex.getMessage());
|
||||||
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -564,8 +598,6 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SelectMachineAction extends AbstractAction {
|
class SelectMachineAction extends AbstractAction {
|
||||||
Simulator simulator;
|
|
||||||
|
|
||||||
public SelectMachineAction() {
|
public SelectMachineAction() {
|
||||||
super("Switch emulated machine...", null);
|
super("Switch emulated machine...", null);
|
||||||
putValue(SHORT_DESCRIPTION, "Select the type of the machine to be emulated");
|
putValue(SHORT_DESCRIPTION, "Select the type of the machine to be emulated");
|
||||||
@@ -695,7 +727,9 @@ public class Simulator {
|
|||||||
*/
|
*/
|
||||||
public void simulatorDidStart() {
|
public void simulatorDidStart() {
|
||||||
loadProgramItem.setEnabled(false);
|
loadProgramItem.setEnabled(false);
|
||||||
loadRomItem.setEnabled(false);
|
if (loadRomItem != null) {
|
||||||
|
loadRomItem.setEnabled(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -703,7 +737,9 @@ public class Simulator {
|
|||||||
*/
|
*/
|
||||||
public void simulatorDidStop() {
|
public void simulatorDidStop() {
|
||||||
loadProgramItem.setEnabled(true);
|
loadProgramItem.setEnabled(true);
|
||||||
loadRomItem.setEnabled(true);
|
if (loadRomItem != null) {
|
||||||
|
loadRomItem.setEnabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initMenu() {
|
private void initMenu() {
|
||||||
@@ -714,15 +750,22 @@ public class Simulator {
|
|||||||
JMenu fileMenu = new JMenu("File");
|
JMenu fileMenu = new JMenu("File");
|
||||||
|
|
||||||
loadProgramItem = new JMenuItem(new LoadProgramAction());
|
loadProgramItem = new JMenuItem(new LoadProgramAction());
|
||||||
loadRomItem = new JMenuItem(new LoadRomAction());
|
|
||||||
JMenuItem prefsItem = new JMenuItem(new ShowPrefsAction());
|
|
||||||
JMenuItem selectMachineItem = new JMenuItem(new SelectMachineAction());
|
|
||||||
JMenuItem quitItem = new JMenuItem(new QuitAction());
|
|
||||||
|
|
||||||
fileMenu.add(loadProgramItem);
|
fileMenu.add(loadProgramItem);
|
||||||
fileMenu.add(loadRomItem);
|
|
||||||
|
// Simple Machine does not implement a ROM, so it makes no sense to
|
||||||
|
// offer a ROM load option.
|
||||||
|
if (machine.getRom() != null) {
|
||||||
|
loadRomItem = new JMenuItem(new LoadRomAction());
|
||||||
|
fileMenu.add(loadRomItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
JMenuItem prefsItem = new JMenuItem(new ShowPrefsAction());
|
||||||
fileMenu.add(prefsItem);
|
fileMenu.add(prefsItem);
|
||||||
|
|
||||||
|
JMenuItem selectMachineItem = new JMenuItem(new SelectMachineAction());
|
||||||
fileMenu.add(selectMachineItem);
|
fileMenu.add(selectMachineItem);
|
||||||
|
|
||||||
|
JMenuItem quitItem = new JMenuItem(new QuitAction());
|
||||||
fileMenu.add(quitItem);
|
fileMenu.add(quitItem);
|
||||||
|
|
||||||
add(fileMenu);
|
add(fileMenu);
|
||||||
|
|||||||
@@ -57,4 +57,5 @@ public interface Machine {
|
|||||||
|
|
||||||
public int getMemorySize();
|
public int getMemorySize();
|
||||||
|
|
||||||
|
String getName();
|
||||||
}
|
}
|
||||||
@@ -148,4 +148,9 @@ public class MulticompMachine implements Machine {
|
|||||||
return MEMORY_SIZE;
|
return MEMORY_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Multicomp";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.loomcom.symon.machines;
|
||||||
|
|
||||||
|
import com.loomcom.symon.Bus;
|
||||||
|
import com.loomcom.symon.Cpu;
|
||||||
|
import com.loomcom.symon.devices.Acia;
|
||||||
|
import com.loomcom.symon.devices.Crtc;
|
||||||
|
import com.loomcom.symon.devices.Memory;
|
||||||
|
import com.loomcom.symon.devices.Via;
|
||||||
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A SimpleMachine is the simplest 6502 implementation possible - it
|
||||||
|
* consists solely of RAM and a CPU. This machine is primarily useful
|
||||||
|
* for running 6502 functional tests or debugging by hand.
|
||||||
|
*/
|
||||||
|
public class SimpleMachine implements Machine {
|
||||||
|
|
||||||
|
private static final int BUS_BOTTOM = 0x0000;
|
||||||
|
private static final int BUS_TOP = 0xffff;
|
||||||
|
|
||||||
|
private final Bus bus;
|
||||||
|
private final Memory ram;
|
||||||
|
private final Cpu cpu;
|
||||||
|
|
||||||
|
public SimpleMachine() throws MemoryRangeException {
|
||||||
|
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
|
||||||
|
this.ram = new Memory(BUS_BOTTOM, BUS_TOP, false);
|
||||||
|
this.cpu = new Cpu();
|
||||||
|
|
||||||
|
bus.addCpu(cpu);
|
||||||
|
bus.addDevice(ram);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bus getBus() {
|
||||||
|
return bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cpu getCpu() {
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Memory getRam() {
|
||||||
|
return ram;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Acia getAcia() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Via getVia() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Crtc getCrtc() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Memory getRom() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRom(Memory rom) throws MemoryRangeException {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRomBase() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRomSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMemorySize() {
|
||||||
|
return BUS_TOP + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Simple";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -161,7 +161,10 @@ public class SymonMachine implements Machine {
|
|||||||
return MEMORY_SIZE;
|
return MEMORY_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Symon";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,11 +24,17 @@
|
|||||||
package com.loomcom.symon.ui;
|
package com.loomcom.symon.ui;
|
||||||
|
|
||||||
import com.loomcom.symon.Cpu;
|
import com.loomcom.symon.Cpu;
|
||||||
|
import com.loomcom.symon.machines.Machine;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.border.EtchedBorder;
|
import javax.swing.border.EtchedBorder;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UI component that displays the current state of the simulated CPU.
|
* UI component that displays the current state of the simulated CPU.
|
||||||
@@ -73,14 +79,17 @@ public class StatusPanel extends JPanel {
|
|||||||
private JLabel xLabel;
|
private JLabel xLabel;
|
||||||
private JLabel yLabel;
|
private JLabel yLabel;
|
||||||
|
|
||||||
|
private Machine machine;
|
||||||
|
|
||||||
private static final int EMPTY_BORDER = 10;
|
private static final int EMPTY_BORDER = 10;
|
||||||
private static final Border LABEL_BORDER = BorderFactory.createEmptyBorder(0, 5, 0, 0);
|
private static final Border LABEL_BORDER = BorderFactory.createEmptyBorder(0, 5, 0, 0);
|
||||||
private static final Font LABEL_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 12);
|
private static final Font LABEL_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 12);
|
||||||
private static final Dimension LARGE_TEXT_FIELD_SIZE = new Dimension(134, 22);
|
private static final Dimension LARGE_TEXT_FIELD_SIZE = new Dimension(134, 22);
|
||||||
private static final Dimension SMALL_TEXT_FIELD_SIZE = new Dimension(65, 22);
|
private static final Dimension SMALL_TEXT_FIELD_SIZE = new Dimension(65, 22);
|
||||||
|
|
||||||
public StatusPanel() {
|
public StatusPanel(Machine machine) {
|
||||||
super();
|
super();
|
||||||
|
this.machine = machine;
|
||||||
createUi();
|
createUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,20 +108,20 @@ public class StatusPanel extends JPanel {
|
|||||||
JPanel statusFlagsPanel = new JPanel();
|
JPanel statusFlagsPanel = new JPanel();
|
||||||
statusFlagsPanel.setAlignmentX(LEFT_ALIGNMENT);
|
statusFlagsPanel.setAlignmentX(LEFT_ALIGNMENT);
|
||||||
|
|
||||||
carryOn = new ImageIcon(this.getClass().getResource("images/C_on.png"));
|
carryOn = new ImageIcon(this.getClass().getResource("/C_on.png"));
|
||||||
carryOff = new ImageIcon(this.getClass().getResource("images/C_off.png"));
|
carryOff = new ImageIcon(this.getClass().getResource("/C_off.png"));
|
||||||
zeroOn = new ImageIcon(this.getClass().getResource("images/Z_on.png"));
|
zeroOn = new ImageIcon(this.getClass().getResource("/Z_on.png"));
|
||||||
zeroOff = new ImageIcon(this.getClass().getResource("images/Z_off.png"));
|
zeroOff = new ImageIcon(this.getClass().getResource("/Z_off.png"));
|
||||||
irqOn = new ImageIcon(this.getClass().getResource("images/I_on.png"));
|
irqOn = new ImageIcon(this.getClass().getResource("/I_on.png"));
|
||||||
irqOff = new ImageIcon(this.getClass().getResource("images/I_off.png"));
|
irqOff = new ImageIcon(this.getClass().getResource("/I_off.png"));
|
||||||
decimalOn = new ImageIcon(this.getClass().getResource("images/D_on.png"));
|
decimalOn = new ImageIcon(this.getClass().getResource("/D_on.png"));
|
||||||
decimalOff = new ImageIcon(this.getClass().getResource("images/D_off.png"));
|
decimalOff = new ImageIcon(this.getClass().getResource("/D_off.png"));
|
||||||
breakOn = new ImageIcon(this.getClass().getResource("images/B_on.png"));
|
breakOn = new ImageIcon(this.getClass().getResource("/B_on.png"));
|
||||||
breakOff = new ImageIcon(this.getClass().getResource("images/B_off.png"));
|
breakOff = new ImageIcon(this.getClass().getResource("/B_off.png"));
|
||||||
overflowOn = new ImageIcon(this.getClass().getResource("images/O_on.png"));
|
overflowOn = new ImageIcon(this.getClass().getResource("/O_on.png"));
|
||||||
overflowOff = new ImageIcon(this.getClass().getResource("images/O_off.png"));
|
overflowOff = new ImageIcon(this.getClass().getResource("/O_off.png"));
|
||||||
negativeOn = new ImageIcon(this.getClass().getResource("images/N_on.png"));
|
negativeOn = new ImageIcon(this.getClass().getResource("/N_on.png"));
|
||||||
negativeOff = new ImageIcon(this.getClass().getResource("images/N_off.png"));
|
negativeOff = new ImageIcon(this.getClass().getResource("/N_off.png"));
|
||||||
|
|
||||||
// Initialize all to off
|
// Initialize all to off
|
||||||
carryFlagLabel = new JLabel(carryOff, JLabel.CENTER);
|
carryFlagLabel = new JLabel(carryOff, JLabel.CENTER);
|
||||||
@@ -157,12 +166,83 @@ public class StatusPanel extends JPanel {
|
|||||||
pcLabel.setToolTipText("Program Counter");
|
pcLabel.setToolTipText("Program Counter");
|
||||||
spLabel.setToolTipText("Stack Pointer");
|
spLabel.setToolTipText("Stack Pointer");
|
||||||
|
|
||||||
opcodeField = makeTextField(LARGE_TEXT_FIELD_SIZE);
|
opcodeField = makeTextField(LARGE_TEXT_FIELD_SIZE, false);
|
||||||
pcField = makeTextField(LARGE_TEXT_FIELD_SIZE);
|
pcField = makeTextField(LARGE_TEXT_FIELD_SIZE, true);
|
||||||
spField = makeTextField(SMALL_TEXT_FIELD_SIZE);
|
spField = makeTextField(SMALL_TEXT_FIELD_SIZE, true);
|
||||||
aField = makeTextField(SMALL_TEXT_FIELD_SIZE);
|
aField = makeTextField(SMALL_TEXT_FIELD_SIZE, true);
|
||||||
xField = makeTextField(SMALL_TEXT_FIELD_SIZE);
|
xField = makeTextField(SMALL_TEXT_FIELD_SIZE, true);
|
||||||
yField = makeTextField(SMALL_TEXT_FIELD_SIZE);
|
yField = makeTextField(SMALL_TEXT_FIELD_SIZE, true);
|
||||||
|
|
||||||
|
// Make fields editable
|
||||||
|
pcField.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
int newVal = getHexVal(pcField) & 0xffff;
|
||||||
|
machine.getCpu().setProgramCounter(newVal);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// Swallow exception
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
spField.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
int newVal = getHexVal(spField) & 0xff;
|
||||||
|
machine.getCpu().setStackPointer(newVal);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// Swallow exception
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
aField.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
int newVal = getHexVal(aField) & 0xff;
|
||||||
|
machine.getCpu().setAccumulator(newVal);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// Swallow exception
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
xField.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
int newVal = getHexVal(xField) & 0xff;
|
||||||
|
machine.getCpu().setXRegister(newVal);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// Swallow exception
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
yField.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
int newVal = getHexVal(yField) & 0xff;
|
||||||
|
machine.getCpu().setYRegister(newVal);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// Swallow exception
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
constraints.anchor = GridBagConstraints.LINE_START;
|
constraints.anchor = GridBagConstraints.LINE_START;
|
||||||
constraints.gridwidth = 2;
|
constraints.gridwidth = 2;
|
||||||
@@ -220,14 +300,13 @@ public class StatusPanel extends JPanel {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the display based on the current state of the CPU.
|
* Update the display based on the current state of the CPU.
|
||||||
*
|
|
||||||
* @param cpu The simulated 6502 CPU.
|
|
||||||
*/
|
*/
|
||||||
public void updateState(Cpu cpu) {
|
public void updateState() {
|
||||||
|
Cpu cpu = machine.getCpu();
|
||||||
Cpu.CpuState cpuState = cpu.getCpuState();
|
Cpu.CpuState cpuState = cpu.getCpuState();
|
||||||
|
|
||||||
// Update the Processor Status Flag display
|
// Update the Processor Status Flag display
|
||||||
int status = cpu.getCpuState().getStatusFlag();
|
int status = cpuState.getStatusFlag();
|
||||||
|
|
||||||
carryFlagLabel.setIcon(iconForFlag(status, 0));
|
carryFlagLabel.setIcon(iconForFlag(status, 0));
|
||||||
zeroFlagLabel.setIcon(iconForFlag(status, 1));
|
zeroFlagLabel.setIcon(iconForFlag(status, 1));
|
||||||
@@ -313,10 +392,10 @@ public class StatusPanel extends JPanel {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JTextField makeTextField(Dimension size) {
|
private JTextField makeTextField(Dimension size, boolean editable) {
|
||||||
JTextField textField = new JTextField("");
|
JTextField textField = new JTextField("");
|
||||||
textField.setAlignmentX(LEFT_ALIGNMENT);
|
textField.setAlignmentX(LEFT_ALIGNMENT);
|
||||||
textField.setEditable(false);
|
textField.setEditable(editable);
|
||||||
textField.setMinimumSize(size);
|
textField.setMinimumSize(size);
|
||||||
textField.setMaximumSize(size);
|
textField.setMaximumSize(size);
|
||||||
textField.setPreferredSize(size);
|
textField.setPreferredSize(size);
|
||||||
@@ -324,4 +403,8 @@ public class StatusPanel extends JPanel {
|
|||||||
return textField;
|
return textField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getHexVal(JTextField source) throws NumberFormatException {
|
||||||
|
String val = source.getText().replaceAll("[^0-9a-fA-F]", "");
|
||||||
|
return Integer.parseInt(val, 16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 191 B |
|
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 191 B |
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 196 B |
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 196 B |
|
Before Width: | Height: | Size: 192 B After Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 192 B After Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 183 B After Width: | Height: | Size: 183 B |
|
Before Width: | Height: | Size: 183 B After Width: | Height: | Size: 183 B |
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 192 B After Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 192 B After Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 198 B |
@@ -583,4 +583,23 @@ public class CpuTest extends TestCase {
|
|||||||
cpu.setYRegister(0x95);
|
cpu.setYRegister(0x95);
|
||||||
assertEquals(0x15, cpu.zpyAddress(0x80));
|
assertEquals(0x15, cpu.zpyAddress(0x80));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test for GitHub symon issue #9, "LSR can yield wrong result"
|
||||||
|
public void testRightShiftMasksBitsCorrectly() throws Exception {
|
||||||
|
// Illegal value, the accumulator should only care about the low 8 bytes.
|
||||||
|
// I'm a little uncomfortable with this test because really, setAccumulator should
|
||||||
|
// defensively mask the value, but does not. Is this relying on a bug to test another bug?
|
||||||
|
cpu.setAccumulator(0xff8);
|
||||||
|
// Sanity check, in case I ever change my mind on setAccumulator's behavior
|
||||||
|
assertEquals(0xff8, cpu.getAccumulator());
|
||||||
|
|
||||||
|
bus.loadProgram(0x4a, // LSR
|
||||||
|
0x4a); // LSR
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x7C, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x3E, cpu.getAccumulator());
|
||||||
|
}
|
||||||
}
|
}
|
||||||