mirror of
https://github.com/sethm/symon.git
synced 2025-08-09 11:25:13 +00:00
Tests for AddressDecoder. Fixed a bug in isComplete().
This commit is contained in:
@@ -4,30 +4,45 @@ import java.util.*;
|
|||||||
|
|
||||||
public class AddressDecoder {
|
public class AddressDecoder {
|
||||||
|
|
||||||
public static final int MEMORY_BOTTOM = 0x0000;
|
private int bottom = 0x0000;
|
||||||
public static final int MEMORY_TOP = 0xFFFF;
|
private int top = 0xffff;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ordered map of memory ranges to IO devices.
|
* Ordered list of IO devices.
|
||||||
*/
|
*/
|
||||||
private SortedMap<MemoryRange, Device> ioMap;
|
private List<Device> devices;
|
||||||
|
|
||||||
public AddressDecoder() {
|
public AddressDecoder(int size) {
|
||||||
ioMap = new TreeMap();
|
this(0, size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDevice(Device d)
|
public AddressDecoder(int bottom, int top) {
|
||||||
|
this.devices = new ArrayList(8);
|
||||||
|
this.bottom = bottom;
|
||||||
|
this.top = top;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int bottom() {
|
||||||
|
return bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int top() {
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDevice(Device device)
|
||||||
throws MemoryRangeException {
|
throws MemoryRangeException {
|
||||||
// Make sure there's no memory overlap.
|
// Make sure there's no memory overlap.
|
||||||
for (MemoryRange memRange : ioMap.keySet()) {
|
MemoryRange memRange = device.getMemoryRange();
|
||||||
|
for (Device d : devices) {
|
||||||
if (d.getMemoryRange().overlaps(memRange)) {
|
if (d.getMemoryRange().overlaps(memRange)) {
|
||||||
throw new MemoryRangeException("The device being added overlaps " +
|
throw new MemoryRangeException("The device being added overlaps " +
|
||||||
"with an existing device.");
|
"with an existing device.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the device to the map.
|
// Add the device
|
||||||
ioMap.put(d.getMemoryRange(), d);
|
devices.add(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,27 +52,32 @@ public class AddressDecoder {
|
|||||||
*/
|
*/
|
||||||
public boolean isComplete() {
|
public boolean isComplete() {
|
||||||
// Emtpy maps cannot be complete.
|
// Emtpy maps cannot be complete.
|
||||||
if (ioMap.isEmpty()) { return false; }
|
if (devices.isEmpty()) { return false; }
|
||||||
|
|
||||||
// Loop over devices and ensure they are contiguous.
|
// Loop over devices and ensure they are contiguous.
|
||||||
MemoryRange prev = null;
|
MemoryRange prev = null;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int size = ioMap.size();
|
int length = devices.size();
|
||||||
for (Map.Entry e : ioMap.entrySet()) {
|
for (Device d : devices) {
|
||||||
MemoryRange cur = (MemoryRange)e.getKey();
|
MemoryRange cur = d.getMemoryRange();
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
// If the first entry doesn't start at MEMORY_BOTTOM, return false.
|
// If the first entry doesn't start at 'bottom', return false.
|
||||||
if (cur.getStartAddress() != MEMORY_BOTTOM) { return false; }
|
if (cur.getStartAddress() != bottom) { return false; }
|
||||||
} else if (i < size - 1) {
|
}
|
||||||
|
|
||||||
|
if (prev != null && i < length - 1) {
|
||||||
// Otherwise, compare previous map's end against this map's
|
// Otherwise, compare previous map's end against this map's
|
||||||
// top. They must be adjacent!
|
// top. They must be adjacent!
|
||||||
if (cur.getStartAddress() - 1 != prev.getEndAddress()) {
|
if (cur.getStartAddress() - 1 != prev.getEndAddress()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// If the last entry doesn't end at MEMORY_TOP, return false;
|
|
||||||
if (cur.getEndAddress() != MEMORY_TOP) { return false; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i == length - 1) {
|
||||||
|
// If the last entry doesn't end at top, return false;
|
||||||
|
if (cur.getEndAddress() != top) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
prev = cur;
|
prev = cur;
|
||||||
}
|
}
|
||||||
@@ -66,10 +86,8 @@ public class AddressDecoder {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public List getDevices() {
|
||||||
* Returns true if there are any gap in the memory map.
|
// Expose a copy of the device list, not the original
|
||||||
*/
|
return new ArrayList(devices);
|
||||||
public boolean hasGaps() {
|
|
||||||
return !isComplete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -9,14 +9,22 @@ public abstract class Device {
|
|||||||
/** The memory range for this device. */
|
/** The memory range for this device. */
|
||||||
private MemoryRange memoryRange;
|
private MemoryRange memoryRange;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
/** Reference to the CPU, for interrupts. */
|
/** Reference to the CPU, for interrupts. */
|
||||||
private Cpu cpu;
|
private Cpu cpu;
|
||||||
|
|
||||||
public Device(MemoryRange range, Cpu cpu) {
|
public Device(int address, int size, String name, Cpu cpu)
|
||||||
this.memoryRange = range;
|
throws MemoryRangeException {
|
||||||
|
this.memoryRange = new MemoryRange(address, address + size - 1);
|
||||||
|
this.name = name;
|
||||||
this.cpu = cpu;
|
this.cpu = cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void write(int address, int data);
|
||||||
|
|
||||||
|
public abstract int read(int address);
|
||||||
|
|
||||||
public MemoryRange getMemoryRange() {
|
public MemoryRange getMemoryRange() {
|
||||||
return memoryRange;
|
return memoryRange;
|
||||||
}
|
}
|
||||||
@@ -29,12 +37,12 @@ public abstract class Device {
|
|||||||
return memoryRange.getStartAddress();
|
return memoryRange.getStartAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateInterrupt() {
|
public String getName() {
|
||||||
cpu.interrupt();
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateNonMaskableInterrupt() {
|
public void setName(String name) {
|
||||||
cpu.nmiInterrupt();
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -58,4 +58,12 @@ public class MemoryRange implements Comparable<MemoryRange> {
|
|||||||
Integer thatStartAddr = new Integer(other.getStartAddress());
|
Integer thatStartAddr = new Integer(other.getStartAddress());
|
||||||
return thisStartAddr.compareTo(thatStartAddr);
|
return thisStartAddr.compareTo(thatStartAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer desc = new StringBuffer("@");
|
||||||
|
desc.append(String.format("0x%04x", startAddress));
|
||||||
|
desc.append("-");
|
||||||
|
desc.append(String.format("0x%04x", endAddress));
|
||||||
|
return desc.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ package com.loomcom.lm6502;
|
|||||||
/**
|
/**
|
||||||
* Exception that will be thrown if devices conflict in the IO map.
|
* Exception that will be thrown if devices conflict in the IO map.
|
||||||
*/
|
*/
|
||||||
class MemoryRangeException extends Exception {
|
public class MemoryRangeException extends Exception {
|
||||||
public MemoryRangeException(String msg) {
|
public MemoryRangeException(String msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
27
src/main/java/com/loomcom/lm6502/devices/Memory.java
Normal file
27
src/main/java/com/loomcom/lm6502/devices/Memory.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package com.loomcom.lm6502.devices;
|
||||||
|
|
||||||
|
import com.loomcom.lm6502.*;
|
||||||
|
|
||||||
|
public class Memory extends Device {
|
||||||
|
|
||||||
|
private boolean readOnly;
|
||||||
|
|
||||||
|
public Memory(int address, int size, Cpu cpu, boolean readOnly)
|
||||||
|
throws MemoryRangeException {
|
||||||
|
super(address, size, "RW Memory", cpu);
|
||||||
|
this.readOnly = readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Memory(int address, int size, Cpu cpu)
|
||||||
|
throws MemoryRangeException {
|
||||||
|
this(address, size, cpu, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(int address, int data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read(int address) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
125
src/test/java/com/loomcom/lm6502/AddressDecoderTest.java
Normal file
125
src/test/java/com/loomcom/lm6502/AddressDecoderTest.java
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package com.loomcom.lm6502;
|
||||||
|
|
||||||
|
import junit.framework.*;
|
||||||
|
|
||||||
|
import com.loomcom.lm6502.devices.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AddressDecoderTest extends TestCase {
|
||||||
|
|
||||||
|
public AddressDecoderTest(String testName) {
|
||||||
|
super(testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Test suite() {
|
||||||
|
return new TestSuite(AddressDecoderTest.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreatingWithTopAndBottom() {
|
||||||
|
AddressDecoder ad = null;
|
||||||
|
|
||||||
|
ad = new AddressDecoder(0x00, 0xff);
|
||||||
|
assertEquals(0x00, ad.bottom());
|
||||||
|
assertEquals(0xff, ad.top());
|
||||||
|
|
||||||
|
ad = new AddressDecoder(0x20, 0xea);
|
||||||
|
assertEquals(0x20, ad.bottom());
|
||||||
|
assertEquals(0xea, ad.top());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCreatingWithSize() {
|
||||||
|
AddressDecoder ad = null;
|
||||||
|
|
||||||
|
ad = new AddressDecoder(256);
|
||||||
|
assertEquals(0x00, ad.bottom());
|
||||||
|
assertEquals(0xff, ad.top());
|
||||||
|
|
||||||
|
ad = new AddressDecoder(4096);
|
||||||
|
assertEquals(0x000, ad.bottom());
|
||||||
|
assertEquals(0xfff, ad.top());
|
||||||
|
|
||||||
|
ad = new AddressDecoder(65536);
|
||||||
|
assertEquals(0x0000, ad.bottom());
|
||||||
|
assertEquals(0xffff, ad.top());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAddDevice() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0x0100, null, true);
|
||||||
|
Device rom = new Memory(0x0100, 0x0200, null, false);
|
||||||
|
|
||||||
|
AddressDecoder ad = new AddressDecoder(0x0000, 0xffff);
|
||||||
|
|
||||||
|
assertEquals(0, ad.getDevices().size());
|
||||||
|
ad.addDevice(memory);
|
||||||
|
assertEquals(1, ad.getDevices().size());
|
||||||
|
ad.addDevice(rom);
|
||||||
|
assertEquals(2, ad.getDevices().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOverlappingDevicesShouldFail() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0x0100, null, true);
|
||||||
|
Device rom = new Memory(0x00ff, 0x0200, null, false);
|
||||||
|
|
||||||
|
AddressDecoder ad = new AddressDecoder(0x0000, 0xffff);
|
||||||
|
|
||||||
|
ad.addDevice(memory);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ad.addDevice(rom);
|
||||||
|
fail("Should have thrown a MemoryRangeException.");
|
||||||
|
} catch (MemoryRangeException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsCompleteWithFirstDeviceNotStartingAtBottom() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x00ff, 0xff00, null, true);
|
||||||
|
|
||||||
|
AddressDecoder ad = new AddressDecoder(0x0000, 0xffff);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", ad.isComplete());
|
||||||
|
ad.addDevice(memory);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", ad.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsCompleteWithOneDevice() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0x10000, null, true);
|
||||||
|
|
||||||
|
AddressDecoder ad = new AddressDecoder(0x0000, 0xffff);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", ad.isComplete());
|
||||||
|
ad.addDevice(memory);
|
||||||
|
assertTrue("Address space should have been complete!", ad.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsCompleteWithTwoDevices() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0x8000, null, true);
|
||||||
|
Device rom = new Memory(0x8000, 0x8000, null, false);
|
||||||
|
|
||||||
|
AddressDecoder ad = new AddressDecoder(0x0000, 0xffff);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", ad.isComplete());
|
||||||
|
ad.addDevice(memory);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", ad.isComplete());
|
||||||
|
ad.addDevice(rom);
|
||||||
|
assertTrue("Address space should have been complete!", ad.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsCompleteWithThreeDevices() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0x8000, null, true);
|
||||||
|
Device rom1 = new Memory(0x8000, 0x4000, null, false);
|
||||||
|
Device rom2 = new Memory(0xC000, 0x4000, null, false);
|
||||||
|
|
||||||
|
AddressDecoder ad = new AddressDecoder(0x0000, 0xffff);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", ad.isComplete());
|
||||||
|
ad.addDevice(memory);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", ad.isComplete());
|
||||||
|
ad.addDevice(rom1);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", ad.isComplete());
|
||||||
|
ad.addDevice(rom2);
|
||||||
|
assertTrue("Address space should have been complete!", ad.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -80,6 +80,17 @@ public class MemoryRangeTest extends TestCase {
|
|||||||
assertTrue(h.overlaps(b));
|
assertTrue(h.overlaps(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testToString() throws MemoryRangeException {
|
||||||
|
MemoryRange a = new MemoryRange(0x0abf, 0xff00);
|
||||||
|
assertEquals("@0x0abf-0xff00", a.toString());
|
||||||
|
|
||||||
|
MemoryRange b = new MemoryRange(0, 255);
|
||||||
|
assertEquals("@0x0000-0x00ff", b.toString());
|
||||||
|
|
||||||
|
MemoryRange c = new MemoryRange(0, 65535);
|
||||||
|
assertEquals("@0x0000-0xffff", c.toString());
|
||||||
|
}
|
||||||
|
|
||||||
public void testIncluded() throws MemoryRangeException {
|
public void testIncluded() throws MemoryRangeException {
|
||||||
MemoryRange a = new MemoryRange(0x0100, 0x0fff);
|
MemoryRange a = new MemoryRange(0x0100, 0x0fff);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user