From 7b5af28f5d50e4070e4a4d21688f5cb1252b59bc Mon Sep 17 00:00:00 2001 From: Adrian Conlon Date: Sat, 29 Jun 2019 11:35:08 +0100 Subject: [PATCH] Refactor the Intel hex file parser. The new parser is isolated and memory efficient. Signed-off-by: Adrian Conlon --- EightBit/Bus.cs | 79 ++++---------------------------- EightBit/EightBit.csproj | 1 + EightBit/IntelHexFile.cs | 99 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 70 deletions(-) create mode 100644 EightBit/IntelHexFile.cs diff --git a/EightBit/Bus.cs b/EightBit/Bus.cs index 699be5b..ca877fe 100644 --- a/EightBit/Bus.cs +++ b/EightBit/Bus.cs @@ -103,69 +103,6 @@ namespace EightBit public abstract void Initialize(); - protected static Dictionary> ParseHexFile(string path) - { - var returned = new Dictionary>(); - - using (var reader = File.OpenText(path)) - { - var eof = false; - while (!reader.EndOfStream && !eof) - { - var line = reader.ReadLine(); - - var colon = line.Substring(0, 1); - if (colon != ":") - { - throw new System.InvalidOperationException("Invalid hex file: line does not begin with a colon"); - } - - var countString = line.Substring(1, 2); - var count = Convert.ToByte(countString, 16); - - var addressString = line.Substring(3, 4); - var address = Convert.ToUInt16(addressString, 16); - - var recordTypeString = line.Substring(7, 2); - var recordType = Convert.ToByte(recordTypeString, 16); - - switch (recordType) - { - case 0x00: - { - var data = new List(count); - var requiredLength = 9 + 2 + (count * 2); - if (line.Length != requiredLength) - { - throw new InvalidOperationException("Invalid hex file: line is not the required length"); - } - - for (var i = 0; i < count; ++i) - { - var position = 9 + (i * 2); - var datumString = line.Substring(position, 2); - var datum = Convert.ToByte(datumString, 16); - data.Add(datum); - } - - returned[address] = data; - } - - break; - - case 0x01: - eof = true; - break; - - default: - throw new InvalidOperationException("Unhandled hex file record."); - } - } - } - - return returned; - } - protected virtual void OnWritingByte() => this.WritingByte?.Invoke(this, EventArgs.Empty); protected virtual void OnWrittenByte() => this.WrittenByte?.Invoke(this, EventArgs.Empty); @@ -195,14 +132,16 @@ namespace EightBit protected void LoadHexFile(string path) { - var chunks = ParseHexFile(path); - foreach (var chunk in chunks) + using (var file = new IntelHexFile(path)) { - var address = chunk.Key; - var content = chunk.Value; - var mapped = this.Mapping(address); - var offset = address - mapped.Begin; - mapped.Memory.Load(content.ToArray(), offset); + foreach (var chunk in file.Parse()) + { + var address = chunk.Item1; + var content = chunk.Item2; + var mapped = this.Mapping(address); + var offset = address - mapped.Begin; + mapped.Memory.Load(content.ToArray(), offset); + } } } } diff --git a/EightBit/EightBit.csproj b/EightBit/EightBit.csproj index 3063d48..34b4db8 100644 --- a/EightBit/EightBit.csproj +++ b/EightBit/EightBit.csproj @@ -51,6 +51,7 @@ + diff --git a/EightBit/IntelHexFile.cs b/EightBit/IntelHexFile.cs new file mode 100644 index 0000000..104a516 --- /dev/null +++ b/EightBit/IntelHexFile.cs @@ -0,0 +1,99 @@ +// +// Copyright (c) Adrian Conlon. All rights reserved. +// + +namespace EightBit +{ + using System; + using System.Collections.Generic; + using System.IO; + + public class IntelHexFile : IDisposable + { + private readonly StreamReader reader; + private bool disposed = false; + + public IntelHexFile(string path) => this.reader = File.OpenText(path); + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!this.disposed) + { + if (disposing) + { + this.reader.Dispose(); + } + + this.disposed = true; + } + } + + public IEnumerable>> Parse() + { + var eof = false; + while (!this.reader.EndOfStream && !eof) + { + var line = this.reader.ReadLine(); + var parsed = this.Parse(line); + eof = parsed == null; + if (!eof) + { + yield return parsed; + } + } + } + + private Tuple> Parse(string line) + { + var colon = line.Substring(0, 1); + if (colon != ":") + { + throw new System.InvalidOperationException("Invalid hex file: line does not begin with a colon"); + } + + var countString = line.Substring(1, 2); + var count = Convert.ToByte(countString, 16); + + var addressString = line.Substring(3, 4); + var address = Convert.ToUInt16(addressString, 16); + + var recordTypeString = line.Substring(7, 2); + var recordType = Convert.ToByte(recordTypeString, 16); + + switch (recordType) + { + case 0x00: + { + var data = new List(count); + var requiredLength = 9 + 2 + (count * 2); + if (line.Length != requiredLength) + { + throw new InvalidOperationException("Invalid hex file: line is not the required length"); + } + + for (var i = 0; i < count; ++i) + { + var position = 9 + (i * 2); + var datumString = line.Substring(position, 2); + var datum = Convert.ToByte(datumString, 16); + data.Add(datum); + } + + return new Tuple>(address, data); + } + + case 0x01: + return null; + + default: + throw new InvalidOperationException("Unhandled hex file record."); + } + } + } +}