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 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..4a1fb7c --- /dev/null +++ b/src/main/java/com/loomcom/symon/devices/SdController.java @@ -0,0 +1,212 @@ +/* + * 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.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +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 int SECTOR_SIZE = 512; + private final static Logger logger = Logger.getLogger(SdController.class.getName()); + + 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"); + + sdImageFile = new File("sd.img"); + if(!sdImageFile.exists()) { + sdImageFile = null; + logger.log(Level.INFO, "Could not find SD card image 'sd.img'"); + } + } + + + @Override + public void write(int address, int data) throws MemoryAccessException { + switch(address) { + case 0 : + writeData(data); + return; + case 1 : + writeCommand(data); + return; + case 2 : + this.lba0 = data; + return; + case 3 : + this.lba1 = data; + return; + case 4 : + this.lba2 = data; + } + } + + @Override + public int read(int address) throws MemoryAccessException { + switch(address) { + case 0: + return readData(); + case 1: + return readStatus(); + 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 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; + } + + int data = readBuffer[readPosition++]; + + if(readPosition >= SECTOR_SIZE) { + this.status = Status.IDLE; + } + + return data; + } + + private void writeData(int data) { + if(status != Status.WRITE) { + return; + } + + writeBuffer[writePosition++] = (byte) data; + + if(writePosition >= SECTOR_SIZE) { + if(sdImageFile != null) { + try { + 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); + } + } + + this.status = Status.IDLE; + } + + } + + private int readStatus() { + switch(this.status) { + case IDLE: + return 128; + case READ: + return 224; + case WRITE: + return 160; + default: + return 0; + } + } + + private void writeCommand(int data) { + this.command = data; + switch(this.command) { + case 0 : + prepareRead(); + return; + case 1 : + prepareWrite(); + return; + default: + this.status = Status.IDLE; + } + } + + @Override + public String toString() { + return getName() + "@" + String.format("%04X", this.getMemoryRange().startAddress); + } + +} diff --git a/src/main/java/com/loomcom/symon/machines/MulticompMachine.java b/src/main/java/com/loomcom/symon/machines/MulticompMachine.java index 0ff12f1..3af98db 100644 --- a/src/main/java/com/loomcom/symon/machines/MulticompMachine.java +++ b/src/main/java/com/loomcom/symon/machines/MulticompMachine.java @@ -31,6 +31,7 @@ import com.loomcom.symon.devices.Acia6850; import com.loomcom.symon.devices.Crtc; import com.loomcom.symon.devices.Memory; import com.loomcom.symon.devices.Pia; +import com.loomcom.symon.devices.SdController; import com.loomcom.symon.exceptions.MemoryRangeException; import java.io.File; import java.util.logging.Logger; @@ -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"); 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)); } }