Refactor the Intel hex file loader into it's own class.

Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon 2019-07-06 22:44:42 +01:00
parent ef09696ea2
commit 36465ce1c8
6 changed files with 119 additions and 61 deletions

View File

@ -2,8 +2,6 @@
#include <cstdint>
#include <string>
#include <map>
#include <vector>
#include "Chip.h"
#include "Signal.h"
@ -58,7 +56,6 @@ namespace EightBit {
[[nodiscard]] auto& reference(const register16_t address) { return reference(address.word); }
[[nodiscard]] uint8_t& reference() { return reference(ADDRESS()); }
[[nodiscard]] static std::map<uint16_t, std::vector<uint8_t>> parseHexFile(std::string path);
void loadHexFile(std::string path);
private:

32
inc/IntelHexFile.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
#include <fstream>
#include <string>
#include <map>
#include <optional>
#include <vector>
#include <utility>
namespace EightBit {
class IntelHexFile {
public:
IntelHexFile(std::string path);
[[nodiscard]] std::map<uint16_t, std::vector<uint8_t>> parse();
private:
[[nodiscard]] std::optional<std::pair<uint16_t, std::vector<uint8_t>>> parse(std::string line);
[[nodiscard]] std::vector<uint8_t> parseDataRecord(std::string line, uint8_t count);
template <class T> T fromHex(std::string input) {
std::istringstream converter(input);
unsigned output;
converter >> std::hex >> output;
return static_cast<T>(output);
}
std::ifstream m_file;
bool m_eof;
};
}

View File

@ -1,13 +1,9 @@
#include "stdafx.h"
#include "Bus.h"
#include "Ram.h"
#include "IntelHexFile.h"
#include "EightBitCompilerDefinitions.h"
#include <fstream>
#include <cstdlib>
#include <stdexcept>
#include <cassert>
void EightBit::Bus::raisePOWER() {}
void EightBit::Bus::lowerPOWER() {}
@ -31,7 +27,8 @@ void EightBit::Bus::write(const uint8_t value) {
}
void EightBit::Bus::loadHexFile(const std::string path) {
const auto chunks = parseHexFile(path);
IntelHexFile file(path);
const auto chunks = file.parse();
for (const auto& chunk : chunks) {
const auto address = chunk.first;
const auto content = chunk.second;
@ -41,57 +38,6 @@ void EightBit::Bus::loadHexFile(const std::string path) {
}
}
std::map<uint16_t, std::vector<uint8_t>> EightBit::Bus::parseHexFile(const std::string path) {
std::ifstream file;
file.open(path);
std::map<uint16_t, std::vector<uint8_t>> returned;
bool eof = false;
while (!file.eof() && !eof) {
std::string line;
std::getline(file, line);
const auto colon = line.substr(0, 1);
if (colon != ":")
throw std::out_of_range("Invalid hex file: line does not begin with a colon");
const auto countString = line.substr(1, 2);
const auto count = (uint8_t)strtoul(countString.c_str(), nullptr, 16);
const auto addressString = line.substr(3, 4);
const auto address = (uint16_t)strtoul(addressString.c_str(), nullptr, 16);
const auto recordTypeString = line.substr(7, 2);
const auto recordType = strtoul(recordTypeString.c_str(), nullptr, 16);
switch (recordType) {
case 0x00: {
std::vector<uint8_t> data(count);
const auto requiredLength = 9 + 2 + (count * 2);
if (line.length() != requiredLength)
throw std::out_of_range("Invalid hex file: line is not the required length");
for (int i = 0; i < count; ++i) {
const auto position = 9 + i * 2;
const auto datumString = line.substr(position, 2);
const auto datum = (uint8_t)strtoul(datumString.c_str(), nullptr, 16);
data[i] = datum;
}
returned[address] = data;
}
break;
case 0x01:
eof = true;
break;
default:
throw std::out_of_range("Unhandled hex file record.");
}
}
return returned;
}
uint8_t& EightBit::Bus::reference(const uint16_t address) {
const auto mapped = mapping(address);
const uint16_t offset = (address - mapped.begin) & mapped.mask;

View File

@ -23,7 +23,7 @@
<ProjectGuid>{A9C24BD9-0CB4-4C84-B09B-46B815F9DA47}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>EightBit</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
@ -72,15 +72,19 @@
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>..\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>..\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>..\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>..\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)</IncludePath>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -120,6 +124,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
<OmitFramePointers>true</OmitFramePointers>
<ControlFlowGuard>false</ControlFlowGuard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -139,6 +144,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
<OmitFramePointers>true</OmitFramePointers>
<ControlFlowGuard>false</ControlFlowGuard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -155,6 +161,7 @@
<ClInclude Include="..\inc\EightBitCompilerDefinitions.h" />
<ClInclude Include="..\inc\EventArgs.h" />
<ClInclude Include="..\inc\InputOutput.h" />
<ClInclude Include="..\inc\IntelHexFile.h" />
<ClInclude Include="..\inc\IntelProcessor.h" />
<ClInclude Include="..\inc\LittleEndianProcessor.h" />
<ClInclude Include="..\inc\Mapper.h" />
@ -176,6 +183,7 @@
<ClCompile Include="Device.cpp" />
<ClCompile Include="EventArgs.cpp" />
<ClCompile Include="InputOutput.cpp" />
<ClCompile Include="IntelHexFile.cpp" />
<ClCompile Include="IntelProcessor.cpp" />
<ClCompile Include="LittleEndianProcessor.cpp" />
<ClCompile Include="Memory.cpp" />

View File

@ -74,6 +74,9 @@
<ClInclude Include="..\inc\ClockedChip.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\inc\IntelHexFile.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -118,5 +121,8 @@
<ClCompile Include="Device.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IntelHexFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

69
src/IntelHexFile.cpp Normal file
View File

@ -0,0 +1,69 @@
#include "stdafx.h"
#include "IntelHexFile.h"
#include <sstream>
EightBit::IntelHexFile::IntelHexFile(std::string path)
: m_eof(false) {
m_file.open(path);
}
std::map<uint16_t, std::vector<uint8_t>> EightBit::IntelHexFile::parse() {
std::map<uint16_t, std::vector<uint8_t>> returned;
while (!m_file.eof() && !m_eof) {
std::string line;
std::getline(m_file, line);
const auto parsed = parse(line);
if (parsed.has_value())
returned[parsed.value().first] = parsed.value().second;
}
if (!m_eof)
throw std::out_of_range("File is missing an EOF record");
return returned;
}
std::optional<std::pair<uint16_t, std::vector<uint8_t>>> EightBit::IntelHexFile::parse(std::string line) {
const auto colon = line.substr(0, 1);
if (colon != ":")
throw std::out_of_range("Invalid hex file: line does not begin with a colon");
const auto countString = line.substr(1, 2);
const auto count = fromHex<uint8_t>(countString);
const auto addressString = line.substr(3, 4);
const auto address = fromHex<uint16_t>(addressString);
const auto recordTypeString = line.substr(7, 2);
const auto recordType = fromHex<uint8_t>(recordTypeString);
switch (recordType) {
case 0x00:
return std::make_pair(address, parseDataRecord(line, count));
case 0x01:
m_eof = true;
return {};
default:
throw std::out_of_range("Unhandled hex file record.");
}
}
std::vector<uint8_t> EightBit::IntelHexFile::parseDataRecord(std::string line, uint8_t count) {
std::vector<uint8_t> data(count);
const auto requiredLength = 9 + 2 + (count * 2);
if (line.length() != requiredLength)
throw std::out_of_range("Invalid hex file: line is not the required length");
for (int i = 0; i < count; ++i) {
const auto position = 9 + i * 2;
const auto datumString = line.substr(position, 2);
const auto datum = fromHex<uint8_t>(datumString);
data.at(i) = datum;
}
return data;
}