-
Notifications
You must be signed in to change notification settings - Fork 5
/
main.cpp
152 lines (130 loc) · 5.58 KB
/
main.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
/*
* Hack Assembler
* Created by Francois W. Nel on 27 Jun 2016.
*
* Description:
* The assembler converts Hack assembly instructions
* into machine code for the Hack computer architecture.
* Assembly instructions should be provided in a .asm source file,
* and the machine code will be stored in a .hack file
* with either the same filename, or using the provided
* filename and extension.
*
* Usage:
* $ chasm <inputfilename.asm> <(optional) outputfilename.hack>
*
* Limitations:
* Please ensure that there is a blank line at the end
* of your .asm source file.
*/
#include <iostream>
#include <fstream>
#include <bitset>
#include "Parser.h"
#include "CodeTranslator.h"
#include "SymbolTable.h"
using namespace std;
int main(int argc, char *argv[]) {
string inputFileName, outputFileName;
int lineNumberROM, newAddress;
unsigned long lineNumberSource;
ofstream fout;
// Get the input and output file names, and provide usage instructions
// if too few or too many arguments are provided.
if (argc < 2 || argc > 3) {
cout << "Usage: " << argv[0] << " <inputfilename.asm> <(optional) outputfilename.hack>" << endl;
exit(1);
}
else {
inputFileName = argv[1];
outputFileName = inputFileName.substr(0, inputFileName.length() - 4) + ".hack";
if (argc == 3) {
outputFileName = argv[2];
}
}
// Create the output file
fout.open(outputFileName);
if (fout.fail()) {
cout << "Failed to create output file." << endl;
exit(1);
}
/*
* First pass: Generate symbol table
* This is done by reading the input file line by line using the symbolSource parser,
* and looking for pseudo instructions. If an A-instruction or C-instruction is encountered,
* lineNumberROM is incremented, which ultimately corresponds to the program counter address
* of the instructions in the ROM / .hack output file. If an L-instruction is encountered
* and it does not already exist in the symbol table, the symbol and the current
* program counter address is stored in the symbol table.
*/
Parser symbolSource(inputFileName);
SymbolTable symbolTable;
lineNumberSource = 0;
lineNumberROM = 0;
while (true) {
symbolSource.advance(lineNumberSource);
if (!symbolSource.hasMoreCommands()) {
break;
}
if (symbolSource.commandType(lineNumberSource) == 'A' || symbolSource.commandType(lineNumberSource) == 'C') {
lineNumberROM++;
}
if (symbolSource.commandType(lineNumberSource) == 'L' && !symbolTable.contains(symbolSource.symbol())) {
symbolTable.addEntry(symbolSource.symbol(), lineNumberROM);
}
}
/*
* Second pass: Assemble machine code
* With the symbol table generated, we read the file line by line once more,
* this time using the assemblySource parser. Since the L-instructions have been dealt with,
* we are only looking for A-instructions and C-instructions.
*
* If an A-instruction is encountered, the address can either be a number,
* a predefined symbol, or a user defined variable. If the address is a number,
* the string is converted to its numeric representation in decimal,
* which is then converted to binary and output to the file.
* If the address is a symbol, the corresponding address is either
* retrieved if it is a predefined symbol, or generated if it is a user defined variable,
* and this address is then converted into binary and output to the file.
*
* If a C-instruction is encountered, the destination, computation and jump mnemonics
* are converted into binary code using the translator, and the resulting bit string
* is output to the file.
*/
Parser assemblySource(inputFileName);
CodeTranslator translator;
lineNumberSource = 0; // Reset the line number for the error handling.
newAddress = 16; // Predefined symbols occupy addresses 0-15.
while (true) {
assemblySource.advance(lineNumberSource);
if (!assemblySource.hasMoreCommands()) {
break;
}
if (assemblySource.commandType(lineNumberSource) == 'A') {
fout << '0'; // A-instructions always start with '0'.
// Check if the symbol is a number.
if (assemblySource.symbol().find_first_not_of("0123456789") == string::npos) {
// Convert the string to a decimal number, convert the decimal number to a binary number.
fout << bitset<15>(stoull(assemblySource.symbol(), nullptr)).to_string();
}
else {
// Check if the symbol is a variable.
if (!symbolTable.contains(assemblySource.symbol())) {
symbolTable.addEntry(assemblySource.symbol(), newAddress++);
}
// Retrieve the address, and convert the decimal number to a binary number.
fout << bitset<15>(static_cast<unsigned long long>(symbolTable.getAddress(assemblySource.symbol()))).to_string();
}
fout << endl;
}
else if (assemblySource.commandType(lineNumberSource) == 'C') {
fout << "111"; // C-instructions always start with "111".
fout << translator.comp(assemblySource.compM(), lineNumberSource);
fout << translator.dest(assemblySource.destM(), lineNumberSource);
fout << translator.jump(assemblySource.jumpM(), lineNumberSource);
fout << endl;
}
}
fout.close();
return 0;
}