From d658cd0ae5f7de9f716b8571a3011ff1369244f7 Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Sat, 9 Aug 2014 14:57:18 +0200 Subject: [PATCH 01/11] completely untested and incomplete implementation of the SD card interface of the MULTICOMP --- .../loomcom/symon/devices/SdController.java | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/main/java/com/loomcom/symon/devices/SdController.java diff --git a/src/main/java/com/loomcom/symon/devices/SdController.java b/src/main/java/com/loomcom/symon/devices/SdController.java new file mode 100644 index 0000000..e637b16 --- /dev/null +++ b/src/main/java/com/loomcom/symon/devices/SdController.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2008-2013 Seth J. Morabito + * Maik Merten + * + * 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.devices; + +import com.loomcom.symon.exceptions.MemoryAccessException; +import com.loomcom.symon.exceptions.MemoryRangeException; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Emulation for the SD-card controller of the MULTICOMP system. + * Neiter comlete nor correct. + * + */ +public class SdController extends Device { + + private enum Status { + IDLE, + READ, + WRITE + } + + public static final int CONTROLLER_SIZE = 8; + private final static Logger logger = Logger.getLogger(SdController.class.getName()); + private byte[] sdcontent; + + private int lba0,lba1,lba2; + private int command; + private int position; + private Status status; + + + public SdController(int address) throws MemoryRangeException { + super(address, address + CONTROLLER_SIZE - 1, "SDCONTROLLER"); + + // assume an empty 64K SD card by default + sdcontent = new byte[64 * 1024]; + + // try to load an actual SD card image + try { + FileInputStream fis = new FileInputStream(new File("sd.img")); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[2048]; + int read = fis.read(buf); + while(read > 0) { + baos.write(buf, 0, read); + read = fis.read(buf); + } + sdcontent = baos.toByteArray(); + } catch (IOException ex) { + logger.log(Level.INFO, "Could not load image for SD card from file 'sd.img'"); + } + } + + + @Override + public void write(int address, int data) throws MemoryAccessException { + switch(address) { + case 0 : + writeData(data); + break; + case 1 : + writeCommand(data); + break; + case 2 : + this.lba0 = data; + computePosition(); + break; + case 3 : + this.lba1 = data; + computePosition(); + break; + case 4 : + this.lba2 = data; + computePosition(); + break; + } + } + + @Override + public int read(int address) throws MemoryAccessException { + switch(address) { + case 0: + return readData(); + case 1: + return readStatus(); + case 2: + return lba0; + case 3: + return lba1; + case 4: + return lba2; + default: + return 0; + } + } + + private void computePosition() { + this.position = lba0 + (lba1 << 8) + (lba2 << 16); + // each sector is 512 bytes, so multiply accordingly + this.position <<= 9; + } + + private int readData() { + this.position %= this.sdcontent.length; + return this.sdcontent[this.position++]; + } + + private void writeData(int data) { + this.position %= this.sdcontent.length; + this.sdcontent[this.position++] = (byte) data; + } + + private int readStatus() { + switch(this.status) { + case IDLE: + return 128; + case READ: + return 224; + default: + return 0; + } + } + + private void writeCommand(int data) { + this.command = data; + switch(this.command) { + case 0 : + this.status = Status.READ; + break; + case 1 : + this.status = Status.WRITE; + break; + default: + this.status = Status.IDLE; + } + } + + @Override + public String toString() { + return getName() + "@" + String.format("%04X", this.getMemoryRange().startAddress); + } + +} From 3a40d35fdf624d9ff9503516eafaa657bf7b95fa Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Sat, 9 Aug 2014 14:58:29 +0200 Subject: [PATCH 02/11] use SD interface in MulticompMachine and lift the speed limit on the ACIA device to better match the experience on the real machine --- .../java/com/loomcom/symon/machines/MulticompMachine.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/loomcom/symon/machines/MulticompMachine.java b/src/main/java/com/loomcom/symon/machines/MulticompMachine.java index f5ec3cc..6eba28c 100644 --- a/src/main/java/com/loomcom/symon/machines/MulticompMachine.java +++ b/src/main/java/com/loomcom/symon/machines/MulticompMachine.java @@ -30,6 +30,7 @@ import com.loomcom.symon.devices.Acia; import com.loomcom.symon.devices.Acia6850; import com.loomcom.symon.devices.Crtc; import com.loomcom.symon.devices.Memory; +import com.loomcom.symon.devices.SdController; import com.loomcom.symon.devices.Via; import com.loomcom.symon.exceptions.MemoryRangeException; import java.io.File; @@ -51,6 +52,8 @@ public class MulticompMachine implements Machine { // ACIA at $FFD0-$FFD1 private static final int ACIA_BASE = 0xFFD0; + // SD controller at $FFD8-$FFDF + private static final int SD_BASE = 0xFFD8; // 8KB ROM at $E000-$FFFF private static final int ROM_BASE = 0xE000; @@ -62,6 +65,7 @@ public class MulticompMachine implements Machine { private final Cpu cpu; private final Acia acia; private final Memory ram; + private final SdController sdController; private Memory rom; @@ -70,10 +74,13 @@ public class MulticompMachine implements Machine { this.cpu = new Cpu(); this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false); this.acia = new Acia6850(ACIA_BASE); + this.acia.setBaudRate(0); + this.sdController = new SdController(SD_BASE); bus.addCpu(cpu); bus.addDevice(ram); bus.addDevice(acia, 1); + bus.addDevice(sdController, 1); // TODO: Make this configurable, of course. File romImage = new File("rom.bin"); From 7cb4e1c945f711ea9353eb120b7f72181d220c99 Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Sat, 9 Aug 2014 15:13:59 +0200 Subject: [PATCH 03/11] NPE fix and reset position on every command --- src/main/java/com/loomcom/symon/devices/SdController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/loomcom/symon/devices/SdController.java b/src/main/java/com/loomcom/symon/devices/SdController.java index e637b16..7c05840 100644 --- a/src/main/java/com/loomcom/symon/devices/SdController.java +++ b/src/main/java/com/loomcom/symon/devices/SdController.java @@ -54,7 +54,7 @@ public class SdController extends Device { private int lba0,lba1,lba2; private int command; private int position; - private Status status; + private Status status = Status.IDLE; public SdController(int address) throws MemoryRangeException { @@ -161,6 +161,7 @@ public class SdController extends Device { default: this.status = Status.IDLE; } + computePosition(); } @Override From c61a63d5bb4e0b52a1fde39ac58f8eaee07ac3ac Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Sat, 9 Aug 2014 16:47:49 +0200 Subject: [PATCH 04/11] more accurate emulation of the SD controller --- .../com/loomcom/symon/devices/SdController.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/loomcom/symon/devices/SdController.java b/src/main/java/com/loomcom/symon/devices/SdController.java index 7c05840..2083de0 100644 --- a/src/main/java/com/loomcom/symon/devices/SdController.java +++ b/src/main/java/com/loomcom/symon/devices/SdController.java @@ -111,12 +111,6 @@ public class SdController extends Device { return readData(); case 1: return readStatus(); - case 2: - return lba0; - case 3: - return lba1; - case 4: - return lba2; default: return 0; } @@ -129,8 +123,16 @@ public class SdController extends Device { } private int readData() { + if(status != Status.READ) { + return 0; + } + this.position %= this.sdcontent.length; - return this.sdcontent[this.position++]; + int data = this.sdcontent[this.position++]; + if(this.position % 512 == 0) { + this.status = Status.IDLE; + } + return data; } private void writeData(int data) { From 7a215736fef0c79aafabbaa651cce82362c62a5f Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Sat, 9 Aug 2014 17:55:18 +0200 Subject: [PATCH 05/11] rework SD controller emulation to work with Input- and OutputStreams (no longer load the image into a byte array) --- .../loomcom/symon/devices/SdController.java | 97 +++++++++++++------ 1 file changed, 68 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/loomcom/symon/devices/SdController.java b/src/main/java/com/loomcom/symon/devices/SdController.java index 2083de0..2d46dc7 100644 --- a/src/main/java/com/loomcom/symon/devices/SdController.java +++ b/src/main/java/com/loomcom/symon/devices/SdController.java @@ -27,9 +27,9 @@ package com.loomcom.symon.devices; import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryRangeException; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -48,34 +48,28 @@ public class SdController extends Device { } public static final int CONTROLLER_SIZE = 8; + private final int SECTOR_SIZE = 512; private final static Logger logger = Logger.getLogger(SdController.class.getName()); - private byte[] sdcontent; - + + private File sdImageFile; private int lba0,lba1,lba2; private int command; private int position; private Status status = Status.IDLE; + + private final byte[] readBuffer = new byte[SECTOR_SIZE]; + private final byte[] writeBuffer = new byte[SECTOR_SIZE]; + private int readPosition = 0; + private int writePosition = 0; public SdController(int address) throws MemoryRangeException { super(address, address + CONTROLLER_SIZE - 1, "SDCONTROLLER"); - - // assume an empty 64K SD card by default - sdcontent = new byte[64 * 1024]; - - // try to load an actual SD card image - try { - FileInputStream fis = new FileInputStream(new File("sd.img")); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buf = new byte[2048]; - int read = fis.read(buf); - while(read > 0) { - baos.write(buf, 0, read); - read = fis.read(buf); - } - sdcontent = baos.toByteArray(); - } catch (IOException ex) { - logger.log(Level.INFO, "Could not load image for SD card from file 'sd.img'"); + + sdImageFile = new File("sd.img"); + if(!sdImageFile.exists()) { + sdImageFile = null; + logger.log(Level.INFO, "Could not find SD card image 'sd.img'"); } } @@ -120,24 +114,70 @@ public class SdController extends Device { this.position = lba0 + (lba1 << 8) + (lba2 << 16); // each sector is 512 bytes, so multiply accordingly this.position <<= 9; - } + } + + private void prepareRead() { + this.status = Status.READ; + this.readPosition = 0; + computePosition(); + + if(sdImageFile != null) { + try { + FileInputStream fis = new FileInputStream(sdImageFile); + fis.skip(this.position); + int read = fis.read(readBuffer); + if(read < SECTOR_SIZE) { + logger.log(Level.WARNING, "not enough data to fill read buffer from SD image file"); + } + fis.close(); + } catch (IOException ex) { + logger.log(Level.WARNING, "could not fill read buffer from SD image file", ex); + } + } + } + + private void prepareWrite() { + this.status = Status.WRITE; + this.writePosition = 0; + computePosition(); + } + private int readData() { if(status != Status.READ) { return 0; } - this.position %= this.sdcontent.length; - int data = this.sdcontent[this.position++]; - if(this.position % 512 == 0) { + int data = readBuffer[readPosition++]; + + if(readPosition >= SECTOR_SIZE) { this.status = Status.IDLE; } + return data; } private void writeData(int data) { - this.position %= this.sdcontent.length; - this.sdcontent[this.position++] = (byte) data; + if(status != Status.WRITE) { + return; + } + + writeBuffer[writePosition++] = (byte) data; + + if(writePosition >= SECTOR_SIZE) { + if(sdImageFile != null) { + try { + FileOutputStream fos = new FileOutputStream(sdImageFile); + fos.write(writeBuffer, this.position, writeBuffer.length); + fos.close(); + } catch (IOException ex) { + logger.log(Level.WARNING, "could not write data back to SD image file!", ex); + } + } + + this.status = Status.IDLE; + } + } private int readStatus() { @@ -155,15 +195,14 @@ public class SdController extends Device { this.command = data; switch(this.command) { case 0 : - this.status = Status.READ; + prepareRead(); break; case 1 : - this.status = Status.WRITE; + prepareWrite(); break; default: this.status = Status.IDLE; } - computePosition(); } @Override From b2670aedf18dfa2338c2afc5904be2661aa106b1 Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Tue, 12 Aug 2014 19:25:37 +0200 Subject: [PATCH 06/11] email address update --- src/main/java/com/loomcom/symon/devices/SdController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/loomcom/symon/devices/SdController.java b/src/main/java/com/loomcom/symon/devices/SdController.java index 2d46dc7..0f7f998 100644 --- a/src/main/java/com/loomcom/symon/devices/SdController.java +++ b/src/main/java/com/loomcom/symon/devices/SdController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2013 Seth J. Morabito + * Copyright (c) 2008-2013 Seth J. Morabito * Maik Merten * * Permission is hereby granted, free of charge, to any person obtaining From 792366fddbcad0709dd8e7764a5823d3500a7989 Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Tue, 12 Aug 2014 21:34:00 +0200 Subject: [PATCH 07/11] some cleanup in the SD card controller --- .../loomcom/symon/devices/SdController.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/loomcom/symon/devices/SdController.java b/src/main/java/com/loomcom/symon/devices/SdController.java index 0f7f998..c323fa6 100644 --- a/src/main/java/com/loomcom/symon/devices/SdController.java +++ b/src/main/java/com/loomcom/symon/devices/SdController.java @@ -79,22 +79,18 @@ public class SdController extends Device { switch(address) { case 0 : writeData(data); - break; + return; case 1 : writeCommand(data); - break; + return; case 2 : this.lba0 = data; - computePosition(); - break; + return; case 3 : this.lba1 = data; - computePosition(); - break; + return; case 4 : this.lba2 = data; - computePosition(); - break; } } @@ -168,7 +164,8 @@ public class SdController extends Device { if(sdImageFile != null) { try { FileOutputStream fos = new FileOutputStream(sdImageFile); - fos.write(writeBuffer, this.position, writeBuffer.length); + fos.getChannel().position(this.position); + fos.write(writeBuffer, 0, writeBuffer.length); fos.close(); } catch (IOException ex) { logger.log(Level.WARNING, "could not write data back to SD image file!", ex); @@ -196,10 +193,10 @@ public class SdController extends Device { switch(this.command) { case 0 : prepareRead(); - break; + return; case 1 : prepareWrite(); - break; + return; default: this.status = Status.IDLE; } From 693d1959ac5908d98db2a5287108547575fb5c19 Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Tue, 12 Aug 2014 21:58:52 +0200 Subject: [PATCH 08/11] finer grained synchronization in TraceLog. Profiling indicates that this speeds up adding log entries quite considerably. --- .../java/com/loomcom/symon/ui/TraceLog.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/loomcom/symon/ui/TraceLog.java b/src/main/java/com/loomcom/symon/ui/TraceLog.java index 6b8981b..4366bf6 100644 --- a/src/main/java/com/loomcom/symon/ui/TraceLog.java +++ b/src/main/java/com/loomcom/symon/ui/TraceLog.java @@ -35,8 +35,8 @@ import java.awt.*; */ public class TraceLog extends JFrame { - private FifoRingBuffer traceLog; - private JTextArea traceLogTextArea; + private final FifoRingBuffer traceLog; + private final JTextArea traceLogTextArea; private static final Dimension MIN_SIZE = new Dimension(320, 200); private static final Dimension PREFERRED_SIZE = new Dimension(640, 480); @@ -67,11 +67,15 @@ public class TraceLog extends JFrame { * call. */ public void refresh() { - synchronized (this) { - StringBuilder logString = new StringBuilder(); + StringBuilder logString = new StringBuilder(); + + synchronized(traceLog) { for (Cpu.CpuState state : traceLog) { logString.append(state.toTraceEvent()); } + } + + synchronized(traceLogTextArea) { traceLogTextArea.setText(logString.toString()); } } @@ -80,8 +84,10 @@ public class TraceLog extends JFrame { * Reset the log area. */ public void reset() { - synchronized (this) { + synchronized(traceLog) { traceLog.reset(); + } + synchronized(traceLogTextArea) { traceLogTextArea.setText(""); traceLogTextArea.setEnabled(true); } @@ -93,7 +99,7 @@ public class TraceLog extends JFrame { * @param state The CPU State to append. */ public void append(Cpu.CpuState state) { - synchronized(this) { + synchronized(traceLog) { traceLog.push(new Cpu.CpuState(state)); } } From a9a1c1aa528e5c618682b9de265a45aaa141c5d2 Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Wed, 13 Aug 2014 19:38:54 +0200 Subject: [PATCH 09/11] get writing to a certain position in a file somewhat more correct --- .../java/com/loomcom/symon/devices/SdController.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/loomcom/symon/devices/SdController.java b/src/main/java/com/loomcom/symon/devices/SdController.java index c323fa6..8aa4e0f 100644 --- a/src/main/java/com/loomcom/symon/devices/SdController.java +++ b/src/main/java/com/loomcom/symon/devices/SdController.java @@ -29,8 +29,8 @@ import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryRangeException; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.RandomAccessFile; import java.util.logging.Level; import java.util.logging.Logger; @@ -163,10 +163,10 @@ public class SdController extends Device { if(writePosition >= SECTOR_SIZE) { if(sdImageFile != null) { try { - FileOutputStream fos = new FileOutputStream(sdImageFile); - fos.getChannel().position(this.position); - fos.write(writeBuffer, 0, writeBuffer.length); - fos.close(); + RandomAccessFile raf = new RandomAccessFile(sdImageFile, "rw"); + raf.skipBytes(this.position); + raf.write(writeBuffer, 0, writeBuffer.length); + raf.close(); } catch (IOException ex) { logger.log(Level.WARNING, "could not write data back to SD image file!", ex); } @@ -182,6 +182,7 @@ public class SdController extends Device { case IDLE: return 128; case READ: + case WRITE: return 224; default: return 0; From cc12e8f70a7a260719ebc300990d90089f47b486 Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Wed, 13 Aug 2014 19:56:40 +0200 Subject: [PATCH 10/11] proper status value when SD controller is in write mode --- src/main/java/com/loomcom/symon/devices/SdController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/loomcom/symon/devices/SdController.java b/src/main/java/com/loomcom/symon/devices/SdController.java index 8aa4e0f..4a1fb7c 100644 --- a/src/main/java/com/loomcom/symon/devices/SdController.java +++ b/src/main/java/com/loomcom/symon/devices/SdController.java @@ -182,8 +182,9 @@ public class SdController extends Device { case IDLE: return 128; case READ: - case WRITE: return 224; + case WRITE: + return 160; default: return 0; } From c595ff5370d1484a940d57dc71508460d9180ff2 Mon Sep 17 00:00:00 2001 From: Maik Merten Date: Fri, 15 Aug 2014 20:15:04 +0200 Subject: [PATCH 11/11] README update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ff07c6a..7c5276c 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ memory. - `$0000`--`$DFFF`: 56KB RAM - `$E000`--`$FFFF`: 8KB ROM - `$FFD0`--`$FFD1`: Motorola 6850 ACIA + - `$FFD8`--`$FFDF`: Controller for SD cards ### 3.1.3 Simple Memory Map