11 Commits

Author SHA1 Message Date
9738ba1afd update to 1.1 2024-11-26 16:05:19 +01:00
432d706314 update README 2024-11-26 16:03:26 +01:00
be1c9f9fd7 add multiplication 2024-11-26 15:59:36 +01:00
f14d05a28a add reversing 2024-11-13 14:50:22 +01:00
6fcc51f2ca throw invalid arguments 2024-10-24 18:14:09 +02:00
777adffdea registry bound check 2024-10-24 18:11:19 +02:00
7c72155fea basic README 2024-10-23 21:09:20 +02:00
8452b98a97 set c++ standard 2024-10-23 20:51:41 +02:00
3207de80dd parse arguments 2024-10-23 20:48:18 +02:00
39eb113385 add multiple outputs 2024-10-23 20:12:11 +02:00
51f113804a fix jump forward 2024-10-23 20:05:41 +02:00
8 changed files with 243 additions and 26 deletions

3
.gitignore vendored
View File

@@ -5,5 +5,6 @@ build/
# MacOS Cache # MacOS Cache
.DS_Store .DS_Store
test.bin
.vscode .vscode
a.out

75
README.md Normal file
View File

@@ -0,0 +1,75 @@
# Assembleur
## Usage
```bash
./Assembleur [--help] [--reversed] [--version] [--output file] [--format type] file
```
There are 3 format types :
- "int" : 32 bits integers are written. Exemple : `10878976`
- "binint" : bits are written. Exemple : `00000000 10100110 00000000 00000000`
- "bin" : the file is written in pure binary
- "logisim" (default) : the file is written in binary for use in LogiSim
## Exemple
```assembly
operations:
add R1 R2 R3
sub R1 R2 #69
and R0 R6 R3
xor R1 R2 R3
or R1 R7 R3
sl R5 R2 #10
sr R1 R2 R3
mult R1 R1 R3
io:
str R1 R2 R3
ld R1 R2 R3
sauts:
jmp controle
jequ R1 R2 io
jneq R1 R2 sauts
jsup R1 R2 operations
jinf R1 R2 controle
controle:
call io
ret
```
Produces
```
00000000 10100110 00000000 00000000
00010100 10100000 00000000 01000101
00100000 10100110 00000000 00000000
01000000 10100110 00000000 00000000
00110000 10100110 00000000 00000000
01010000 10100110 00000000 00000000
01100000 10100110 00000000 00000000
01110000 10100110 00000000 00000000
01000001 01001100 00000000 00000000
01010001 01001100 00000000 00000000
11000000 00000000 00000000 00000101
11010001 01010000 00000000 00000011
11100001 01010000 00000000 00000010
11110001 01010000 00000000 00001101
11000001 01000000 00000000 00000001
11010100 00000000 00000000 00000111
11100000 00000000 00000000 00000000
```
## Build
You should have [xmake](https://xmake.io) installed
```bash
xmake
```
## Run
```bash
xmake run
```

View File

@@ -12,7 +12,7 @@ enum TypeArithmetique {
Xor, Xor,
Sl, Sl,
Sr, Sr,
Not, Mult,
}; };
enum TypeMemoire { enum TypeMemoire {
@@ -38,6 +38,7 @@ static std::map<std::string, Instruction> INSTRUCTION_KEYS = {
{"xor", {Arithmetique, Xor}}, {"xor", {Arithmetique, Xor}},
{"sl", {Arithmetique, Sl}}, {"sl", {Arithmetique, Sl}},
{"sr", {Arithmetique, Sr}}, {"sr", {Arithmetique, Sr}},
{"mult", {Arithmetique, Mult}},
{"str", {Memoire, Str}}, {"str", {Memoire, Str}},
{"ld", {Memoire, Ld}}, {"ld", {Memoire, Ld}},
{"jmp", {SautControle, Jump}}, {"jmp", {SautControle, Jump}},
@@ -53,7 +54,7 @@ std::uint32_t Assembleur::ParseLabel(const std::string& a_Label) {
auto it = m_Labels.find(a_Label); auto it = m_Labels.find(a_Label);
if (it == m_Labels.end()) { if (it == m_Labels.end()) {
throw std::runtime_error("Label " + a_Label + " not found !"); throw std::invalid_argument("Label " + a_Label + " not found !");
} }
return it->second; return it->second;
@@ -78,16 +79,16 @@ std::uint32_t Assembleur::ParseOperationImmediate(
} }
std::uint32_t Assembleur::ParseJump(Instruction a_Instruction, const std::string& a_Label) { std::uint32_t Assembleur::ParseJump(Instruction a_Instruction, const std::string& a_Label) {
int jump = a_Instruction.m_Line - ParseLabel(a_Label); std::int32_t jump = ParseLabel(a_Label) - a_Instruction.m_Line;
if (jump < 0) if (jump < 0)
jump = jump & 0x7FFFFFF | 0x4000000; jump = std::abs(jump) & 0x7FFFFFF | 0x4000000;
return IToInt(a_Instruction) | jump; return IToInt(a_Instruction) | jump;
} }
std::uint32_t Assembleur::ParseJump(Instruction a_Instruction, std::uint8_t a_R1, std::uint8_t a_R2, const std::string& a_Label) { std::uint32_t Assembleur::ParseJump(Instruction a_Instruction, std::uint8_t a_R1, std::uint8_t a_R2, const std::string& a_Label) {
int jump = a_Instruction.m_Line - ParseLabel(a_Label); std::int32_t jump = ParseLabel(a_Label) - a_Instruction.m_Line;
if (jump < 0) if (jump < 0)
jump = jump & 0xFFFFF | 100000; jump = std::abs(jump) & 0xFFFFF | 0x100000;
return IToInt(a_Instruction) | a_R1 << 24 | a_R2 << 21 | jump; return IToInt(a_Instruction) | a_R1 << 24 | a_R2 << 21 | jump;
} }
@@ -105,8 +106,11 @@ std::uint32_t Assembleur::ParseIO(Instruction a_Instruction, std::uint32_t a_R1,
static std::uint32_t ParseRegistry(const std::string& a_Str) { static std::uint32_t ParseRegistry(const std::string& a_Str) {
if (a_Str.at(0) != 'R') if (a_Str.at(0) != 'R')
throw std::runtime_error("Registry " + a_Str + " not found !"); throw std::invalid_argument("Registry " + a_Str + " not found !");
return std::stoi(a_Str.substr(1)); std::uint32_t registry = std::stoi(a_Str.substr(1));
if (registry > 7)
throw std::invalid_argument("You can only have up to 8 registries !");
return registry;
} }
static bool IsConstant(const std::string& a_Str) { static bool IsConstant(const std::string& a_Str) {
@@ -115,7 +119,7 @@ static bool IsConstant(const std::string& a_Str) {
static std::uint32_t ParseConstant(const std::string& a_Str) { static std::uint32_t ParseConstant(const std::string& a_Str) {
if (a_Str.at(0) != '#') if (a_Str.at(0) != '#')
throw std::runtime_error("Registry " + a_Str + " not found !"); throw std::invalid_argument("Registry " + a_Str + " not found !");
return std::stoi(a_Str.substr(1)); return std::stoi(a_Str.substr(1));
} }
@@ -126,7 +130,7 @@ std::uint32_t Assembleur::ParseInstruction(const std::string& a_Str, std::uint32
auto it = INSTRUCTION_KEYS.find(ins); auto it = INSTRUCTION_KEYS.find(ins);
if (it == INSTRUCTION_KEYS.end()) { if (it == INSTRUCTION_KEYS.end()) {
throw std::runtime_error("[Line " + std::to_string(a_RealLine) + "] " + "Instruction \"" + ins + "\" not found !"); throw std::invalid_argument("[Line " + std::to_string(a_RealLine) + "] " + "Instruction \"" + ins + "\" not found !");
} }
Instruction instruction = it->second; Instruction instruction = it->second;
@@ -174,8 +178,8 @@ std::uint32_t Assembleur::ParseInstruction(const std::string& a_Str, std::uint32
} }
} }
} catch (std::runtime_error& e) { } catch (std::invalid_argument& e) {
throw std::runtime_error("[Line " + std::to_string(a_RealLine) + "] " + e.what()); throw std::invalid_argument("[Line " + std::to_string(a_RealLine) + "] " + e.what());
} }
return 0; return 0;

View File

@@ -1,34 +1,110 @@
#include "IO.h" #include "IO.h"
#include "Assembleur.h" #include "Assembleur.h"
#include <algorithm>
#include <bitset>
#include <cstring>
#include <fstream> #include <fstream>
#include <iomanip>
static std::uint32_t reverseInt(std::uint32_t a_Int) {
std::uint32_t result = 0;
for (int i = 0; i < 32; i++) {
result |= a_Int & 0x1;
result <<= 1;
a_Int >>= 1;
}
return result;
}
static void reverseDataByte(BinaryData& a_Data) {
for (std::uint32_t& element : a_Data) {
element = reverseInt(element);
}
}
BinaryData ParseFile(const std::string& fileName) { BinaryData ParseFile(const std::string& fileName) {
std::ifstream file{fileName}; std::ifstream file{fileName};
std::uint32_t lineNumber = 0, realLineNumber = 0; std::uint32_t lineNumber = 0, realLineNumber = 0;
std::string line; std::string currentLine;
std::vector<std::string> lines;
Assembleur assembleur; Assembleur assembleur;
BinaryData output; BinaryData output;
while (getline(file, line)) { // parsing labels
while (getline(file, currentLine)) {
lines.push_back(currentLine);
lineNumber++; lineNumber++;
realLineNumber++; realLineNumber++;
if (line.find(":") != std::string::npos) { if (currentLine.find(":") != std::string::npos) {
std::string label = line.substr(0, line.size() - 1); std::string label = currentLine.substr(0, currentLine.size() - 1);
assembleur.AddLabel(label, lineNumber); assembleur.AddLabel(label, lineNumber);
lineNumber--; lineNumber--;
} else { }
}
lineNumber = realLineNumber = 0;
// parsing instructions
for (std::string line : lines) {
lineNumber++;
realLineNumber++;
if (line.find(":") == std::string::npos) {
output.push_back(assembleur.ParseInstruction(line, lineNumber, realLineNumber)); output.push_back(assembleur.ParseInstruction(line, lineNumber, realLineNumber));
} else {
lineNumber--;
} }
} }
return output; return output;
} }
void OutputFile(const BinaryData& a_Data, const std::string& fileName) { void OutputFileBinary(BinaryData& a_Data, const std::string& fileName, bool a_Reverse) {
std::ofstream file{fileName}; std::ofstream file{fileName};
if (a_Reverse)
reverseDataByte(a_Data);
file.write(reinterpret_cast<const char*>(a_Data.data()), a_Data.size() * sizeof(a_Data.at(0))); file.write(reinterpret_cast<const char*>(a_Data.data()), a_Data.size() * sizeof(a_Data.at(0)));
} }
void OutputFileIntegers(BinaryData& a_Data, const std::string& fileName, bool a_Reverse) {
std::ofstream file{fileName};
if (a_Reverse)
reverseDataByte(a_Data);
for (std::uint32_t number : a_Data) {
file << number << "\n";
}
}
void OutputFileBinIntegers(BinaryData& a_Data, const std::string& fileName, bool a_Reverse) {
std::ofstream file{fileName};
if (a_Reverse)
reverseDataByte(a_Data);
for (std::uint32_t number : a_Data) {
file << std::bitset<8>(number >> 24) << " " << std::bitset<8>(number >> 16) << " " << std::bitset<8>(number >> 8) << " "
<< std::bitset<8>(number) << "\n";
}
}
void OutputFileLogisim(BinaryData& a_Data, const std::string& fileName, const std::string& a_Header, bool a_Reverse) {
std::ofstream file{fileName};
file << a_Header << "\n";
std::uint64_t cursor = 0;
if (a_Reverse)
reverseDataByte(a_Data);
for (std::uint32_t number : a_Data) {
if (cursor % 8 == 0) {
file << std::setfill('0') << std::setw(4) << std::hex << cursor << std::dec << ": ";
file << std::bitset<8>(number >> 24) << " " << std::bitset<8>(number >> 16) << " " << std::bitset<8>(number >> 8) << " "
<< std::bitset<8>(number) << " ";
} else {
file << std::bitset<8>(number >> 24) << " " << std::bitset<8>(number >> 16) << " " << std::bitset<8>(number >> 8) << " "
<< std::bitset<8>(number) << "\n";
}
cursor += 4;
}
}

View File

@@ -7,4 +7,7 @@ using BinaryData = std::vector<std::uint32_t>;
BinaryData ParseFile(const std::string& fileName); BinaryData ParseFile(const std::string& fileName);
void OutputFile(const BinaryData& a_Data, const std::string& fileName); void OutputFileBinary(BinaryData& a_Data, const std::string& fileName, bool a_Reverse);
void OutputFileIntegers(BinaryData& a_Data, const std::string& fileName, bool a_Reverse);
void OutputFileBinIntegers(BinaryData& a_Data, const std::string& fileName, bool a_Reverse);
void OutputFileLogisim(BinaryData& a_Data, const std::string& fileName, const std::string& a_Header, bool a_Reverse);

View File

@@ -1,13 +1,65 @@
#include <argparse/argparse.hpp>
#include <iostream> #include <iostream>
#include "IO.h" #include "IO.h"
#define ASSEMBLEUR_VERSION "1.1"
int main(int argc, char** argv) { int main(int argc, char** argv) {
try { argparse::ArgumentParser program("Assembleur", ASSEMBLEUR_VERSION);
auto output = ParseFile("test.asm");
std::string inputFileName;
program.add_argument("file")
.help("The assembly file to compile")
.store_into(inputFileName);
std::string outputFileName;
program.add_argument("-o", "--output")
.help("Specify the output file.")
.metavar("file")
.default_value(std::string("a.out"))
.store_into(outputFileName);
std::string formatType;
program.add_argument("-f", "--format")
.help("Type of the output. [logisim|bin|int|binint]")
.metavar("type")
.default_value(std::string("logisim"))
.choices("logisim", "bin", "int", "binint")
.store_into(formatType);
std::string header;
program.add_argument("-h", "--header")
.help("Header of the file")
.metavar("header")
.default_value(std::string("v3.0 hex words addressed"))
.store_into(header);
bool reverse;
program.add_argument("-r", "--reversed").help("inverse bit orders").flag().store_into(reverse);
try {
program.parse_args(argc, argv);
} catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
try {
auto output = ParseFile(inputFileName);
if (formatType == "logisim") {
OutputFileLogisim(output, outputFileName, header, reverse);
} else if (formatType == "bin") {
OutputFileBinary(output, outputFileName, reverse);
} else if (formatType == "int") {
OutputFileIntegers(output, outputFileName, reverse);
} else {
OutputFileBinIntegers(output, outputFileName, reverse);
}
OutputFile(output, "test.bin");
} catch (std::runtime_error& e) { } catch (std::runtime_error& e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
return 1; return 1;

View File

@@ -6,15 +6,16 @@ operations:
or R1 R2 R3 or R1 R2 R3
sl R1 R2 R3 sl R1 R2 R3
sr R1 R2 R3 sr R1 R2 R3
mult R1 R2 R3
io: io:
str R1 R2 R3 str R1 R2 R3
ld R1 R2 R3 ld R1 R2 R3
sauts: sauts:
jmp operations jmp controle
jequ R1 R2 io jequ R1 R2 io
jneq R1 R2 sauts jneq R1 R2 sauts
jsup R1 R2 operations jsup R1 R2 operations
jinf R1 R2 io jinf R1 R2 controle
controle: controle:
call io call io
ret ret

View File

@@ -1,9 +1,14 @@
add_rules("mode.debug", "mode.release") add_rules("mode.debug", "mode.release")
add_requires("argparse")
set_languages("c++17")
target("Assembleur") target("Assembleur")
set_kind("binary") set_kind("binary")
add_files("src/*.cpp") add_files("src/*.cpp")
set_rundir(".") set_rundir(".")
add_packages("argparse")
-- --
-- If you want to known more usage about xmake, please see https://xmake.io -- If you want to known more usage about xmake, please see https://xmake.io