mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2026-10-22 17:02:18 +00:00
Add more checks/logging. Also make checks optional in a "strict" mode, to allow problematic files to still load, if possible.
This commit is contained in:
1 parent
486014e48d
commit
c745af2dfa
2 files changed
+64
-32
No files matched your search
@@ -88,6 +88,8 @@ namespace EightBit
|
||||
protected void LoadHexFile(string path)
|
||||
{
|
||||
var file = new IntelHexFile(path);
|
||||
//file.Debug = true;
|
||||
//file.Strict = true;
|
||||
foreach (var (address, content) in file.Parse())
|
||||
{
|
||||
if (file.ExtendedSegmentAddress != 0)
|
||||
|
||||
+62
-32
@@ -14,6 +14,10 @@ namespace EightBit
|
||||
private ushort _extendedLinearAddressHigh;
|
||||
private uint _startLinearAddress;
|
||||
|
||||
public bool Debug { get; set; }
|
||||
|
||||
public bool Strict { get; set; }
|
||||
|
||||
private bool EOF => this._eof;
|
||||
|
||||
public ushort ExtendedSegmentAddress => this._extendedSegmentAddress;
|
||||
@@ -26,24 +30,43 @@ namespace EightBit
|
||||
|
||||
public uint StartLinearAddress => this._startLinearAddress;
|
||||
|
||||
private void Log(string message)
|
||||
{
|
||||
if (this.Debug)
|
||||
Console.WriteLine($"Intel Hex File: {message}");
|
||||
}
|
||||
|
||||
private void MaybeThrow(string message)
|
||||
{
|
||||
if (this.Strict)
|
||||
throw new InvalidDataException(message);
|
||||
Log(message);
|
||||
}
|
||||
|
||||
private bool Check(bool failure, string message)
|
||||
{
|
||||
if (failure)
|
||||
this.MaybeThrow(message);
|
||||
return failure;
|
||||
}
|
||||
|
||||
public IEnumerable<Tuple<ushort, byte[]>> Parse()
|
||||
{
|
||||
this._eof = false;
|
||||
|
||||
this.Log($"Opening: {this._path}");
|
||||
using var reader = File.OpenText(this._path);
|
||||
|
||||
this.Log("Reading");
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine() ?? throw new InvalidDataException("Early EOF detected");
|
||||
var parsed = this.Parse(line);
|
||||
if (parsed is not null)
|
||||
{
|
||||
yield return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.EOF)
|
||||
{
|
||||
throw new InvalidDataException("File is missing an EOF record");
|
||||
}
|
||||
this.Check(!this.EOF, "File is missing an EOF record");
|
||||
this.Log("Finished");
|
||||
}
|
||||
|
||||
private static byte CalculateChecksum(byte[] data)
|
||||
@@ -54,28 +77,27 @@ namespace EightBit
|
||||
return (byte)-Chip.LowByte(sum);
|
||||
}
|
||||
|
||||
private static void VerifyChecksum(byte desired, byte[] data)
|
||||
private bool VerifyChecksum(byte desired, byte[] data)
|
||||
{
|
||||
var calculated = CalculateChecksum(data);
|
||||
if (calculated != desired)
|
||||
throw new InvalidDataException("Checksum failure");
|
||||
return !this.Check(calculated != desired, "Checksum failure"); ;
|
||||
}
|
||||
|
||||
private Tuple<ushort, byte[]>? Parse(string line)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNullOrEmpty(line);
|
||||
|
||||
if (this._eof)
|
||||
throw new InvalidDataException("Reading past EOF marker");
|
||||
if (this.Check(this.EOF, "Reading past EOF marker"))
|
||||
return null;
|
||||
|
||||
var colon = line[..1];
|
||||
if (colon != ":")
|
||||
throw new InvalidDataException("Record does not begin with a colon");
|
||||
if (this.Check(colon != ":", "Record does not begin with a colon"))
|
||||
return null;
|
||||
|
||||
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");
|
||||
if (this.Check(characterCount % 2 != 0, "Record data cannot be parsed as two character hex numbers"))
|
||||
return null;
|
||||
|
||||
var byteCount = characterCount / 2;
|
||||
var bytes = new byte[byteCount];
|
||||
@@ -87,8 +109,8 @@ namespace EightBit
|
||||
bytes[index] = datum;
|
||||
}
|
||||
|
||||
if (byteCount < 5)
|
||||
throw new InvalidDataException("Not enough bytes in record");
|
||||
if (this.Check(byteCount < 5, "Not enough bytes in record"))
|
||||
return null;
|
||||
|
||||
var count = bytes[0];
|
||||
var address = Chip.MakeShort(bytes[2], bytes[1]);
|
||||
@@ -96,59 +118,67 @@ namespace EightBit
|
||||
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");
|
||||
if (this.Check(count != data.Length, "Record count does not match data length"))
|
||||
return null;
|
||||
|
||||
VerifyChecksum(checksum, bytes[..lastIndex]); // All data, except the desired checksum (obviously!)
|
||||
// All data, except the desired checksum (obviously!)
|
||||
if (!VerifyChecksum(checksum, bytes[..lastIndex]))
|
||||
return null;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
// Data
|
||||
case 0x00:
|
||||
this.Log($"Type: {type:x2}, Address: {address:x4}, Count: {count}");
|
||||
return new Tuple<ushort, byte[]>(address, data);
|
||||
|
||||
// End-of-file
|
||||
case 0x01:
|
||||
if (this.EOF)
|
||||
throw new InvalidDataException("HEX file contains multiple EOF markers");
|
||||
this.Check(this.EOF, "HEX file contains multiple EOF markers");
|
||||
this._eof = true;
|
||||
this.Log($"Type: {type:x2}, EOF");
|
||||
return null;
|
||||
|
||||
// Extended segment address
|
||||
case 0x02:
|
||||
if (count != 2)
|
||||
throw new InvalidDataException("Byte count is invalid for field type 2 (Extended Segment Address)");
|
||||
if (this.Check(count != 2, "Byte count is invalid for field type 2 (Extended Segment Address)"))
|
||||
return null;
|
||||
this._extendedSegmentAddress = Chip.MakeShort(data[1], bytes[0]);
|
||||
this.Log($"Type: {type:x2}, Extended segment address: {this.ExtendedSegmentAddress:x4}");
|
||||
return null;
|
||||
|
||||
// Start segment address
|
||||
case 0x03:
|
||||
if (count != 4)
|
||||
throw new InvalidDataException("Byte count is invalid for field type 3 (Start Segment Address)");
|
||||
if (this.Check(count != 4, "Byte count is invalid for field type 3 (Start Segment Address)"))
|
||||
return null;
|
||||
this._codeSegment = Chip.MakeShort(data[1], bytes[0]);
|
||||
this._programCounter = Chip.MakeShort(data[2], bytes[3]);
|
||||
this.Log($"Type: {type:x2}, CS: {this.CodeSegment:x4}, PC: {this.ProgramCounter:x4}");
|
||||
return null;
|
||||
|
||||
// Extended linear address
|
||||
case 0x04:
|
||||
if (count != 2)
|
||||
throw new InvalidDataException("Byte count is invalid for field type 4 (Extended Linear Address)");
|
||||
if (this.Check(count != 2, "Byte count is invalid for field type 4 (Extended Linear Address)"))
|
||||
return null;
|
||||
this._extendedLinearAddressHigh = Chip.MakeShort(data[1], bytes[0]);
|
||||
this.Log($"Type: {type:x2}, Extended linear address high: {this.ExtendedLinearAddressHigh:x4}");
|
||||
return null;
|
||||
|
||||
// Start linear address
|
||||
case 0x05:
|
||||
if (count != 4)
|
||||
throw new InvalidDataException("Byte count is invalid for field type 5 (Start Linear Address)");
|
||||
if (this.Check(count != 4, "Byte count is invalid for field type 5 (Start Linear Address)"))
|
||||
return null;
|
||||
{
|
||||
var high = Chip.MakeShort(data[1], bytes[0]);
|
||||
var low = Chip.MakeShort(data[2], bytes[3]);
|
||||
this._startLinearAddress = Chip.MakeInteger(low, high);
|
||||
}
|
||||
this.Log($"Type: {type:x2}, Start linear address: {this.StartLinearAddress:x8}");
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Unhandled HEX file record.");
|
||||
this.MaybeThrow("Unhandled HEX file record.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user