mirror of
https://github.com/sethm/symon.git
synced 2024-12-29 02:31:59 +00:00
Merge pull request #10 from maikmerten/master
Multicomp: SD card controller emulation
This commit is contained in:
commit
af12039686
@ -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
|
||||
|
||||
|
212
src/main/java/com/loomcom/symon/devices/SdController.java
Normal file
212
src/main/java/com/loomcom/symon/devices/SdController.java
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2013 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.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.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);
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
|
@ -35,8 +35,8 @@ import java.awt.*;
|
||||
*/
|
||||
public class TraceLog extends JFrame {
|
||||
|
||||
private FifoRingBuffer<Cpu.CpuState> traceLog;
|
||||
private JTextArea traceLogTextArea;
|
||||
private final FifoRingBuffer<Cpu.CpuState> 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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user