mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2026-10-23 23:02:16 +00:00
Improve Intel HEX file parsing. Should be OK, according to wikipedia, anyway!
This commit is contained in:
1 parent
b2be613cc3
commit
2fd16b84bb
2 files changed
+108
-27
No files matched your search
@@ -90,6 +90,16 @@ namespace EightBit
|
||||
var file = new IntelHexFile(path);
|
||||
foreach (var (address, content) in file.Parse())
|
||||
{
|
||||
if (file.ExtendedSegmentAddress != 0)
|
||||
throw new InvalidDataException("ExtendedSegmentAddress must be zero");
|
||||
if (file.CodeSegment != 0)
|
||||
throw new InvalidDataException("CodeSegment must be zero");
|
||||
if (file.ProgramCounter != 0)
|
||||
throw new InvalidDataException("ProgramCounter must be zero");
|
||||
if (file.ExtendedLinearAddressHigh != 0)
|
||||
throw new InvalidDataException("ExtendedLinearAddressHigh must be zero");
|
||||
if (file.StartLinearAddress != 0)
|
||||
throw new InvalidDataException("StartLinearAddress must be zero");
|
||||
var mapped = this.Mapping(address);
|
||||
var offset = address - mapped.Begin;
|
||||
mapped.Memory.Load(content, offset);
|
||||
|
||||
+98
-27
@@ -8,14 +8,31 @@ namespace EightBit
|
||||
{
|
||||
private readonly string _path = path;
|
||||
private bool _eof;
|
||||
private ushort _extendedSegmentAddress;
|
||||
private ushort _codeSegment;
|
||||
private ushort _programCounter;
|
||||
private ushort _extendedLinearAddressHigh;
|
||||
private uint _startLinearAddress;
|
||||
|
||||
public bool EOF => this._eof;
|
||||
|
||||
public ushort ExtendedSegmentAddress => this._extendedSegmentAddress;
|
||||
|
||||
public ushort CodeSegment => this._codeSegment;
|
||||
|
||||
public ushort ProgramCounter => this._programCounter;
|
||||
|
||||
public ushort ExtendedLinearAddressHigh => this._extendedLinearAddressHigh;
|
||||
|
||||
public uint StartLinearAddress => this._startLinearAddress;
|
||||
|
||||
public IEnumerable<Tuple<ushort, byte[]>> Parse()
|
||||
{
|
||||
this._eof = false;
|
||||
using var reader = File.OpenText(this._path);
|
||||
while (!reader.EndOfStream && !this._eof)
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine() ?? throw new InvalidOperationException("Early EOF detected");
|
||||
var line = reader.ReadLine() ?? throw new InvalidDataException("Early EOF detected");
|
||||
var parsed = this.Parse(line);
|
||||
if (parsed is not null)
|
||||
{
|
||||
@@ -25,50 +42,104 @@ namespace EightBit
|
||||
|
||||
if (!this._eof)
|
||||
{
|
||||
throw new InvalidOperationException("File is missing an EOF record");
|
||||
throw new InvalidDataException("File is missing an EOF record");
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] ParseDataRecord(string line, byte count)
|
||||
private static void VerifyChecksum(byte desired, byte[] data)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNullOrEmpty(line);
|
||||
|
||||
var requiredLength = 9 + 2 + (count * 2);
|
||||
ArgumentOutOfRangeException.ThrowIfNotEqual(line.Length, requiredLength, nameof(line));
|
||||
var data = new byte[count];
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
var position = 9 + (i * 2);
|
||||
var extracted = line.Substring(position, 2);
|
||||
data[i] = Convert.ToByte(extracted, 16);
|
||||
}
|
||||
|
||||
return data;
|
||||
ushort sum = 0;
|
||||
foreach (var datum in data)
|
||||
sum += datum;
|
||||
var calculated = (byte)-Chip.LowByte(sum);
|
||||
if (calculated != desired)
|
||||
throw new InvalidDataException("Checksum failure");
|
||||
}
|
||||
|
||||
private Tuple<ushort, byte[]>? Parse(string line)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNullOrEmpty(line);
|
||||
|
||||
if (this._eof)
|
||||
throw new InvalidDataException("Reading past EOF marker");
|
||||
|
||||
var colon = line[..1];
|
||||
ArgumentOutOfRangeException.ThrowIfNotEqual(colon, ":", nameof(line));
|
||||
if (colon != ":")
|
||||
throw new InvalidDataException("Record does not begin with a colon");
|
||||
|
||||
var countString = line.Substring(1, 2);
|
||||
var count = Convert.ToByte(countString, 16);
|
||||
var useful = line[1..];
|
||||
var characterCount = useful.Length;
|
||||
if (characterCount % 2 != 0)
|
||||
throw new InvalidDataException("Record data cannot be parsed as two character hex numbers");
|
||||
|
||||
var addressString = line.Substring(3, 4);
|
||||
var address = Convert.ToUInt16(addressString, 16);
|
||||
|
||||
var recordTypeString = line.Substring(7, 2);
|
||||
switch (Convert.ToByte(recordTypeString, 16))
|
||||
var byteCount = characterCount / 2;
|
||||
var bytes = new byte[byteCount];
|
||||
for (int index = 0; index < byteCount; ++index)
|
||||
{
|
||||
case 0x00:
|
||||
return new Tuple<ushort, byte[]>(address, ParseDataRecord(line, count));
|
||||
var position = index * 2;
|
||||
var extracted = useful.Substring(position, 2);
|
||||
var datum = Convert.ToByte(extracted, 16);
|
||||
bytes[index] = datum;
|
||||
}
|
||||
|
||||
if (byteCount < 5)
|
||||
throw new InvalidDataException("Not enough bytes in record");
|
||||
|
||||
var count = bytes[0];
|
||||
var address = Chip.MakeShort(bytes[2], bytes[1]);
|
||||
var type = bytes[3];
|
||||
var lastIndex = byteCount - 1;
|
||||
var checksum = bytes[lastIndex];
|
||||
var data = bytes[4..lastIndex];
|
||||
if (count != data.Length)
|
||||
throw new InvalidDataException("Record count does not match data length");
|
||||
|
||||
VerifyChecksum(checksum, bytes[..lastIndex]);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
// Data
|
||||
case 0x00:
|
||||
return new Tuple<ushort, byte[]>(address, data);
|
||||
|
||||
// End-of-file
|
||||
case 0x01:
|
||||
this._eof = true;
|
||||
return null;
|
||||
|
||||
// Extended segment address
|
||||
case 0x02:
|
||||
if (count != 2)
|
||||
throw new InvalidDataException("Byte count is invalid for field type 2 (Extended Segment Address)");
|
||||
this._extendedSegmentAddress = Chip.MakeShort(data[1], bytes[0]);
|
||||
return null;
|
||||
|
||||
// Start segment address
|
||||
case 0x03:
|
||||
if (count != 4)
|
||||
throw new InvalidDataException("Byte count is invalid for field type 3 (Start Segment Address)");
|
||||
this._codeSegment = Chip.MakeShort(data[1], bytes[0]);
|
||||
this._programCounter = Chip.MakeShort(data[2], bytes[3]);
|
||||
return null;
|
||||
|
||||
// Extended linear address
|
||||
case 0x04:
|
||||
if (count != 2)
|
||||
throw new InvalidDataException("Byte count is invalid for field type 4 (Extended Linear Address)");
|
||||
this._extendedLinearAddressHigh = Chip.MakeShort(data[1], bytes[0]);
|
||||
return null;
|
||||
|
||||
// Start linear address
|
||||
case 0x05:
|
||||
if (count != 4)
|
||||
throw new InvalidDataException("Byte count is invalid for field type 5 (Start Linear Address)");
|
||||
{
|
||||
var high = Chip.MakeShort(data[1], bytes[0]);
|
||||
var low = Chip.MakeShort(data[2], bytes[3]);
|
||||
this._startLinearAddress = Chip.MakeInteger(low, high);
|
||||
}
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Unhandled hex file record.");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user