diff --git a/src/main/java/com/loomcom/lm6502/AddressDecoder.java b/src/main/java/com/loomcom/lm6502/AddressDecoder.java index 72f380a..178d60a 100644 --- a/src/main/java/com/loomcom/lm6502/AddressDecoder.java +++ b/src/main/java/com/loomcom/lm6502/AddressDecoder.java @@ -4,30 +4,45 @@ import java.util.*; public class AddressDecoder { - public static final int MEMORY_BOTTOM = 0x0000; - public static final int MEMORY_TOP = 0xFFFF; + private int bottom = 0x0000; + private int top = 0xffff; /** - * Ordered map of memory ranges to IO devices. + * Ordered list of IO devices. */ - private SortedMap ioMap; + private List devices; - public AddressDecoder() { - ioMap = new TreeMap(); + public AddressDecoder(int size) { + 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 { // 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)) { throw new MemoryRangeException("The device being added overlaps " + "with an existing device."); } } - // Add the device to the map. - ioMap.put(d.getMemoryRange(), d); + // Add the device + devices.add(device); } /** @@ -37,27 +52,32 @@ public class AddressDecoder { */ public boolean isComplete() { // Emtpy maps cannot be complete. - if (ioMap.isEmpty()) { return false; } + if (devices.isEmpty()) { return false; } // Loop over devices and ensure they are contiguous. MemoryRange prev = null; int i = 0; - int size = ioMap.size(); - for (Map.Entry e : ioMap.entrySet()) { - MemoryRange cur = (MemoryRange)e.getKey(); + int length = devices.size(); + for (Device d : devices) { + MemoryRange cur = d.getMemoryRange(); if (i == 0) { - // If the first entry doesn't start at MEMORY_BOTTOM, return false. - if (cur.getStartAddress() != MEMORY_BOTTOM) { return false; } - } else if (i < size - 1) { + // If the first entry doesn't start at 'bottom', return false. + if (cur.getStartAddress() != bottom) { return false; } + } + + if (prev != null && i < length - 1) { // Otherwise, compare previous map's end against this map's // top. They must be adjacent! if (cur.getStartAddress() - 1 != prev.getEndAddress()) { 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++; prev = cur; } @@ -66,10 +86,8 @@ public class AddressDecoder { return true; } - /** - * Returns true if there are any gap in the memory map. - */ - public boolean hasGaps() { - return !isComplete(); + public List getDevices() { + // Expose a copy of the device list, not the original + return new ArrayList(devices); } } \ No newline at end of file diff --git a/src/main/java/com/loomcom/lm6502/Device.java b/src/main/java/com/loomcom/lm6502/Device.java index 0a3e2f5..47fcb45 100644 --- a/src/main/java/com/loomcom/lm6502/Device.java +++ b/src/main/java/com/loomcom/lm6502/Device.java @@ -9,14 +9,22 @@ public abstract class Device { /** The memory range for this device. */ private MemoryRange memoryRange; + private String name; + /** Reference to the CPU, for interrupts. */ private Cpu cpu; - public Device(MemoryRange range, Cpu cpu) { - this.memoryRange = range; + public Device(int address, int size, String name, Cpu cpu) + throws MemoryRangeException { + this.memoryRange = new MemoryRange(address, address + size - 1); + this.name = name; this.cpu = cpu; } + public abstract void write(int address, int data); + + public abstract int read(int address); + public MemoryRange getMemoryRange() { return memoryRange; } @@ -29,12 +37,12 @@ public abstract class Device { return memoryRange.getStartAddress(); } - public void generateInterrupt() { - cpu.interrupt(); + public String getName() { + return name; } - public void generateNonMaskableInterrupt() { - cpu.nmiInterrupt(); + public void setName(String name) { + this.name = name; } } \ No newline at end of file diff --git a/src/main/java/com/loomcom/lm6502/MemoryRange.java b/src/main/java/com/loomcom/lm6502/MemoryRange.java index b86532c..ecf3e3e 100644 --- a/src/main/java/com/loomcom/lm6502/MemoryRange.java +++ b/src/main/java/com/loomcom/lm6502/MemoryRange.java @@ -58,4 +58,12 @@ public class MemoryRange implements Comparable { Integer thatStartAddr = new Integer(other.getStartAddress()); 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(); + } } diff --git a/src/main/java/com/loomcom/lm6502/MemoryRangeException.java b/src/main/java/com/loomcom/lm6502/MemoryRangeException.java index e309c36..0d8e42c 100644 --- a/src/main/java/com/loomcom/lm6502/MemoryRangeException.java +++ b/src/main/java/com/loomcom/lm6502/MemoryRangeException.java @@ -3,7 +3,7 @@ package com.loomcom.lm6502; /** * 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) { super(msg); } diff --git a/src/main/java/com/loomcom/lm6502/devices/Memory.java b/src/main/java/com/loomcom/lm6502/devices/Memory.java new file mode 100644 index 0000000..a3878c8 --- /dev/null +++ b/src/main/java/com/loomcom/lm6502/devices/Memory.java @@ -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; + } + +} \ No newline at end of file diff --git a/src/test/java/com/loomcom/lm6502/AddressDecoderTest.java b/src/test/java/com/loomcom/lm6502/AddressDecoderTest.java new file mode 100644 index 0000000..29fb4c0 --- /dev/null +++ b/src/test/java/com/loomcom/lm6502/AddressDecoderTest.java @@ -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()); + } + +} \ No newline at end of file diff --git a/src/test/java/com/loomcom/lm6502/MemoryRangeTest.java b/src/test/java/com/loomcom/lm6502/MemoryRangeTest.java index 23937e0..d115626 100644 --- a/src/test/java/com/loomcom/lm6502/MemoryRangeTest.java +++ b/src/test/java/com/loomcom/lm6502/MemoryRangeTest.java @@ -80,6 +80,17 @@ public class MemoryRangeTest extends TestCase { 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 { MemoryRange a = new MemoryRange(0x0100, 0x0fff);