mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-17 08:55:05 -04:00
Added BIN and Intel HEX support for the RAM/ROM content loader. Closes #225
This commit is contained in:
parent
73189a12df
commit
eac5ea91c2
@ -10,6 +10,7 @@ import de.neemann.digital.hdl.hgs.HGSArray;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
@ -58,16 +59,6 @@ public class DataField implements HGSArray {
|
||||
this(Arrays.copyOf(dataField.data, newSize), newSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance and fills it with the data in the given file
|
||||
*
|
||||
* @param file the file containing the data
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
public DataField(File file) throws IOException {
|
||||
this(new InputStreamReader(new FileInputStream(file), "UTF-8"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance and fills it with the data in the given reader
|
||||
*
|
||||
@ -116,7 +107,7 @@ public class DataField implements HGSArray {
|
||||
*/
|
||||
public void saveTo(File file) throws IOException {
|
||||
DataField df = getMinimized();
|
||||
try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"))) {
|
||||
try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
|
||||
w.write("v2.0 raw");
|
||||
w.newLine();
|
||||
for (long l : df.getData()) {
|
||||
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Helmut Neemann.
|
||||
* Use of this source code is governed by the GPL v3 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package de.neemann.digital.core.memory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Helper to import data memory
|
||||
*/
|
||||
public final class DataFieldImporter {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DataFieldImporter.class);
|
||||
|
||||
private DataFieldImporter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a file and converts it into a DataField instance
|
||||
*
|
||||
* @param file the file to read
|
||||
* @param dataBits the data bits of the target memory
|
||||
* @return the DataField instance
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
public static DataField read(File file, int dataBits) throws IOException {
|
||||
String name = file.getName().toLowerCase();
|
||||
if (name.endsWith(".hex")) {
|
||||
try {
|
||||
return new DataField(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
LOGGER.info(file + ": could not read native hex, try intel hex");
|
||||
return readByteArray(file, dataBits, IntelHexReader::new);
|
||||
}
|
||||
} else {
|
||||
LOGGER.info(file + ": read as binary");
|
||||
return readByteArray(file, dataBits, DataFieldImporter::readBinary);
|
||||
}
|
||||
}
|
||||
|
||||
private static DataField readByteArray(File file, int dataBits, Reader reader) throws IOException {
|
||||
DataField dataField = new DataField(0x10000);
|
||||
reader.read(file, create(dataField, dataBits));
|
||||
return dataField.getMinimized();
|
||||
}
|
||||
|
||||
interface DataArray {
|
||||
void put(int addr, int aByte);
|
||||
}
|
||||
|
||||
static DataArray create(DataField dataField, int dataBits) {
|
||||
if (dataBits <= 8)
|
||||
return dataField::setData;
|
||||
return new DataArrayMod(dataField, (dataBits - 1) / 8 + 1);
|
||||
}
|
||||
|
||||
private static final class DataArrayMod implements DataArray {
|
||||
private final DataField dataField;
|
||||
private final int div;
|
||||
|
||||
private DataArrayMod(DataField dataField, int div) {
|
||||
this.dataField = dataField;
|
||||
this.div = div;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(int addr, int aByte) {
|
||||
int a = addr / div;
|
||||
int b = addr % div;
|
||||
|
||||
long val = dataField.getDataWord(a);
|
||||
val = val | ((((long) aByte) & 0xff) << (b * 8));
|
||||
dataField.setData(a, val);
|
||||
}
|
||||
}
|
||||
|
||||
interface Reader {
|
||||
void read(File file, DataArray dataArray) throws IOException;
|
||||
}
|
||||
|
||||
private static void readBinary(File file, DataArray dataArray) throws IOException {
|
||||
try (InputStream in = new FileInputStream(file)) {
|
||||
int d;
|
||||
int addr = 0;
|
||||
while ((d = in.read()) > 0) {
|
||||
dataArray.put(addr, d);
|
||||
addr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
106
src/main/java/de/neemann/digital/core/memory/IntelHexReader.java
Normal file
106
src/main/java/de/neemann/digital/core/memory/IntelHexReader.java
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Helmut Neemann.
|
||||
* Use of this source code is governed by the GPL v3 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package de.neemann.digital.core.memory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Reader for intel hex files
|
||||
*/
|
||||
class IntelHexReader {
|
||||
private final BufferedReader bufferedReader;
|
||||
private final DataFieldImporter.DataArray dataArray;
|
||||
private final int[] data;
|
||||
private int segment = 0;
|
||||
|
||||
/**
|
||||
* Creates a new reader instance
|
||||
*
|
||||
* @param file the file to read
|
||||
* @param dataArray the array to write the data to
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
IntelHexReader(File file, DataFieldImporter.DataArray dataArray) throws IOException {
|
||||
this(new FileInputStream(file), dataArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new reader instance
|
||||
*
|
||||
* @param inputStream the stream to read
|
||||
* @param dataArray the array to write the data to
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
IntelHexReader(InputStream inputStream, DataFieldImporter.DataArray dataArray) throws IOException {
|
||||
this(new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)), dataArray);
|
||||
}
|
||||
|
||||
private IntelHexReader(BufferedReader bufferedReader, DataFieldImporter.DataArray dataArray) throws IOException {
|
||||
this.bufferedReader = bufferedReader;
|
||||
this.dataArray = dataArray;
|
||||
data = new int[300];
|
||||
read();
|
||||
}
|
||||
|
||||
private void read() throws IOException {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
int payload = parseLine(line);
|
||||
switch (data[3]) {
|
||||
case 0:
|
||||
readData(payload);
|
||||
break;
|
||||
case 2:
|
||||
readDataSegment(payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readDataSegment(int len) throws IOException {
|
||||
if (len != 2)
|
||||
throw new IOException("invalid segment address");
|
||||
segment = ((data[4] << 8) + data[5])<<4;
|
||||
}
|
||||
|
||||
private void readData(int len) {
|
||||
int addr = (data[1] << 8) + data[2];
|
||||
for (int i = 0; i < len; i++)
|
||||
dataArray.put(segment + addr + i, data[i + 4]);
|
||||
}
|
||||
|
||||
private int parseLine(String line) throws IOException {
|
||||
if (line.charAt(0) != ':')
|
||||
throw new IOException("not a intel hex file");
|
||||
|
||||
int addr = 0;
|
||||
int p = 1;
|
||||
while (p < line.length()) {
|
||||
data[addr] = Integer.parseInt(line.substring(p, p + 2), 16);
|
||||
addr++;
|
||||
p += 2;
|
||||
}
|
||||
|
||||
int payload = addr - 5;
|
||||
|
||||
if (payload < 0)
|
||||
throw new IOException("not a intel hex file");
|
||||
|
||||
if (data[0] != payload)
|
||||
throw new IOException("invalid record size");
|
||||
|
||||
int sum = 0;
|
||||
for (int i = 0; i < addr; i++)
|
||||
sum += data[i];
|
||||
|
||||
sum = sum & 0xff;
|
||||
if (sum != 0)
|
||||
throw new IOException("wrong checksum in intel hex file: 0x" + Integer.toHexString(sum));
|
||||
|
||||
return payload;
|
||||
}
|
||||
}
|
@ -22,4 +22,8 @@ public interface ProgramMemory {
|
||||
*/
|
||||
void setProgramMemory(DataField dataField);
|
||||
|
||||
/**
|
||||
* @return the data bits
|
||||
*/
|
||||
int getDataBits();
|
||||
}
|
||||
|
@ -24,11 +24,6 @@ public interface RAMInterface extends ProgramMemory {
|
||||
*/
|
||||
int getSize();
|
||||
|
||||
/**
|
||||
* @return the data bits
|
||||
*/
|
||||
int getDataBits();
|
||||
|
||||
/**
|
||||
* @return the addr bits
|
||||
*/
|
||||
|
@ -103,7 +103,7 @@ public class ROM extends Node implements Element, ROMInterface, ProgramMemory {
|
||||
public void init(Model model) throws NodeException {
|
||||
if (autoLoad) {
|
||||
try {
|
||||
data = new DataField(hexFile);
|
||||
data = DataFieldImporter.read(hexFile, dataBits);
|
||||
} catch (IOException e) {
|
||||
throw new NodeException(e.getMessage(), this, -1, null);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ package de.neemann.digital.gui;
|
||||
import de.neemann.digital.core.Model;
|
||||
import de.neemann.digital.core.Node;
|
||||
import de.neemann.digital.core.NodeException;
|
||||
import de.neemann.digital.core.memory.DataField;
|
||||
import de.neemann.digital.core.memory.DataFieldImporter;
|
||||
import de.neemann.digital.core.memory.ProgramMemory;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
|
||||
@ -40,7 +40,8 @@ public class ProgramMemoryLoader implements ModelModifier {
|
||||
throw new NodeException(Lang.get("err_moreThenOneRomFound"));
|
||||
|
||||
try {
|
||||
((ProgramMemory) progMem.get(0)).setProgramMemory(new DataField(romHex));
|
||||
final ProgramMemory memory = (ProgramMemory) progMem.get(0);
|
||||
memory.setProgramMemory(DataFieldImporter.read(romHex, memory.getDataBits()));
|
||||
} catch (IOException e) {
|
||||
throw new NodeException(Lang.get("err_errorLoadingRomData"), e);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import de.neemann.digital.core.Model;
|
||||
import de.neemann.digital.core.ModelEvent;
|
||||
import de.neemann.digital.core.SyncAccess;
|
||||
import de.neemann.digital.core.memory.DataField;
|
||||
import de.neemann.digital.core.memory.DataFieldImporter;
|
||||
import de.neemann.digital.gui.SaveAsHelper;
|
||||
import de.neemann.digital.lang.Lang;
|
||||
import de.neemann.gui.ErrorMessage;
|
||||
@ -133,7 +134,7 @@ public class DataEditor extends JDialog {
|
||||
if (fc.showOpenDialog(DataEditor.this) == JFileChooser.APPROVE_OPTION) {
|
||||
fileName = fc.getSelectedFile();
|
||||
try {
|
||||
localDataField.setDataFrom(new DataField(fc.getSelectedFile()));
|
||||
localDataField.setDataFrom(DataFieldImporter.read(fc.getSelectedFile(), dataBits));
|
||||
dm.fireEvent(new TableModelEvent(dm));
|
||||
} catch (IOException e1) {
|
||||
new ErrorMessage(Lang.get("msg_errorReadingFile")).addCause(e1).show(DataEditor.this);
|
||||
|
@ -14,6 +14,7 @@ import de.neemann.digital.core.extern.Application;
|
||||
import de.neemann.digital.core.extern.PortDefinition;
|
||||
import de.neemann.digital.core.io.InValue;
|
||||
import de.neemann.digital.core.memory.DataField;
|
||||
import de.neemann.digital.core.memory.DataFieldImporter;
|
||||
import de.neemann.digital.core.memory.ROM;
|
||||
import de.neemann.digital.core.memory.rom.ROMManger;
|
||||
import de.neemann.digital.draw.elements.PinException;
|
||||
@ -627,9 +628,13 @@ public final class EditorFactory {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
try {
|
||||
data = new DataField(attr.getFile(ROM.LAST_DATA_FILE_KEY));
|
||||
getAttributeDialog().storeEditedValues();
|
||||
int dataBits = attr.get(Keys.BITS);
|
||||
data = DataFieldImporter.read(attr.getFile(ROM.LAST_DATA_FILE_KEY), dataBits);
|
||||
} catch (IOException e1) {
|
||||
new ErrorMessage(Lang.get("msg_errorReadingFile")).addCause(e1).show(panel);
|
||||
} catch (EditorParseException e1) {
|
||||
new ErrorMessage(Lang.get("msg_invalidEditorValue")).addCause(e1).show(panel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Helmut Neemann
|
||||
* Use of this source code is governed by the GPL v3 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package de.neemann.digital.core.memory;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class DataFieldImporterTest extends TestCase {
|
||||
|
||||
public void testCreate16() {
|
||||
DataField df = new DataField(16);
|
||||
DataFieldImporter.DataArray a = DataFieldImporter.create(df, 16);
|
||||
a.put(0, 0x01);
|
||||
a.put(1, 0x10);
|
||||
a.put(2, 0x77);
|
||||
a.put(3, 0x33);
|
||||
|
||||
assertEquals(0x1001, df.getDataWord(0));
|
||||
assertEquals(0x3377, df.getDataWord(1));
|
||||
}
|
||||
|
||||
public void testCreate24() {
|
||||
DataField df = new DataField(16);
|
||||
DataFieldImporter.DataArray a = DataFieldImporter.create(df, 24);
|
||||
a.put(0, 0x01);
|
||||
a.put(1, 0x10);
|
||||
a.put(2, 0x77);
|
||||
a.put(3, 0x33);
|
||||
|
||||
assertEquals(0x771001, df.getDataWord(0));
|
||||
assertEquals(0x33, df.getDataWord(1));
|
||||
}
|
||||
|
||||
public void testCreate32() {
|
||||
DataField df = new DataField(16);
|
||||
DataFieldImporter.DataArray a = DataFieldImporter.create(df, 32);
|
||||
a.put(0, 0x01);
|
||||
a.put(1, 0x10);
|
||||
a.put(2, 0x77);
|
||||
a.put(3, 0x33);
|
||||
a.put(4, 0x11);
|
||||
|
||||
assertEquals(0x33771001, df.getDataWord(0));
|
||||
assertEquals(0x11, df.getDataWord(1));
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Helmut Neemann
|
||||
* Use of this source code is governed by the GPL v3 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package de.neemann.digital.core.memory;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class IntelHexReaderTest extends TestCase {
|
||||
|
||||
public void testReadHex() throws IOException {
|
||||
String data = ":100000000C9438000C9442000C9442000C94420072\n" +
|
||||
":100010000C9442000C9442000C9442000C94420058\n" +
|
||||
":100020000C9442000C9442000C9442000C94420048\n" +
|
||||
":100030000C9442000C9442000C9442000C94420038\n" +
|
||||
":100040000C9442000C9442000C9442000C94420028\n" +
|
||||
":100050000C9442000C9442000C9442000C94420018\n" +
|
||||
":100060000C9442000C9442000C9442000C94420008\n" +
|
||||
":1000700011241FBECFEFD0E1DEBFCDBF0E944400F0\n" +
|
||||
":100080000C944A000C9400008FEF84B986B18095DF\n" +
|
||||
":0800900085B9FCCFF894FFCF05\n" +
|
||||
":00000001FF";
|
||||
|
||||
int[] bin = new int[200];
|
||||
new IntelHexReader(new ByteArrayInputStream(data.getBytes()), (addr, aByte) -> bin[addr] = aByte);
|
||||
|
||||
assertEquals(0x0c, bin[0x00]);
|
||||
assertEquals(0x11, bin[0x70]);
|
||||
assertEquals(0x24, bin[0x71]);
|
||||
assertEquals(0x00, bin[0x7F]);
|
||||
assertEquals(0x95, bin[0x8F]);
|
||||
assertEquals(0xCF, bin[0x97]);
|
||||
}
|
||||
|
||||
public void testReadSeg() throws IOException {
|
||||
String data = ":020000020110EB\n" +
|
||||
":0800000085B9FCCFF894FFCF95\n";
|
||||
|
||||
int[] bin = new int[0x11010];
|
||||
new IntelHexReader(new ByteArrayInputStream(data.getBytes()), (addr, aByte) -> bin[addr] = aByte);
|
||||
|
||||
assertEquals(0x00, bin[0x00]);
|
||||
assertEquals(0x85, bin[0x1100]);
|
||||
assertEquals(0xb9, bin[0x1101]);
|
||||
}
|
||||
}
|
@ -7,10 +7,7 @@ package de.neemann.digital.integration;
|
||||
|
||||
import de.neemann.digital.core.Model;
|
||||
import de.neemann.digital.core.NodeException;
|
||||
import de.neemann.digital.core.memory.DataField;
|
||||
import de.neemann.digital.core.memory.RAMDualPort;
|
||||
import de.neemann.digital.core.memory.RAMSinglePort;
|
||||
import de.neemann.digital.core.memory.ROM;
|
||||
import de.neemann.digital.core.memory.*;
|
||||
import de.neemann.digital.draw.elements.PinException;
|
||||
import de.neemann.digital.draw.library.ElementNotFoundException;
|
||||
import junit.framework.TestCase;
|
||||
@ -37,7 +34,7 @@ public class TestProcessor extends TestCase {
|
||||
}
|
||||
assertNotNull(rom);
|
||||
|
||||
rom.setData(new DataField(new File(Resources.getRoot(), program)));
|
||||
rom.setData(DataFieldImporter.read(new File(Resources.getRoot(), program), rom.getDataBits()));
|
||||
|
||||
runner.getModel().init(true);
|
||||
return runner;
|
||||
|
Loading…
x
Reference in New Issue
Block a user