blob: 23a792be24937257e54c7f265ec73f3afaad9bb0 [file] [log] [blame] [raw]
/*
* Copyright (c) 2016 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 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;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
/**
* 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 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, CONTROLLER_SIZE, "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) {
switch (data) {
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);
}
}