Work In Progress: CPU behavior, UI changes

This is something of a "Work in Progress" checkpoint of several features
that are all half baked:

1. Allow loading of 16KB ROM files at address $C000 at run-time, not
   just at startup. See the "Load ROM..." File menu item.

2. Introduces the notion of "CPU Behaviors", so the core 6502 CPU
   implementation can match the behavior of either an early NMOS 6502, late
   NMOS 6502, or CMOS 65C02. Very little of this is actually implemented so
   far.

3. Adds a completely bogus implementation of the 6522 VIA (it
   does absolutely nothing right now).

4. Changes the address of the ACIA in the simulated system to match a
   real hardware implementation I put together.
This commit is contained in:
Seth Morabito committed 2012-11-25 22:49:21 -08:00
1 parent 795ccfde5d
commit 2ebdd254b3
33 files changed
+529 -150

No files matched your search

+3 -3
View File
@@ -4,8 +4,8 @@ SYMON - A 6502 System Simulator
**NOTE: THIS IS BETA QUALITY SOFTWARE UNDER ACTIVE DEVELOPMENT. Feedback is **NOTE: THIS IS BETA QUALITY SOFTWARE UNDER ACTIVE DEVELOPMENT. Feedback is
welcome!** welcome!**
**Version:** 0.5 **Version:** 0.6
**Last Updated:** 21 October, 2012 **Last Updated:** 5 November, 2012
Copyright (c) 2008-2012 Seth J. Morabito <web@loomcom.com> Copyright (c) 2008-2012 Seth J. Morabito <web@loomcom.com>
@@ -47,7 +47,7 @@ Maven will build Symon, run unit tests, and produce a jar file in the
Symon is meant to be invoked directly from the jar file. To run with Symon is meant to be invoked directly from the jar file. To run with
Java 1.5 or greater, just type: Java 1.5 or greater, just type:
$ java -jar symon-0.3-jar-with-dependencies.jar $ java -jar symon-0.6-jar-with-dependencies.jar
When Symon is running, you should be presented with a simple graphical When Symon is running, you should be presented with a simple graphical
interface. interface.
+3 -2
View File
@@ -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.3</version> <version>0.6-snapshot</version>
<name>symon</name> <name>symon</name>
<url>http://www.loomcom.com/symon</url> <url>http://www.loomcom.com/symon</url>
<properties> <properties>
@@ -44,7 +44,7 @@
<dependency> <dependency>
<groupId>com.grahamedgecombe.jterminal</groupId> <groupId>com.grahamedgecombe.jterminal</groupId>
<artifactId>jterminal</artifactId> <artifactId>jterminal</artifactId>
<version>1.0.2-loomcom</version> <version>1.0.2.1-loomcom</version>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -79,6 +79,7 @@
<descriptorRefs> <descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef> <descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs> </descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
<archive> <archive>
<manifest> <manifest>
<mainClass>com.loomcom.symon.Simulator</mainClass> <mainClass>com.loomcom.symon.Simulator</mainClass>
+1 -1
View File
@@ -3,7 +3,7 @@
;; ;;
.alias iobase $c000 .alias iobase $8000
.alias iostatus [iobase + 1] .alias iostatus [iobase + 1]
.alias iocmd [iobase + 2] .alias iocmd [iobase + 2]
.alias ioctrl [iobase + 3] .alias ioctrl [iobase + 3]
BIN
View File
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -441,7 +441,7 @@ Ram_top = $C000 ; end of user RAM+1 (set as needed, should be page aligned)
; This start can be changed to suit your system ; This start can be changed to suit your system
.org $D000 .org $C000
; BASIC cold start entry point ; BASIC cold start entry point
+1 -1
View File
@@ -15,7 +15,7 @@ NMI_vec = IRQ_vec+$0A ; NMI code vector
; setup for the 6502 simulator environment ; setup for the 6502 simulator environment
IO_AREA = $C000 IO_AREA = $8800
ACIAdata = IO_AREA ; simulated ACIA r/w port ACIAdata = IO_AREA ; simulated ACIA r/w port
ACIAstatus = IO_AREA+1 ACIAstatus = IO_AREA+1
ACIAcommand = IO_AREA+2 ACIAcommand = IO_AREA+2
+2 -2
View File
@@ -1,6 +1,6 @@
MEMORY { MEMORY {
RAM1: start = $0000, size = $C000; RAM1: start = $0000, size = $8000;
ROM1: start = $D000, size = $2F00, fill = yes; ROM1: start = $C000, size = $3F00, fill = yes;
MONITOR: start = $FF00, size = $FA, fill = yes; MONITOR: start = $FF00, size = $FA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes; ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
} }
+1 -1
View File
@@ -3,7 +3,7 @@
;; ;;
.alias iobase $c000 .alias iobase $8000
.alias iostatus [iobase + 1] .alias iostatus [iobase + 1]
.alias iocmd [iobase + 2] .alias iocmd [iobase + 2]
.alias ioctrl [iobase + 3] .alias ioctrl [iobase + 3]
BIN
View File
Binary file not shown.
+24 -3
View File
@@ -10,11 +10,16 @@ import com.loomcom.symon.exceptions.*;
*/ */
public class Bus { public class Bus {
// The default address at which to load programs
public static int DEFAULT_LOAD_ADDRESS = 0x0200;
// By default, our bus starts at 0, and goes up to 64K // By default, our bus starts at 0, and goes up to 64K
private int startAddress = 0x0000; private int startAddress = 0x0000;
private int endAddress = 0xffff; private int endAddress = 0xffff;
// The CPU // The CPU
private Cpu cpu; private Cpu cpu;
// Ordered list of IO devices. // Ordered list of IO devices.
private SortedSet<Device> devices; private SortedSet<Device> devices;
@@ -36,8 +41,13 @@ public class Bus {
return endAddress; return endAddress;
} }
public void addDevice(Device device) /**
throws MemoryRangeException { * Add a device to the bus. Throws a MemoryRangeException if the device overlaps with any others.
*
* @param device
* @throws MemoryRangeException
*/
public void addDevice(Device device) throws MemoryRangeException {
// Make sure there's no memory overlap. // Make sure there's no memory overlap.
MemoryRange memRange = device.getMemoryRange(); MemoryRange memRange = device.getMemoryRange();
for (Device d : devices) { for (Device d : devices) {
@@ -54,9 +64,20 @@ public class Bus {
devices.add(device); devices.add(device);
} }
/**
* Remove a device from the bus.
*
* @param device
*/
public void removeDevice(Device device) {
if (devices.contains(device)) {
devices.remove(device);
}
}
public void addCpu(Cpu cpu) { public void addCpu(Cpu cpu) {
cpu.setBus(this);
this.cpu = cpu; this.cpu = cpu;
cpu.setBus(this);
} }
/** /**
+57 -6
View File
@@ -1,14 +1,37 @@
/*
* Copyright (c) 2008-2012 Seth J. Morabito <sethm@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon; package com.loomcom.symon;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
/** /**
* Main 6502 CPU Simulation. * This class provides a simulation of the MOS 6502 CPU's state machine.
* A simple interface allows this 6502 to read and write to a simulated bus,
* and exposes some of the internal state for inspection and debugging.
*/ */
public class Cpu implements InstructionTable { public class Cpu implements InstructionTable {
public static final int DEFAULT_BASE_ADDRESS = 0x200;
/* Process status register mnemonics */ /* Process status register mnemonics */
public static final int P_CARRY = 0x01; public static final int P_CARRY = 0x01;
public static final int P_ZERO = 0x02; public static final int P_ZERO = 0x02;
@@ -33,6 +56,9 @@ public class Cpu implements InstructionTable {
// TODO: Make configurable // TODO: Make configurable
private static final int CLOCK_IN_NS = 1000; private static final int CLOCK_IN_NS = 1000;
/* Simulated behavior */
private static CpuBehavior behavior;
/* The Bus */ /* The Bus */
private Bus bus; private Bus bus;
@@ -82,6 +108,11 @@ public class Cpu implements InstructionTable {
* Construct a new CPU. * Construct a new CPU.
*/ */
public Cpu() { public Cpu() {
this(CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG);
}
public Cpu(CpuBehavior behavior) {
this.behavior = behavior;
} }
/** /**
@@ -98,6 +129,14 @@ public class Cpu implements InstructionTable {
return bus; return bus;
} }
public void setBehavior(CpuBehavior behavior) {
this.behavior = behavior;
}
public CpuBehavior getBehavior() {
return behavior;
}
/** /**
* Reset the CPU to known initial values. * Reset the CPU to known initial values.
*/ */
@@ -389,7 +428,15 @@ public class Cpu implements InstructionTable {
break; break;
case 0x6c: // JMP - Indirect case 0x6c: // JMP - Indirect
lo = address(args[0], args[1]); // Address of low byte lo = address(args[0], args[1]); // Address of low byte
hi = lo + 1; // Address of high byte
if (args[0] == 0xff &&
(behavior == CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) {
hi = address(0x00, args[1]);
} else {
hi = lo + 1;
}
pc = address(bus.read(lo), bus.read(hi)); pc = address(bus.read(lo), bus.read(hi));
/* TODO: For accuracy, allow a flag to enable broken behavior /* TODO: For accuracy, allow a flag to enable broken behavior
* of early 6502s: * of early 6502s:
@@ -1400,11 +1447,15 @@ public class Cpu implements InstructionTable {
private void delayLoop(int opcode) { private void delayLoop(int opcode) {
int clockSteps = Cpu.instructionClocks[0xff & opcode]; int clockSteps = Cpu.instructionClocks[0xff & opcode];
// Just a precaution. This could be better. // Just a precaution. This could be better.
if (clockSteps == 0) { clockSteps = 1; } if (clockSteps == 0) {
clockSteps = 1;
}
long startTime = System.nanoTime(); long startTime = System.nanoTime();
long stopTime = startTime + (CLOCK_IN_NS * clockSteps); long stopTime = startTime + (CLOCK_IN_NS * clockSteps);
// Busy loop // Busy loop
while (System.nanoTime() < stopTime) { ; } while (System.nanoTime() < stopTime) {
;
}
} }
/** /**
@@ -2,6 +2,43 @@ package com.loomcom.symon;
public interface InstructionTable { public interface InstructionTable {
/**
* Enumeration of valid CPU behaviors. These determine what behavior and instruction
* set will be simulated, depending on desired version of 6502.
*
* TODO: As of version 0.6, this is still not used! All CPUs are "idealized" NMOS 6502 only.
*/
public enum CpuBehavior {
/**
* The earliest NMOS 6502 includes a bug that causes the ROR instruction
* to behave like an ASL that does not affect the carry bit. This version
* is very rare in the wild.
*
* NB: Does NOT implement "unimplemented" NMOS instructions.
*/
NMOS_WITH_ROR_BUG,
/**
* All NMOS 6502's have a bug with the indirect JMP instruction. If the
*
* NB: Does NOT implement "unimplemented" NMOS instructions.
*/
NMOS_WITH_INDIRECT_JMP_BUG,
/**
* Emulate an NMOS 6502 without the indirect JMP bug. This type of 6502
* does not actually exist in the wild.
*
* NB: Does NOT implement "unimplemented" NMOS instructions.
*/
NMOS_WITHOUT_INDIRECT_JMP_BUG,
/**
* Emulate a CMOS 65C02, with all CMOS instructions and addressing modes.
*/
CMOS
}
/** /**
* Enumeration of Addressing Modes. * Enumeration of Addressing Modes.
*/ */
+145 -42
View File
@@ -1,15 +1,41 @@
/*
* Copyright (c) 2008-2012 Seth J. Morabito <sethm@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon; package com.loomcom.symon;
import com.loomcom.symon.devices.Acia; import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Memory; import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.devices.Via;
import com.loomcom.symon.exceptions.FifoUnderrunException; import com.loomcom.symon.exceptions.FifoUnderrunException;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException; import com.loomcom.symon.exceptions.MemoryRangeException;
import com.loomcom.symon.exceptions.SymonException; import com.loomcom.symon.exceptions.SymonException;
import com.loomcom.symon.ui.Console;
import com.loomcom.symon.ui.PreferencesDialog; import com.loomcom.symon.ui.PreferencesDialog;
import com.loomcom.symon.ui.StatusPanel; import com.loomcom.symon.ui.StatusPanel;
import com.loomcom.symon.ui.Console; import sun.rmi.rmic.iiop.DirectoryLoader;
import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
@@ -19,24 +45,35 @@ import java.util.Observable;
import java.util.Observer; import java.util.Observer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.swing.*;
/**
* Symon Simulator Interface and Control.
*
* This class provides a control and I/O system for the simulated 6502 system.
* It includes the simulated CPU itself, as well as 32KB of RAM, 16KB of ROM,
* and a simulated ACIA for serial I/O. The ACIA is attached to a dumb terminal
* with a basic 80x25 character display.
*
*/
public class Simulator implements ActionListener, Observer { public class Simulator implements ActionListener, Observer {
// Constants used by the simulated system. These define the memory map. // Constants used by the simulated system. These define the memory map.
private static final int BUS_BOTTOM = 0x0000; private static final int BUS_BOTTOM = 0x0000;
private static final int BUS_TOP = 0xffff; private static final int BUS_TOP = 0xffff;
// 32K of RAM // 32K of RAM from $0000 - $7FFF
private static final int MEMORY_BASE = 0x0000; private static final int MEMORY_BASE = 0x0000;
private static final int MEMORY_SIZE = 0xC000; private static final int MEMORY_SIZE = 0x8000;
// IO area at $D000 // VIA at $8000-$800F
private static final int ACIA_BASE = 0xC000; private static final int VIA_BASE = 0x8000;
// 8KB ROM at E000-FFFF // ACIA at $8800-$8803
private static final int ROM_BASE = 0xD000; private static final int ACIA_BASE = 0x8800;
private static final int ROM_SIZE = 0x3000;
// 16KB ROM at $C000-$FFFF
private static final int ROM_BASE = 0xC000;
private static final int ROM_SIZE = 0x4000;
// Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford // Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford
// to refresh the view on every simulated clock cycle. Instead, we will only refresh the view after this // to refresh the view on every simulated clock cycle. Instead, we will only refresh the view after this
@@ -55,8 +92,9 @@ public class Simulator implements ActionListener, Observer {
private final Bus bus; private final Bus bus;
private final Cpu cpu; private final Cpu cpu;
private final Acia acia; private final Acia acia;
private final Via via;
private final Memory ram; private final Memory ram;
private final Memory rom; private Memory rom;
// A counter to keep track of the number of UI updates that have been // A counter to keep track of the number of UI updates that have been
// requested // requested
@@ -74,38 +112,37 @@ public class Simulator implements ActionListener, Observer {
// The most recently read key code // The most recently read key code
private char keyBuffer; private char keyBuffer;
// TODO: loadMenuItem seriously violates encapsulation! // TODO: loadProgramItem seriously violates encapsulation!
// A far better solution would be to extend JMenu and add callback // A far better solution would be to extend JMenu and add callback
// methods to enable and disable menus as required. // methods to enable and disable menus as required.
// Menu Items // Menu Items
private JMenuItem loadMenuItem; private JMenuItem loadProgramItem;
private JMenuItem loadRomItem;
private JFileChooser fileChooser; private JFileChooser fileChooser;
private PreferencesDialog preferences; private PreferencesDialog preferences;
public Simulator() throws MemoryRangeException, IOException { public Simulator() throws MemoryRangeException, IOException {
this.acia = new Acia(ACIA_BASE); this.acia = new Acia(ACIA_BASE);
this.via = new Via(VIA_BASE);
this.bus = new Bus(BUS_BOTTOM, BUS_TOP); this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu(); this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_SIZE, false); this.ram = new Memory(MEMORY_BASE, MEMORY_SIZE, false);
bus.addCpu(cpu);
bus.addDevice(ram);
bus.addDevice(via);
bus.addDevice(acia);
// TODO: Make this configurable, of course. // TODO: Make this configurable, of course.
File romImage = new File("rom.bin"); File romImage = new File("rom.bin");
if (romImage.canRead()) { if (romImage.canRead()) {
logger.info("Loading ROM image from file " + romImage); logger.info("Loading ROM image from file " + romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_SIZE, romImage); this.rom = Memory.makeROM(ROM_BASE, ROM_SIZE, romImage);
} else {
logger.info("No ROM file 'rom.bin' found. ROM image will be empty R/W memory.");
// Just make it a normal RAM image.
this.rom = Memory.makeRAM(ROM_BASE, ROM_SIZE);
}
bus.addCpu(cpu);
bus.addDevice(ram);
bus.addDevice(acia);
bus.addDevice(rom); bus.addDevice(rom);
} }
}
/** /**
* Display the main simulator UI. * Display the main simulator UI.
@@ -124,7 +161,7 @@ public class Simulator implements ActionListener, Observer {
this.statusPane = new StatusPanel(); this.statusPane = new StatusPanel();
// File Chooser // File Chooser
fileChooser = new JFileChooser(); fileChooser = new JFileChooser(System.getProperty("user.dir"));
preferences = new PreferencesDialog(mainWindow, true); preferences = new PreferencesDialog(mainWindow, true);
preferences.addObserver(this); preferences.addObserver(this);
@@ -177,8 +214,11 @@ public class Simulator implements ActionListener, Observer {
menuBar.add(fileMenu); menuBar.add(fileMenu);
loadMenuItem = new JMenuItem("Load Program"); loadProgramItem = new JMenuItem("Load Program");
loadMenuItem.setMnemonic(KeyEvent.VK_L); loadProgramItem.setMnemonic(KeyEvent.VK_L);
loadRomItem = new JMenuItem("Load ROM...");
loadRomItem.setMnemonic(KeyEvent.VK_R);
JMenuItem prefsItem = new JMenuItem("Preferences..."); JMenuItem prefsItem = new JMenuItem("Preferences...");
prefsItem.setMnemonic(KeyEvent.VK_P); prefsItem.setMnemonic(KeyEvent.VK_P);
@@ -186,12 +226,18 @@ public class Simulator implements ActionListener, Observer {
JMenuItem quitItem = new JMenuItem("Quit"); JMenuItem quitItem = new JMenuItem("Quit");
quitItem.setMnemonic(KeyEvent.VK_Q); quitItem.setMnemonic(KeyEvent.VK_Q);
loadMenuItem.addActionListener(new ActionListener() { loadProgramItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) { public void actionPerformed(ActionEvent actionEvent) {
handleProgramLoad(); handleProgramLoad();
} }
}); });
loadRomItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
handleRomLoad();
}
});
prefsItem.addActionListener(new ActionListener() { prefsItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) { public void actionPerformed(ActionEvent actionEvent) {
showAndUpdatePreferences(); showAndUpdatePreferences();
@@ -204,7 +250,8 @@ public class Simulator implements ActionListener, Observer {
} }
}); });
fileMenu.add(loadMenuItem); fileMenu.add(loadProgramItem);
fileMenu.add(loadRomItem);
fileMenu.add(prefsItem); fileMenu.add(prefsItem);
fileMenu.add(quitItem); fileMenu.add(quitItem);
@@ -224,17 +271,76 @@ public class Simulator implements ActionListener, Observer {
} else if (actionEvent.getSource() == stepButton) { } else if (actionEvent.getSource() == stepButton) {
handleStep(); handleStep();
} else if (actionEvent.getSource() == runStopButton) { } else if (actionEvent.getSource() == runStopButton) {
if (runLoop != null && runLoop.isRunning()) {
handleStop();
} else {
handleStart();
}
}
}
private void handleStart() {
// Shift focus to the console. // Shift focus to the console.
console.requestFocus(); console.requestFocus();
if (runLoop != null && runLoop.isRunning()) {
runLoop.requestStop();
runLoop.interrupt();
runLoop = null;
} else {
// Spin up the new run loop // Spin up the new run loop
runLoop = new RunLoop(); runLoop = new RunLoop();
runLoop.start(); runLoop.start();
} }
private void handleStop() {
runLoop.requestStop();
runLoop.interrupt();
runLoop = null;
simulatorDidStop();
}
/*
* Handle post-stop actions.
*/
private void simulatorDidStop() {
// The simulator is lazy about updating the UI for performance reasons, so always request an
// immediate update after stopping.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Now update the state
statusPane.updateState(cpu);
}
});
// TODO: Write to Log window, if frame is visible.
// TODO: Update memory window, if frame is visible.
}
// TODO: Alert user of errors.
private void handleRomLoad() {
try {
int retVal = fileChooser.showOpenDialog(mainWindow);
if (retVal == JFileChooser.APPROVE_OPTION) {
File romFile = fileChooser.getSelectedFile();
if (romFile.canRead()) {
long fileSize = romFile.length();
if (fileSize != ROM_SIZE) {
throw new IOException("ROM file must be exactly " + String.valueOf(fileSize) + " bytes.");
} else {
if (rom != null) {
// Unload the existing ROM image.
bus.removeDevice(rom);
}
// Load the new ROM image
bus.addDevice(Memory.makeROM(ROM_BASE, ROM_SIZE, romFile));
logger.log(Level.INFO, "ROM File `" + romFile.getName() + "' loaded at " +
String.format("0x%04X", ROM_BASE));
}
}
}
} catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to read file: " + ex.getMessage());
ex.printStackTrace();
} catch (MemoryRangeException ex) {
logger.log(Level.SEVERE, "Memory range error loading ROM");
ex.printStackTrace();
} }
} }
@@ -315,15 +421,7 @@ public class Simulator implements ActionListener, Observer {
private void handleStep() { private void handleStep() {
try { try {
step(); step();
simulatorDidStop();
// The simulator is lazy about updating the UI for performance reasons, so always request an
// immediate update after stepping manually.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Now update the state
statusPane.updateState(cpu);
}
});
} catch (SymonException ex) { } catch (SymonException ex) {
logger.log(Level.SEVERE, "Exception during simulator step: " + ex.getMessage()); logger.log(Level.SEVERE, "Exception during simulator step: " + ex.getMessage());
ex.printStackTrace(); ex.printStackTrace();
@@ -466,9 +564,12 @@ public class Simulator implements ActionListener, Observer {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
public void run() { public void run() {
// Tell the console to start handling key presses
console.startListening();
// Don't allow step while the simulator is running // Don't allow step while the simulator is running
stepButton.setEnabled(false); stepButton.setEnabled(false);
loadMenuItem.setEnabled(false); loadProgramItem.setEnabled(false);
loadRomItem.setEnabled(false);
// Toggle the state of the run button // Toggle the state of the run button
runStopButton.setText("Stop"); runStopButton.setText("Stop");
} }
@@ -486,9 +587,11 @@ public class Simulator implements ActionListener, Observer {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
public void run() { public void run() {
console.stopListening();
// Allow step while the simulator is stopped // Allow step while the simulator is stopped
stepButton.setEnabled(true); stepButton.setEnabled(true);
loadMenuItem.setEnabled(true); loadProgramItem.setEnabled(true);
loadRomItem.setEnabled(true);
runStopButton.setText("Run"); runStopButton.setText("Run");
// Now update the state // Now update the state
statusPane.updateState(cpu); statusPane.updateState(cpu);
@@ -0,0 +1,44 @@
package com.loomcom.symon.devices;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
public class Via extends Device {
public static final int VIA_SIZE = 16;
private static final int ORB = 0;
private static final int ORA = 1;
private static final int DDRB = 2;
private static final int DDRA = 3;
private static final int T1C_L = 4;
private static final int T1C_H = 5;
private static final int T1L_L = 6;
private static final int T1L_H = 7;
private static final int T2C_L = 8;
private static final int T2C_H = 9;
private static final int SR = 10;
private static final int ACR = 11;
private static final int PCR = 12;
private static final int IFR = 13;
private static final int IER = 14;
private static final int ORA_H = 15;
public Via(int address) throws MemoryRangeException {
super(address, VIA_SIZE, "VIA");
}
@Override
public void write(int address, int data) throws MemoryAccessException {
}
@Override
public int read(int address) throws MemoryAccessException {
return 0;
}
@Override
public String toString() {
return null;
}
}
@@ -4,6 +4,8 @@ import java.awt.event.KeyEvent;
import java.awt.event.KeyListener; import java.awt.event.KeyListener;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.MouseListener; import java.awt.event.MouseListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.grahamedgecombe.jterminal.JTerminal; import com.grahamedgecombe.jterminal.JTerminal;
import com.grahamedgecombe.jterminal.vt100.Vt100TerminalModel; import com.grahamedgecombe.jterminal.vt100.Vt100TerminalModel;
@@ -24,12 +26,15 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
private static final int DEFAULT_COLUMNS = 80; private static final int DEFAULT_COLUMNS = 80;
private static final int DEFAULT_ROWS = 24; private static final int DEFAULT_ROWS = 24;
private static final int DEFAULT_BORDER_WIDTH = 10; private static final int DEFAULT_BORDER_WIDTH = 10;
// If true, swap CR and LF characters // If true, swap CR and LF characters.
private static final boolean SWAP_CR_AND_LF = true; private static final boolean SWAP_CR_AND_LF = true;
// If true, send CRLF (0x0d 0x0a) whenever CR is typed // If true, send CRLF (0x0d 0x0a) whenever CR is typed
private static final boolean SEND_CR_LF_FOR_CR = false; private static final boolean SEND_CR_LF_FOR_CR = false;
private FifoRingBuffer typeAheadBuffer; // If true, the console is actively listening for key input.
private boolean isListening;
private FifoRingBuffer<Character> typeAheadBuffer;
public Console() { public Console() {
this(DEFAULT_COLUMNS, DEFAULT_ROWS); this(DEFAULT_COLUMNS, DEFAULT_ROWS);
@@ -39,7 +44,8 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
super(new Vt100TerminalModel(columns, rows)); super(new Vt100TerminalModel(columns, rows));
// A small type-ahead buffer, as might be found in any real // A small type-ahead buffer, as might be found in any real
// VT100-style serial terminal. // VT100-style serial terminal.
this.typeAheadBuffer = new FifoRingBuffer(128); this.typeAheadBuffer = new FifoRingBuffer<Character>(128);
this.isListening = false;
setBorderWidth(DEFAULT_BORDER_WIDTH); setBorderWidth(DEFAULT_BORDER_WIDTH);
addKeyListener(this); addKeyListener(this);
addMouseListener(this); addMouseListener(this);
@@ -52,7 +58,6 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
/** /**
* Reset the console. This will cause the console to be cleared and the cursor returned to the * Reset the console. This will cause the console to be cleared and the cursor returned to the
* home position. * home position.
*
*/ */
public void reset() { public void reset() {
typeAheadBuffer.reset(); typeAheadBuffer.reset();
@@ -62,6 +67,14 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
repaint(); repaint();
} }
public void startListening() {
this.isListening = true;
}
public void stopListening() {
this.isListening = false;
}
/** /**
* Returns true if a key has been pressed since the last time input was read. * Returns true if a key has been pressed since the last time input was read.
* *
@@ -77,23 +90,24 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @param keyEvent The key event. * @param keyEvent The key event.
*/ */
public void keyTyped(KeyEvent keyEvent) { public void keyTyped(KeyEvent keyEvent) {
if (isListening) {
char keyTyped = keyEvent.getKeyChar(); char keyTyped = keyEvent.getKeyChar();
if (SWAP_CR_AND_LF) { if (SWAP_CR_AND_LF) {
if (keyTyped == 0x0a) { if (keyTyped == 0x0a) {
keyTyped = 0x0d; keyTyped = 0x0d;
} } else if (keyTyped == 0x0d) {
else if (keyTyped == 0x0d) {
keyTyped = 0x0a; keyTyped = 0x0a;
} }
} }
if (SEND_CR_LF_FOR_CR && keyTyped == 0x0d) { if (SEND_CR_LF_FOR_CR && keyTyped == 0x0d) {
typeAheadBuffer.push(0x0d); typeAheadBuffer.push((char) 0x0d);
typeAheadBuffer.push(0x0a); typeAheadBuffer.push((char) 0x0a);
} else { } else {
typeAheadBuffer.push(keyTyped); typeAheadBuffer.push(keyTyped);
} }
}
keyEvent.consume(); keyEvent.consume();
} }
@@ -113,7 +127,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @return The character typed. * @return The character typed.
*/ */
public char readInputChar() throws FifoUnderrunException { public char readInputChar() throws FifoUnderrunException {
return (char)typeAheadBuffer.pop(); return typeAheadBuffer.pop();
} }
/** /**
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2008-2012 Seth J. Morabito <sethm@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.loomcom.symon.ui;
import com.loomcom.symon.util.FifoRingBuffer;
import javax.swing.*;
/**
* This frame displays a trace of CPU execution. The most recent TRACE_LENGTH lines
* are captured in a buffer and rendered to the JFrame's main text area upon request.
*/
public class TraceLog extends JFrame {
private FifoRingBuffer<String> traceLog;
}
@@ -2,77 +2,57 @@ package com.loomcom.symon.util;
import com.loomcom.symon.exceptions.*; import com.loomcom.symon.exceptions.*;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
/** /**
* A very simple and efficient FIFO ring buffer implementation backed * A FIFO buffer with a bounded maximum size.
* by an array. It can only hold only integers.
*/ */
public class FifoRingBuffer { public class FifoRingBuffer<E> implements Iterable<E> {
private int[] fifoBuffer; private Queue<E> fifoBuffer;
private int readPtr = 0; private int maxLength;
private int writePtr = 0;
private int size = 0;
public FifoRingBuffer(int size) { public FifoRingBuffer(int maxLength) {
if (size <= 0) { this.fifoBuffer = new LinkedList<E>();
throw new RuntimeException("Cannot create a FifoRingBuffer with size <= 0."); this.maxLength = maxLength;
}
this.size = size;
fifoBuffer = new int[size];
} }
public int peek() throws FifoUnderrunException { public E pop() throws FifoUnderrunException {
if (isEmpty()) { return fifoBuffer.remove();
throw new FifoUnderrunException("Buffer Underrun");
}
return fifoBuffer[readPtr];
}
public int pop() throws FifoUnderrunException {
if (isEmpty()) {
throw new FifoUnderrunException("Buffer Underrun");
}
int val = fifoBuffer[readPtr];
incrementReadPointer();
return val;
} }
public boolean isEmpty() { public boolean isEmpty() {
return(readPtr == writePtr); return fifoBuffer.isEmpty();
} }
public boolean isFull() { public void push(E val) {
return((readPtr == 0 && writePtr == (size - 1)) || if (fifoBuffer.size() == maxLength) {
writePtr == (readPtr - 1)); // Delete the oldest element.
fifoBuffer.remove();
}
fifoBuffer.offer(val);
} }
public void push(int val) { public E peek() {
fifoBuffer[writePtr] = val; return fifoBuffer.peek();
incrementWritePointer();
} }
public void reset() { public void reset() {
readPtr = 0; fifoBuffer.clear();
writePtr = 0;
} }
private void incrementWritePointer() { public int length() {
if (++writePtr == size) { return fifoBuffer.size();
writePtr = 0;
}
if (writePtr == readPtr) {
incrementReadPointer();
}
}
private void incrementReadPointer() {
if (++readPtr == size) {
readPtr = 0;
}
} }
public String toString() { public String toString() {
return "[FifoRingBuffer: size=" + size + "]"; return "[FifoRingBuffer: size=" + fifoBuffer.size() + "]";
}
public Iterator<E> iterator() {
return fifoBuffer.iterator();
} }
} }
@@ -19,8 +19,8 @@ public class CpuAbsoluteModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -19,8 +19,8 @@ public class CpuAbsoluteXModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -19,8 +19,8 @@ public class CpuAbsoluteYModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -19,8 +19,8 @@ public class CpuAccumulatorModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset(); cpu.reset();
@@ -19,8 +19,8 @@ public class CpuImmediateModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -19,8 +19,8 @@ public class CpuImpliedModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset(); cpu.reset();
@@ -20,8 +20,8 @@ public class CpuIndexedIndirectModeTest {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -20,8 +20,8 @@ public class CpuIndirectIndexedModeTest {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -2,6 +2,7 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Memory; import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.InstructionTable;
import junit.framework.TestCase; import junit.framework.TestCase;
@@ -19,8 +20,8 @@ public class CpuIndirectModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -41,7 +42,7 @@ public class CpuIndirectModeTest extends TestCase {
/* JMP - Jump - $6c */ /* JMP - Jump - $6c */
public void test_JMP() throws MemoryAccessException { public void test_JMP_notOnPageBoundary() throws MemoryAccessException {
bus.write(0x3400, 0x00); bus.write(0x3400, 0x00);
bus.write(0x3401, 0x54); bus.write(0x3401, 0x54);
bus.loadProgram(0x6c, 0x00, 0x34); bus.loadProgram(0x6c, 0x00, 0x34);
@@ -51,4 +52,52 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG);
bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54);
bus.loadProgram(0x6c, 0xff, 0x34);
cpu.step();
assertEquals(0x2200, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
}
public void test_JMP_withIndirectBug() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG);
bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54);
bus.loadProgram(0x6c, 0xff, 0x34);
cpu.step();
assertEquals(0x2200, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
}
public void test_JMP_withOutIndirectBug() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITHOUT_INDIRECT_JMP_BUG);
bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54);
bus.loadProgram(0x6c, 0xff, 0x34);
cpu.step();
assertEquals(0x5400, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
}
public void test_JMP_cmos() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.CMOS);
bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54);
bus.loadProgram(0x6c, 0xff, 0x34);
cpu.step();
assertEquals(0x5400, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
}
} }
@@ -18,8 +18,8 @@ public class CpuIndirectXModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -19,8 +19,8 @@ public class CpuRelativeModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -19,8 +19,8 @@ public class CpuZeroPageModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -19,8 +19,8 @@ public class CpuZeroPageXModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -19,8 +19,8 @@ public class CpuZeroPageYModeTest extends TestCase {
bus.addDevice(mem); bus.addDevice(mem);
// Load the reset vector. // Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state // Assert initial state
@@ -0,0 +1,39 @@
package com.loomcom.symon;
import com.loomcom.symon.util.FifoRingBuffer;
import junit.framework.TestCase;
import java.lang.Character;
/**
* Created with IntelliJ IDEA.
* User: seth
* Date: 11/22/12
* Time: 11:28 PM
* To change this template use File | Settings | File Templates.
*/
public class FifoRingBufferTest extends TestCase {
public void testRingBufferShouldDeleteOldestItemIfAtMaximum() {
FifoRingBuffer<Character> buffer = new FifoRingBuffer<Character>(3);
assertEquals(0, buffer.length());
buffer.push('a');
assertEquals(1, buffer.length());
assertTrue('a' == buffer.peek());
buffer.push('b');
assertEquals(2, buffer.length());
assertTrue('a' == buffer.peek());
buffer.push('c');
assertEquals(3, buffer.length());
assertTrue('a' == buffer.peek());
buffer.push('d');
assertEquals(3, buffer.length());
assertTrue('b' == buffer.peek());
buffer.push('e');
assertEquals(3, buffer.length());
assertTrue('c' == buffer.peek());
}
}