-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhex8asm.cpp
241 lines (204 loc) · 8.66 KB
/
hex8asm.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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// Copyright (c) 2020 Tom Deakin
// SPDX-License-Identifier: MIT
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <cassert>
#include "hex8asm.hpp"
// Output file name
static constexpr char outputFile[] = "a.hex";
// Main routine
int main(int argc, char *argv[]) {
// Check number of arguments
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " prog.hex8" << std::endl;
exit(EXIT_FAILURE);
}
// Load input file from command line argument
std::ifstream progSource(argv[1]);
if (!progSource.is_open()) {
std::cerr << "Could not open file: " << argv[1] << std::endl;
exit(EXIT_FAILURE);
}
// Load output file, using fixed name
std::ofstream hexOutput(outputFile);
if (!hexOutput.is_open()) {
std::cerr << "Could not open output file " << outputFile << std::endl;
exit(EXIT_FAILURE);
}
// Set up Hex 8 ISA
ISA hex8;
// Create table of labels
assemblyLabels labels;
// Create hex output stream
hexOutputStream outputStream;
// First pass
// Loop over the file and translate each instruction
// When labels are defined, save them along with the output line number
// Long backwards jumps stay correctly referenced because we (non-optimally)
// always output a PFIX instruction before each intruction which uses a label.
// Any instructions which forward reference a label need to be processed on a second pass.
// If there are any invalid instructions, we stop processing
{
std::string line;
int srcLineNum = 0;
int outLineNum = 0;
std::vector<std::string> labelDeclarations;
while (std::getline(progSource, line)) {
// Skip blank lines
if (!line.size()) {
srcLineNum++;
continue;
}
// Loop over line and construct a list (vector) of words on the line
std::stringstream ss(line);
std::vector<std::string> tokens;
for (std::string t; ss >> t;) {
// Convert to lower case
std::transform(t.begin(), t.end(), t.begin(), ::tolower);
// Add to list of tokens
tokens.push_back(t);
}
// Check only for labels
if (hex8.validLabel(tokens[0])) {
// Add to label list
labelDeclarations.push_back(tokens[0]);
} else if (hex8.validData(tokens[0])) {
// Process the data instructions
// Found a line of the form DATA xxx
// Now found a non-label, so know target of labels is this current output line
labels.setLabels(labelDeclarations, outLineNum);
// Empty the list of labels to process
labelDeclarations.clear();
assert(outLineNum == outputStream.getOutputLength());
// Check that it has an operand.
if (tokens.size() != 2) {
std::cerr << "Error: Illformed DATA (line " << srcLineNum+1 << ") - " << line << std::endl;
exit(EXIT_FAILURE);
}
// Convert operand to integer
int data = std::stoi(tokens[1]);
// Data instructions are just 8-bit values, so we can just output them
class line output;
output.data = (data & 0xFF);
output.isData = true;
outputStream.emitInstruction(output);
outLineNum++;
} else if (hex8.validInstruction(tokens[0])) {
// Process a regular instruction
auto inst = hex8.getInstruction(tokens[0]);
// Now found a non-label, so know target of labels is the next output line
labels.setLabels(labelDeclarations, outLineNum);
// Empty the list of labels to process
labelDeclarations.clear();
if (inst.type != operandType::none) {
// Instruction needs an operand, so check it has one
if (tokens.size() != 2) {
std::cerr << "Error - instruction missing operand (line " << srcLineNum+1 << ") - " << line << std::endl;
exit(EXIT_FAILURE);
}
// If operand is a label, we emit a prefix instruction just in case we need all 8-bits
// Note this is not that optimal, but an iterative pass could remove redundant ones
if (hex8.validLabel(tokens[1])) {
class line prefix;
prefix.opcode = "pfix";
prefix.requiresLabelResolution = true;
prefix.label = tokens[1];
outputStream.emitInstruction(prefix);
outLineNum++;
// Then emit this instruction
class line output;
output.opcode = tokens[0];
output.requiresLabelResolution = true;
output.label = tokens[1];
outputStream.emitInstruction(output);
outLineNum++;
assert(outLineNum == outputStream.getOutputLength());
} else {
// Otherwise, operand is just an integer
// Check if we need a prefix, which will be is operand is negative or large
// Convert operand to integer
int operand = std::stoi(tokens[1]);
if (operand < 0 || operand > 15) {
// Emit prefix
class line prefix;
prefix.opcode = "pfix";
prefix.operand = ((operand & 0xFF) >> 0x4); // 4 high bits
outputStream.emitInstruction(prefix);
outLineNum++;
}
// Output the instruction
class line output;
output.opcode = tokens[0];
output.operand = (operand & 0xF); // 4 low bits
outputStream.emitInstruction(output);
outLineNum++;
assert(outLineNum == outputStream.getOutputLength());
}
} else {
// Instruction has no operand
class line output;
output.opcode = tokens[0];
output.operand = 0;
outputStream.emitInstruction(output);
outLineNum++;
assert(outLineNum == outputStream.getOutputLength());
}
}
srcLineNum++;
} // End of line loop
// Print out information about the first pass
std::cout << "--------------------------------------------------------------------------------" << std::endl;
std::cout << "Pass 1 successful" << std::endl;
std::cout << "Lines of source: " << srcLineNum << std::endl;
std::cout << std::endl;
labels.printLabelCount();
labels.printLabels();
std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;
} // End of pass 1
{
// Second pass
// Now we just loop over the output instructions, and resolve any outstanding labels
int outLineNum = 0;
for (auto out = outputStream.outputStream.begin(); out != outputStream.outputStream.end(); ++out) {
auto output = *out;
if (output.requiresLabelResolution) {
// Will be either a prefix or an instruction
if (output.opcode == "pfix") {
// Set the operand to be the 4 high bits of the label
// Label offsets will be from the next line, so +1 to the line number
output.operand = ((labels.resolveLabel(hex8, output.label, outLineNum+1, std::next(out)->opcode) >> 0x4) & 0xF);
} else {
// Set the operand to be the 4 low bits of the label
output.operand = (labels.resolveLabel(hex8, output.label, outLineNum, output.opcode) & 0xF);
}
}
if (output.isData) {
if (output.data == 0) {
hexOutput << "00 ";
std::cout << std::dec << outLineNum << ": DATA 0" << std::endl;
} else if (output.data <= 0xF) {
hexOutput << std::hex << std::uppercase << "0" << (output.data & 0xF) << " ";
std::cout << std::dec << outLineNum << ": DATA " << std::hex << std::uppercase << (output.data & 0xFF) << std::endl;
} else {
hexOutput << std::hex << std::uppercase << (output.data & 0xFF) << " ";
std::cout << std::dec << outLineNum << ": DATA " << std::hex << std::uppercase << (output.data & 0xFF) << std::endl;
}
} else {
hexOutput << std::hex << std::uppercase << (hex8.getInstruction(output.opcode).opcode & 0xF) << (output.operand & 0xF) << " ";
std::cout << std::dec << outLineNum << ": " << output.opcode << " " << std::hex << std::uppercase << (output.operand & 0xF) << std::endl;
}
outLineNum++;
}
// Print out information about the first pass
std::cout << "--------------------------------------------------------------------------------" << std::endl;
std::cout << "Pass 2 successful" << std::endl;
std::cout << "Number of instructions output: " << std::dec << outLineNum << std::endl;
std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;
} // End of pass 2
return EXIT_SUCCESS;
}