forked from benoitryder/megumi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinput_hex.cpp
159 lines (136 loc) · 4.21 KB
/
input_hex.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include <fstream>
#include <iostream>
#include <sstream>
#include <exception>
#include "input_hex.h"
class hex_parsing_error: public std::exception
{
public:
hex_parsing_error(unsigned int lineno, const std::string& msg):
lineno_(lineno), msg_(msg)
{
}
virtual const char* what() const throw()
{
if(what_.empty()) {
std::ostringstream ss;
ss << "line " << lineno_ << ": " << msg_;
what_ = ss.str();
}
return what_.c_str();
}
private:
unsigned int lineno_;
const std::string msg_;
mutable std::string what_;
};
static int parse_hex_digit(char c)
{
if(c >= '0' && c <= '9') return c-'0';
c |= ' ';
if(c >= 'a' && c <= 'f') return (c-'a')+0xa;
return -1;
}
std::vector<uint8_t> parse_hex_file(const std::string& filename)
{
std::ifstream fin(filename);
std::vector<uint8_t> ret;
std::string line;
unsigned int lineno = 0;
uint32_t ex_addr_mask = 0;
bool eof = false;
while(std::getline(fin, line)) {
if(eof) {
throw hex_parsing_error(lineno, "end of file record not in the last line");
}
lineno++;
// strip CR/LF
size_t pos = line.find_first_of("\r\n");
if(pos != std::string::npos) { // should always be true
line.erase(pos);
}
if(line.size() < 11) {
throw hex_parsing_error(lineno, "line is too short");
}
if(line[0] != ':') {
throw hex_parsing_error(lineno, "invalid start code (':' expected)");
}
if((line.size()-1) % 2 != 0) {
throw hex_parsing_error(lineno, "odd number of hex digits");
}
// parse line bytes
std::vector<uint8_t> bytes((line.size()-1)/2);
for(unsigned int i=1; i<line.size(); i+=2) {
int d0 = parse_hex_digit(line[i]);
int d1 = parse_hex_digit(line[i+1]);
if(d0 == -1 || d1 == -1) {
throw hex_parsing_error(lineno, "invalid hex digit");
}
bytes[(i-1)/2] = (d0 << 4) + d1;
}
// check checksum
uint8_t checksum = 0;
for(unsigned int i=0; i<bytes.size(); i++) {
checksum = (checksum + bytes[i]) & 0xff;
}
if(checksum != 0) {
throw hex_parsing_error(lineno, "checksum mismatch");
}
// check byte count
unsigned int byte_count = bytes[0];
if(byte_count != bytes.size()-5) {
throw hex_parsing_error(lineno, "invalid byte count");
}
uint16_t address = (bytes[1] << 8) + bytes[2];
switch(bytes[3]) {
case 0: // data
if(address < ret.size()) {
throw hex_parsing_error(lineno, "address decreased");
}
ret.resize(address, 0xff);
ret.insert(ret.end(), bytes.begin()+4, bytes.begin()+4+byte_count);
break;
case 1: // end of file
if(byte_count != 0) {
throw hex_parsing_error(lineno, "unexpected data in end of file record");
}
eof = true;
break;
case 2: // extended segment address
if(byte_count != 2) {
throw hex_parsing_error(lineno, "invalid byte count for extended segment address record");
}
if(address != 0) {
throw hex_parsing_error(lineno, "address must be 0000 for extended segment address record");
}
if((bytes[5] & 0xf) != 0) {
throw hex_parsing_error(lineno, "extended segment address least-signficant digit must be 0");
}
ex_addr_mask &= ~0x0ffff0;
ex_addr_mask |= (((uint32_t)bytes[4] << 8) + bytes[5]) << 4;
break;
case 3: // start segment address record
// ignored
break;
case 4: // extended linear address
if(byte_count != 2) {
throw hex_parsing_error(lineno, "invalid byte count for extended linear address record");
}
if(address != 0) {
throw hex_parsing_error(lineno, "address must be 0000 for extended linear address record");
}
if((bytes[5] & 0xf) != 0) {
throw hex_parsing_error(lineno, "extended linear address least-signficant digit must be 0");
}
ex_addr_mask &= 0xffff;
ex_addr_mask |= (((uint32_t)bytes[4] << 8) + bytes[5]) << 16;
break;
case 5: // start linear address record
// ignored
break;
default:
throw hex_parsing_error(lineno, "invalid record type");
}
}
return ret;
}