summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAmlal El Mahrouss <113760121+Amlal-ElMahrouss@users.noreply.github.com>2024-04-05 02:06:40 +0200
committerAmlal El Mahrouss <113760121+Amlal-ElMahrouss@users.noreply.github.com>2024-04-05 02:09:16 +0200
commit4fd8405b79823c6ce8dcfc3cbe0da29b2a8f5bf2 (patch)
tree7606ed7050a026c4a4d2c73b02e22f86540f138f
parent0f9388e1c596c13ae3c317d81e7bcfe23626acc2 (diff)
CodeTools: Working on PowerPC and 64x0 C compiler.
CodeTools: Working on PowerPC assembler. Signed-off-by: Amlal El Mahrouss <113760121+Amlal-ElMahrouss@users.noreply.github.com>
-rw-r--r--64x000-cc.rsrc25
-rw-r--r--Examples/ExampleCDialect.S21
-rw-r--r--Headers/AsmKit/Arch/powerpc.hpp73
-rw-r--r--Headers/AsmKit/AsmKit.hpp18
-rw-r--r--Sources/32asm.cc2
-rw-r--r--Sources/64asm.cc2
-rw-r--r--Sources/64x000-cc.cc (renamed from Sources/cc.cc)6
-rw-r--r--Sources/Deprecated/ccplus.cc2
-rw-r--r--Sources/bpp.cc2
-rw-r--r--Sources/i64asm.cc2
-rw-r--r--Sources/link.cc2
-rw-r--r--Sources/ppc-cc.cc1345
-rw-r--r--Sources/ppcasm.cc905
-rw-r--r--makefile29
-rw-r--r--ppc-cc.rsrc25
15 files changed, 2429 insertions, 30 deletions
diff --git a/64x000-cc.rsrc b/64x000-cc.rsrc
new file mode 100644
index 0000000..e6bf607
--- /dev/null
+++ b/64x000-cc.rsrc
@@ -0,0 +1,25 @@
+#include "Headers/Version.hxx"
+
+1 VERSIONINFO
+FILEVERSION 1,0,0,0
+PRODUCTVERSION 1,0,0,0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080904E4"
+ BEGIN
+ VALUE "CompanyName", "Mahrouss-Logic"
+ VALUE "FileDescription", "NewOS 64x000 C compiler."
+ VALUE "FileVersion", DIST_VERSION
+ VALUE "InternalName", "NewC"
+ VALUE "LegalCopyright", "Mahrouss-Logic"
+ VALUE "OriginalFilename", "64x000-cc.exe"
+ VALUE "ProductName", "NewC"
+ VALUE "ProductVersion", DIST_VERSION
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x809, 1252
+ END
+END \ No newline at end of file
diff --git a/Examples/ExampleCDialect.S b/Examples/ExampleCDialect.S
index 46982d5..d9613ef 100644
--- a/Examples/ExampleCDialect.S
+++ b/Examples/ExampleCDialect.S
@@ -1,10 +1,23 @@
# Path: Examples/ExampleCDialect.c
-# Language: 64x0 Assembly (Generated from ANSI C)
-# Build Date: 2024-3-29
+# Language: PowerPC Assembly (Generated from ANSI C)
+# Build Date: 2024-4-4
+
+dword export .code64 main
+
-dword export .code64 __start
lda r2,0x1000
- ldw r19, 57
+ lda r12, import __MPCC_IF_PROC_417129812448
+ #r12 = Code to jump on, r11 right cond, r10 left cond.
+ beq r10, r11, r12
+dword export .code64 __MPCC_IF_PROC_417129812448
+
+
+
+ ldw r19, import foo
+ jlr
+
+
+ ldw r19, 57
jlr
diff --git a/Headers/AsmKit/Arch/powerpc.hpp b/Headers/AsmKit/Arch/powerpc.hpp
index e91c939..53a15ca 100644
--- a/Headers/AsmKit/Arch/powerpc.hpp
+++ b/Headers/AsmKit/Arch/powerpc.hpp
@@ -7,7 +7,76 @@
#pragma once
#include <Headers/Defines.hpp>
+#include <vector>
-// @brief PowerPC support.
-// @file Arch/powerpc.hpp
+// @brief 64x0 support.
+// @file Arch/64x0.hpp
+#define kAsmOpcodeDecl(__NAME, __OPCODE, __FUNCT3, __FUNCT7) \
+ {.fName = __NAME, \
+ .fOpcode = __OPCODE, \
+ .fFunct3 = __FUNCT3, \
+ .fFunct7 = __FUNCT7},
+
+#define kAsmImmediate 0x01
+#define kAsmRegToReg 0x02
+#define kAsmSyscall 0x03
+#define kAsmJump 0x04
+#define kAsmNoArgs 0x00
+
+typedef char e64k_character_t;
+typedef uint8_t e64k_num_t;
+
+struct CpuCode64x0 {
+ const e64k_character_t fName[32];
+ e64k_num_t fOpcode;
+ e64k_num_t fFunct3;
+ e64k_num_t fFunct7;
+};
+
+inline std::vector<CpuCode64x0> kOpcodes64x0 = {
+ kAsmOpcodeDecl("nop", 0b0000000, 0b0000000, kAsmNoArgs) // no-operation.
+ kAsmOpcodeDecl("np", 0b0000000, 0b0000000, kAsmNoArgs) // no-operation.
+ kAsmOpcodeDecl("jlr", 0b1110011, 0b0000111,
+ kAsmJump) // jump to linked return register
+ kAsmOpcodeDecl("jrl", 0b1110011, 0b0001111,
+ kAsmJump) // jump from return register.
+ kAsmOpcodeDecl("mv", 0b0100011, 0b101, kAsmRegToReg) kAsmOpcodeDecl(
+ "bg", 0b1100111, 0b111,
+ kAsmRegToReg) kAsmOpcodeDecl("bl", 0b1100111, 0b011, kAsmRegToReg)
+ kAsmOpcodeDecl("beq", 0b1100111, 0b000, kAsmRegToReg) kAsmOpcodeDecl(
+ "bne", 0b1100111, 0b001,
+ kAsmRegToReg) kAsmOpcodeDecl("bge", 0b1100111, 0b101, kAsmRegToReg)
+ kAsmOpcodeDecl("ble", 0b1100111, 0b100, kAsmRegToReg)
+ kAsmOpcodeDecl("stw", 0b0001111, 0b100, kAsmImmediate)
+ kAsmOpcodeDecl("ldw", 0b0001111, 0b100, kAsmImmediate)
+ kAsmOpcodeDecl("lda", 0b0001111, 0b101, kAsmImmediate)
+ kAsmOpcodeDecl("sta", 0b0001111, 0b001,
+ kAsmImmediate)
+ // add/sub without carry flag
+ kAsmOpcodeDecl("add", 0b0101011, 0b100, kAsmImmediate)
+ kAsmOpcodeDecl("dec", 0b0101011, 0b101, kAsmImmediate)
+ // add/sub with carry flag
+ kAsmOpcodeDecl("addc", 0b0101011, 0b110, kAsmImmediate)
+ kAsmOpcodeDecl("decc", 0b0101011, 0b111, kAsmImmediate)
+ kAsmOpcodeDecl("int", 0b1110011, 0b00, kAsmSyscall)
+ kAsmOpcodeDecl("pha", 0b1110011, 0b00, kAsmNoArgs)
+ kAsmOpcodeDecl("pla", 0b1110011, 0b01, kAsmNoArgs)
+};
+
+// \brief 64x0 register prefix
+// example: r32, r0
+// r32 -> sp
+// r0 -> hw zero
+
+#define kAsmFloatZeroRegister 0
+#define kAsmZeroRegister 0
+
+#define kAsmRegisterPrefix "r"
+#define kAsmRegisterLimit 31
+#define kAsmPcRegister 17
+#define kAsmCrRegister 18
+#define kAsmSpRegister 5
+
+/* return address register */
+#define kAsmRetRegister 19
diff --git a/Headers/AsmKit/AsmKit.hpp b/Headers/AsmKit/AsmKit.hpp
index 72791a8..86a5248 100644
--- a/Headers/AsmKit/AsmKit.hpp
+++ b/Headers/AsmKit/AsmKit.hpp
@@ -163,4 +163,22 @@ class Encoder32x0 final : public EncoderInterface {
};
#endif // __ASM_NEED_32x0__
+
+#ifdef __ASM_NEED_PPC__
+
+class EncoderPowerPC final : public EncoderInterface {
+ public:
+ explicit EncoderPowerPC() = default;
+ ~EncoderPowerPC() override = default;
+
+ MPCC_COPY_DEFAULT(EncoderPowerPC);
+
+ virtual std::string CheckLine(std::string& line,
+ const std::string& file) override;
+ virtual bool WriteLine(std::string& line, const std::string& file) override;
+ virtual bool WriteNumber(const std::size_t& pos,
+ std::string& from_what) override;
+};
+
+#endif // __ASM_NEED_32x0__
} // namespace CompilerKit
diff --git a/Sources/32asm.cc b/Sources/32asm.cc
index 1d20f8f..72cad8d 100644
--- a/Sources/32asm.cc
+++ b/Sources/32asm.cc
@@ -50,6 +50,6 @@
/////////////////////////////////////////////////////////////////////////////////////////
-MPCC_MODULE(HCoreAssembler32000) {
+MPCC_MODULE(NewOSAssembler32000) {
return 0;
}
diff --git a/Sources/64asm.cc b/Sources/64asm.cc
index 92687ae..78fa590 100644
--- a/Sources/64asm.cc
+++ b/Sources/64asm.cc
@@ -102,7 +102,7 @@ void print_warning(std::string reason, const std::string &file) noexcept {
/////////////////////////////////////////////////////////////////////////////////////////
-MPCC_MODULE(HCoreAssembler64000) {
+MPCC_MODULE(NewOSAssembler64000) {
for (size_t i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
if (strcmp(argv[i], "-version") == 0 || strcmp(argv[i], "-v") == 0) {
diff --git a/Sources/cc.cc b/Sources/64x000-cc.cc
index c4aba6a..55c403a 100644
--- a/Sources/cc.cc
+++ b/Sources/64x000-cc.cc
@@ -34,7 +34,7 @@
/// @author Amlal El Mahrouss (amlel)
/// @file cc.cc
-/// @brief C Compiler.
+/// @brief 64x0 C Compiler.
/////////////////////
@@ -167,7 +167,7 @@ class CompilerBackendCLang final : public ParserKit::CompilerBackend {
std::string Check(const char *text, const char *file);
bool Compile(const std::string &text, const char *file) override;
- const char *Language() override { return "ANSI C"; }
+ const char *Language() override { return "64x000 C"; }
};
static CompilerBackendCLang *kCompilerBackend = nullptr;
@@ -1256,7 +1256,7 @@ static void cc_print_help() {
#define kExt ".c"
-MPCC_MODULE(HCoreCompilerCLang64x0) {
+MPCC_MODULE(NewOSCompilerCLang64x0) {
kCompilerTypes.push_back({.fName = "void", .fValue = "void"});
kCompilerTypes.push_back({.fName = "char", .fValue = "byte"});
kCompilerTypes.push_back({.fName = "short", .fValue = "hword"});
diff --git a/Sources/Deprecated/ccplus.cc b/Sources/Deprecated/ccplus.cc
index 00b6994..3385835 100644
--- a/Sources/Deprecated/ccplus.cc
+++ b/Sources/Deprecated/ccplus.cc
@@ -13,7 +13,7 @@
#define kSplashCxx() \
kPrintF(kWhite "%s\n", \
- "Mahrouss Visual C++ Compiler for HCore, Copyright Mahrouss Logic.")
+ "Mahrouss Visual C++ Compiler for NewOS, Copyright Mahrouss Logic.")
#include <Headers/AsmKit/Arch/amd64.hpp>
#include <Headers/ParserKit.hpp>
diff --git a/Sources/bpp.cc b/Sources/bpp.cc
index acae226..0b818cf 100644
--- a/Sources/bpp.cc
+++ b/Sources/bpp.cc
@@ -773,7 +773,7 @@ void bpp_parse_file(std::ifstream &hdr_file, std::ofstream &pp_out) {
/////////////////////////////////////////////////////////////////////////////////////////
-MPCC_MODULE(HCorePreprocessor) {
+MPCC_MODULE(NewOSPreprocessor) {
try {
bool skip = false;
bool double_skip = false;
diff --git a/Sources/i64asm.cc b/Sources/i64asm.cc
index 8a580ce..0eae5fc 100644
--- a/Sources/i64asm.cc
+++ b/Sources/i64asm.cc
@@ -115,7 +115,7 @@ void print_warning(std::string reason, const std::string &file) noexcept {
/////////////////////////////////////////////////////////////////////////////////////////
-MPCC_MODULE(HCoreAssemblerAMD64) {
+MPCC_MODULE(NewOSAssemblerAMD64) {
//////////////// CPU OPCODES BEGIN ////////////////
std::string opcodes_jump[kJumpLimit] = {
diff --git a/Sources/link.cc b/Sources/link.cc
index 93982cb..c3dab29 100644
--- a/Sources/link.cc
+++ b/Sources/link.cc
@@ -74,7 +74,7 @@ static const char *kLdDynamicSym = ":RuntimeSymbol:";
static std::vector<std::string> kObjectList;
static std::vector<char> kObjectBytes;
-MPCC_MODULE(HCoreLinker) {
+MPCC_MODULE(NewOSLinker) {
bool is_executable = true;
/**
diff --git a/Sources/ppc-cc.cc b/Sources/ppc-cc.cc
new file mode 100644
index 0000000..78f9bc1
--- /dev/null
+++ b/Sources/ppc-cc.cc
@@ -0,0 +1,1345 @@
+/*
+ * ========================================================
+ *
+ * cc
+ * Copyright Mahrouss Logic, all rights reserved.
+ *
+ * ========================================================
+ */
+
+/// BUGS: ?
+/// TODO:
+
+#include <Headers/AsmKit/Arch/powerpc.hpp>
+#include <Headers/ParserKit.hpp>
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <utility>
+#include <vector>
+#include <memory>
+#include <filesystem>
+
+#include <random>
+#include <Headers/UUID.hpp>
+
+#define kOk 0
+
+/// TODO: support structures, else if, else, ., ->
+
+/* C driver */
+/* This is part of MultiProcessor C SDK. */
+/* (c) Mahrouss Logic */
+
+/// @author Amlal El Mahrouss (amlel)
+/// @file cc.cc
+/// @brief PowerPC C Compiler.
+
+/////////////////////
+
+// ANSI ESCAPE CODES
+
+/////////////////////
+
+#define kBlank "\e[0;30m"
+#define kRed "\e[0;31m"
+#define kWhite "\e[0;97m"
+
+/////////////////////////////////////
+
+// INTERNAL STUFF OF THE C COMPILER
+
+/////////////////////////////////////
+
+namespace detail {
+// \brief name to register struct.
+struct CompilerRegisterMap final {
+ std::string fName;
+ std::string fReg;
+};
+
+// \brief Map for C structs
+// \author amlel
+struct CompilerStructMap final {
+ // 'my_foo'
+ std::string fName;
+
+ // if instance: stores a valid register.
+ std::string fReg;
+
+ // offset count
+ std::size_t fOffsetsCnt;
+
+ // offset array.
+ std::vector<std::pair<Int32, std::string>> fOffsets;
+};
+
+struct CompilerState final {
+ std::vector<ParserKit::SyntaxLeafList> fSyntaxTreeList;
+ std::vector<CompilerRegisterMap> kStackFrame;
+ std::vector<CompilerStructMap> kStructMap;
+ ParserKit::SyntaxLeafList *fSyntaxTree{nullptr};
+ std::unique_ptr<std::ofstream> fOutputAssembly;
+ std::string fLastFile;
+ std::string fLastError;
+ bool fVerbose;
+};
+} // namespace detail
+
+static detail::CompilerState kState;
+static SizeType kErrorLimit = 100;
+static std::string kIfFunction = "";
+static Int32 kAcceptableErrors = 0;
+
+namespace detail {
+void print_error(std::string reason, std::string file) noexcept {
+ if (reason[0] == '\n') reason.erase(0, 1);
+
+ if (file.find(".pp") != std::string::npos) {
+ file.erase(file.find(".pp"), 3);
+ }
+
+ if (kState.fLastFile != file) {
+ std::cout << kRed << "[ cc ] " << kWhite
+ << ((file == "cc") ? "internal compiler error "
+ : ("in file, " + file))
+ << kBlank << std::endl;
+ std::cout << kRed << "[ cc ] " << kWhite << reason << kBlank << std::endl;
+
+ kState.fLastFile = file;
+ } else {
+ std::cout << kRed << "[ cc ] [ " << kState.fLastFile << " ] " << kWhite
+ << reason << kBlank << std::endl;
+ }
+
+ if (kAcceptableErrors > kErrorLimit) std::exit(3);
+
+ ++kAcceptableErrors;
+}
+
+struct CompilerType final {
+ std::string fName;
+ std::string fValue;
+};
+} // namespace detail
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+// Target architecture.
+static int kMachine = 0;
+
+/////////////////////////////////////////
+
+// REGISTERS ACCORDING TO USED ASSEMBLER
+
+/////////////////////////////////////////
+
+static size_t kRegisterCnt = kAsmRegisterLimit;
+static size_t kStartUsable = 2;
+static size_t kUsableLimit = 15;
+static size_t kRegisterCounter = kStartUsable;
+static std::string kRegisterPrefix = kAsmRegisterPrefix;
+
+/////////////////////////////////////////
+
+// COMPILER PARSING UTILITIES/STATES.
+
+/////////////////////////////////////////
+
+static std::vector<std::string> kFileList;
+static CompilerKit::AssemblyFactory kFactory;
+static bool kInStruct = false;
+static bool kOnWhileLoop = false;
+static bool kOnForLoop = false;
+static bool kInBraces = false;
+static bool kIfFound = false;
+static size_t kBracesCount = 0UL;
+
+/* @brief C compiler backend for C */
+class CompilerBackendCLang final : public ParserKit::CompilerBackend {
+ public:
+ explicit CompilerBackendCLang() = default;
+ ~CompilerBackendCLang() override = default;
+
+ MPCC_COPY_DEFAULT(CompilerBackendCLang);
+
+ std::string Check(const char *text, const char *file);
+ bool Compile(const std::string &text, const char *file) override;
+
+ const char *Language() override { return "PowerPC C"; }
+};
+
+static CompilerBackendCLang *kCompilerBackend = nullptr;
+static std::vector<detail::CompilerType> kCompilerVariables;
+static std::vector<std::string> kCompilerFunctions;
+static std::vector<detail::CompilerType> kCompilerTypes;
+
+namespace detail {
+union number_cast final {
+ public:
+ number_cast(UInt64 _Raw) : _Raw(_Raw) {}
+
+ public:
+ char _Num[8];
+ UInt64 _Raw;
+};
+
+union double_cast final {
+ public:
+ double_cast(float _Raw) : _Raw(_Raw) {}
+
+ public:
+ char _Sign;
+ char _Lh[8];
+ char _Rh[23];
+
+ float _Raw;
+};
+} // namespace detail
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+// @name Compile
+// @brief Generate MASM from a C assignement.
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CompilerBackendCLang::Compile(const std::string &text, const char *file) {
+ std::string textBuffer = text;
+
+ bool typeFound = false;
+ bool fnFound = false;
+
+ // setup generator.
+ std::random_device rd;
+
+ auto seed_data = std::array<int, std::mt19937::state_size> {};
+ std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
+ std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
+ std::mt19937 generator(seq);
+
+ // start parsing
+ for (size_t text_index = 0; text_index < textBuffer.size(); ++text_index) {
+ auto syntaxLeaf = ParserKit::SyntaxLeafList::SyntaxLeaf();
+
+ auto gen = uuids::uuid_random_generator{generator};
+ uuids::uuid out = gen();
+
+ detail::number_cast time_off = (UInt64)out.as_bytes().data();
+
+ if (!typeFound) {
+ auto substr = textBuffer.substr(text_index);
+ std::string match_type;
+
+ for (size_t y = 0; y < substr.size(); ++y) {
+ if (substr[y] == ' ') {
+ while (match_type.find(' ') != std::string::npos) {
+ match_type.erase(match_type.find(' '));
+ }
+
+ for (auto &clType : kCompilerTypes) {
+ if (clType.fName == match_type) {
+ match_type.clear();
+
+ std::string buf;
+
+ buf += clType.fValue;
+ buf += ' ';
+
+ if (substr.find('=') != std::string::npos) {
+ break;
+ }
+
+ if (textBuffer.find('(') != std::string::npos) {
+ syntaxLeaf.fUserValue = buf;
+
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+ }
+
+ typeFound = true;
+ break;
+ }
+ }
+
+ break;
+ }
+
+ match_type += substr[y];
+ }
+ }
+
+ if (textBuffer[text_index] == '{') {
+ if (kInStruct) {
+ continue;
+ }
+
+ kInBraces = true;
+ ++kBracesCount;
+
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+ }
+
+ // return keyword handler
+ if (textBuffer[text_index] == 'r') {
+ std::string return_keyword;
+ return_keyword += "return";
+
+ std::size_t index = 0UL;
+
+ std::string value;
+
+ for (size_t return_index = text_index; return_index < textBuffer.size();
+ ++return_index) {
+ if (textBuffer[return_index] != return_keyword[index]) {
+ for (size_t value_index = return_index; value_index < textBuffer.size();
+ ++value_index) {
+ if (textBuffer[value_index] == ';') break;
+
+ value += textBuffer[value_index];
+ }
+
+ break;
+ }
+
+ ++index;
+ }
+
+ if (index == return_keyword.size()) {
+ if (!value.empty()) {
+ if (value.find('(') != std::string::npos) {
+ value.erase(value.find('('));
+ }
+
+ if (!isdigit(value[value.find('(') + 2])) {
+ std::string tmp = value;
+ bool reg_to_reg = false;
+
+ value.clear();
+
+ value += " import";
+ value += tmp;
+ }
+
+ syntaxLeaf.fUserValue = "\tldw r19, ";
+
+ // make it pretty.
+ if (value.find('\t') != std::string::npos)
+ value.erase(value.find('\t'), 1);
+
+ syntaxLeaf.fUserValue += value + "\n";
+ }
+
+ syntaxLeaf.fUserValue += "\tjlr";
+
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+
+ break;
+ }
+ }
+
+ if (textBuffer[text_index] == 'i' && textBuffer[text_index+1] == 'f') {
+ auto expr = textBuffer.substr(text_index + 2);
+ textBuffer.erase(text_index, 2);
+
+ if (expr.find("{") != std::string::npos) {
+ expr.erase(expr.find("{"));
+ }
+
+ if (expr.find("(") != std::string::npos)
+ expr.erase(expr.find("("));
+
+ if (expr.find(")") != std::string::npos)
+ expr.erase(expr.find(")"));
+
+ kIfFunction = "__MPCC_IF_PROC_";
+ kIfFunction += std::to_string(time_off._Raw);
+
+ syntaxLeaf.fUserValue = "\tlda r12, import ";
+ syntaxLeaf.fUserValue += kIfFunction + "\n\t#r12 = Code to jump on, r11 right cond, r10 left cond.\n\tbeq r10, r11, r12\ndword export .code64 " + kIfFunction + "\n";
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+
+ kIfFound = true;
+ }
+
+ // Parse expressions and instructions here.
+ // what does this mean?
+ // we encounter an assignment, or we reached the end of an expression.
+ if (textBuffer[text_index] == '=' || textBuffer[text_index] == ';') {
+ if (fnFound) continue;
+ if (kIfFound) continue;
+
+ if (textBuffer[text_index] == ';' && kInStruct) continue;
+
+ if (textBuffer.find("typedef ") != std::string::npos) continue;
+
+ if (textBuffer[text_index] == '=' && kInStruct) {
+ detail::print_error("assignement of value in struct " + textBuffer, file);
+ continue;
+ }
+
+ if (textBuffer[text_index] == ';' && kInStruct) {
+ bool space_found_ = false;
+ std::string sym;
+
+ for (auto &ch : textBuffer) {
+ if (ch == ' ') {
+ space_found_ = true;
+ }
+
+ if (ch == ';') break;
+
+ if (space_found_) sym.push_back(ch);
+ }
+
+ kState.kStructMap[kState.kStructMap.size() - 1].fOffsets.push_back(
+ std::make_pair(
+ kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt + 4,
+ sym));
+
+ kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt =
+ kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt + 4;
+
+ continue;
+ }
+
+ if (textBuffer[text_index] == '=' && kInStruct) {
+ continue;
+ }
+
+ if (textBuffer[text_index + 1] == '=' || textBuffer[text_index - 1] == '!' ||
+ textBuffer[text_index - 1] == '<' || textBuffer[text_index - 1] == '>') {
+ continue;
+ }
+
+ std::string substr;
+
+ if (textBuffer.find('=') != std::string::npos && kInBraces &&
+ !kIfFound) {
+ if (textBuffer.find("*") != std::string::npos) {
+ if (textBuffer.find("=") > textBuffer.find("*"))
+ substr += "\tlda ";
+ else
+ substr += "\tldw ";
+ } else {
+ substr += "\tldw ";
+ }
+ } else if (textBuffer.find('=') != std::string::npos && !kInBraces) {
+ substr += "stw export .data64 ";
+ }
+
+ int first_encountered = 0;
+
+ std::string str_name;
+
+ for (size_t text_index_2 = 0; text_index_2 < textBuffer.size();
+ ++text_index_2) {
+ if (textBuffer[text_index_2] == '\"') {
+ ++text_index_2;
+
+ // want to add this, so that the parser recognizes that this is a
+ // string.
+ substr += '"';
+
+ for (; text_index_2 < textBuffer.size(); ++text_index_2) {
+ if (textBuffer[text_index_2] == '\"') break;
+
+ substr += textBuffer[text_index_2];
+ }
+ }
+
+ if (textBuffer[text_index_2] == '{' || textBuffer[text_index_2] == '}') continue;
+
+ if (textBuffer[text_index_2] == ';') {
+ break;
+ }
+
+ if (textBuffer[text_index_2] == ' ' || textBuffer[text_index_2] == '\t') {
+ if (first_encountered != 2) {
+ if (textBuffer[text_index] != '=' &&
+ substr.find("export .data64") == std::string::npos && !kInStruct)
+ substr += "export .data64 ";
+ }
+
+ ++first_encountered;
+
+ continue;
+ }
+
+ if (textBuffer[text_index_2] == '=') {
+ if (!kInBraces) {
+ substr.replace(substr.find("export .data64"), strlen("export .data64"),
+ "export .page_zero ");
+ }
+
+ substr += ",";
+ continue;
+ }
+
+ substr += textBuffer[text_index_2];
+ }
+
+ for (auto &clType : kCompilerTypes) {
+ if (substr.find(clType.fName) != std::string::npos) {
+ if (substr.find(clType.fName) > substr.find('"')) continue;
+
+ substr.erase(substr.find(clType.fName), clType.fName.size());
+ } else if (substr.find(clType.fValue) != std::string::npos) {
+ if (substr.find(clType.fValue) > substr.find('"')) continue;
+
+ if (clType.fName == "const") continue;
+
+ substr.erase(substr.find(clType.fValue), clType.fValue.size());
+ }
+ }
+
+ if (substr.find("extern") != std::string::npos) {
+ substr.replace(substr.find("extern"), strlen("extern"), "import ");
+
+ if (substr.find("export .data64") != std::string::npos)
+ substr.erase(substr.find("export .data64"), strlen("export .data64"));
+ }
+
+ auto var_to_find =
+ std::find_if(kCompilerVariables.cbegin(), kCompilerVariables.cend(),
+ [&](detail::CompilerType type) {
+ return type.fName.find(substr) != std::string::npos;
+ });
+
+ if (kRegisterCounter == 5 || kRegisterCounter == 6) ++kRegisterCounter;
+
+ std::string reg = kAsmRegisterPrefix;
+ reg += std::to_string(kRegisterCounter);
+
+ if (var_to_find == kCompilerVariables.cend()) {
+ ++kRegisterCounter;
+
+ kState.kStackFrame.push_back({.fName = substr, .fReg = reg});
+ kCompilerVariables.push_back({.fName = substr});
+ }
+
+ syntaxLeaf.fUserValue += substr;
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+
+ if (textBuffer[text_index] == '=') break;
+ }
+
+ // function handler.
+
+ if (textBuffer[text_index] == '(' && !fnFound && !kIfFound) {
+ std::string substr;
+ std::string args_buffer;
+ std::string args;
+
+ bool type_crossed = false;
+
+ for (size_t idx = textBuffer.find('(') + 1; idx < textBuffer.size(); ++idx) {
+ if (textBuffer[idx] == ',') continue;
+
+ if (textBuffer[idx] == ' ') continue;
+
+ if (textBuffer[idx] == ')') break;
+ }
+
+ for (char substr_first_index : textBuffer) {
+ if (substr_first_index != ',')
+ args_buffer += substr_first_index;
+ else
+ args_buffer += '$';
+
+ if (substr_first_index == ';') {
+ args_buffer = args_buffer.erase(0, args_buffer.find('('));
+ args_buffer = args_buffer.erase(args_buffer.find(';'), 1);
+ args_buffer = args_buffer.erase(args_buffer.find(')'), 1);
+ args_buffer = args_buffer.erase(args_buffer.find('('), 1);
+
+ if (!args_buffer.empty()) args += "\tldw r6, ";
+
+ std::string register_type;
+ std::size_t index = 7UL;
+
+ while (args_buffer.find("$") != std::string::npos) {
+ register_type = kRegisterPrefix;
+ register_type += std::to_string(index);
+
+ ++index;
+
+ args_buffer.replace(args_buffer.find('$'), 1,
+ "\n\tldw " + register_type + ",");
+ }
+
+ args += args_buffer;
+ args += "\n\tlda r19, ";
+ }
+ }
+
+ for (char _text_i : textBuffer) {
+ if (_text_i == '\t' || _text_i == ' ') {
+ if (!type_crossed) {
+ substr.clear();
+ type_crossed = true;
+ }
+
+ continue;
+ }
+
+ if (_text_i == '(') break;
+
+ substr += _text_i;
+ }
+
+ if (kInBraces) {
+ syntaxLeaf.fUserValue = args;
+ syntaxLeaf.fUserValue += substr;
+ syntaxLeaf.fUserValue += "\n\tjrl\n";
+
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+
+ fnFound = true;
+ } else {
+ syntaxLeaf.fUserValue.clear();
+
+ syntaxLeaf.fUserValue += "export .code64 ";
+
+ syntaxLeaf.fUserValue += substr;
+ syntaxLeaf.fUserValue += "\n";
+
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+
+ fnFound = true;
+ }
+
+ kCompilerFunctions.push_back(textBuffer);
+ }
+
+ if (textBuffer[text_index] == '-' && textBuffer[text_index + 1] == '-') {
+ textBuffer = textBuffer.replace(textBuffer.find("--"), strlen("--"), "");
+
+ for (int _text_i = 0; _text_i < textBuffer.size(); ++_text_i) {
+ if (textBuffer[_text_i] == '\t' || textBuffer[_text_i] == ' ')
+ textBuffer.erase(_text_i, 1);
+ }
+
+ syntaxLeaf.fUserValue += "dec ";
+ syntaxLeaf.fUserValue += textBuffer;
+
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+ break;
+ }
+
+ if (textBuffer[text_index] == '}') {
+ kRegisterCounter = kStartUsable;
+
+ --kBracesCount;
+
+ if (kBracesCount < 1) {
+ kInBraces = false;
+ kBracesCount = 0;
+ }
+
+ if (kIfFound)
+ kIfFound = false;
+
+ if (kInStruct) kInStruct = false;
+
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+ }
+
+ syntaxLeaf.fUserValue.clear();
+ }
+
+ auto syntaxLeaf = ParserKit::SyntaxLeafList::SyntaxLeaf();
+ syntaxLeaf.fUserValue = "\n";
+ kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf);
+
+ return true;
+}
+
+static bool kShouldHaveBraces = false;
+static std::string kFnName;
+
+std::string CompilerBackendCLang::Check(const char *text, const char *file) {
+ std::string err_str;
+ std::string ln = text;
+
+ if (ln.empty()) {
+ return err_str;
+ }
+
+ bool non_ascii_found = false;
+
+ for (int i = 0; i < ln.size(); ++i) {
+ if (isalnum(ln[i])) {
+ non_ascii_found = true;
+ break;
+ }
+ }
+
+ if (kShouldHaveBraces && ln.find('{') != std::string::npos) {
+ kShouldHaveBraces = false;
+ }
+
+ if (!non_ascii_found) return err_str;
+
+ size_t string_index = 1UL;
+
+ if (ln.find('\'') != std::string::npos) {
+ string_index = ln.find('\'') + 1;
+
+ for (; string_index < ln.size(); ++string_index) {
+ if (ln[string_index] == '\'') {
+ if (ln[string_index + 1] != ';') {
+ ln.erase(string_index, 1);
+ }
+
+ return err_str;
+ }
+ }
+ } else if (ln.find('"') != std::string::npos) {
+ string_index = ln.find('"') + 1;
+
+ for (; string_index < ln.size(); ++string_index) {
+ if (ln[string_index] == '"') {
+ if (ln[string_index + 1] != ';') {
+ ln.erase(string_index, 1);
+ } else {
+ break;
+ }
+ }
+ }
+ } else if (ln.find('"') == std::string::npos &&
+ ln.find('\'') == std::string::npos) {
+ std::vector<std::string> forbidden_words;
+
+ forbidden_words.push_back("\\");
+ forbidden_words.push_back("?");
+ forbidden_words.push_back("@");
+ forbidden_words.push_back("~");
+ forbidden_words.push_back("::");
+ forbidden_words.push_back("/*");
+ forbidden_words.push_back("*/");
+
+ // add them to avoid stupid mistakes.
+ forbidden_words.push_back("namespace");
+ forbidden_words.push_back("class");
+ forbidden_words.push_back("extern \"C\"");
+
+ for (auto &forbidden : forbidden_words) {
+ if (ln.find(forbidden) != std::string::npos) {
+ err_str += "\nForbidden character detected: ";
+ err_str += forbidden;
+
+ return err_str;
+ }
+ }
+ }
+
+ struct CompilerVariableRange final {
+ std::string fBegin;
+ std::string fEnd;
+ };
+
+ const std::vector<CompilerVariableRange> variables_list = {
+ {.fBegin = "static ", .fEnd = "="}, {.fBegin = "=", .fEnd = ";"},
+ {.fBegin = "if(", .fEnd = "="}, {.fBegin = "if (", .fEnd = "="},
+ {.fBegin = "if(", .fEnd = "<"}, {.fBegin = "if (", .fEnd = "<"},
+ {.fBegin = "if(", .fEnd = ">"}, {.fBegin = "if (", .fEnd = ">"},
+ {.fBegin = "if(", .fEnd = ")"}, {.fBegin = "if (", .fEnd = ")"},
+
+ {.fBegin = "else(", .fEnd = "="}, {.fBegin = "else (", .fEnd = "="},
+ {.fBegin = "else(", .fEnd = "<"}, {.fBegin = "else (", .fEnd = "<"},
+ {.fBegin = "else(", .fEnd = ">"}, {.fBegin = "else (", .fEnd = ">"},
+ {.fBegin = "else(", .fEnd = ")"}, {.fBegin = "else (", .fEnd = ")"},
+ };
+
+ for (auto &variable : variables_list) {
+ if (ln.find(variable.fBegin) != std::string::npos) {
+ string_index = ln.find(variable.fBegin) + variable.fBegin.size();
+
+ while (ln[string_index] == ' ') ++string_index;
+
+ std::string keyword;
+
+ for (; string_index < ln.size(); ++string_index) {
+ if (ln[string_index] == variable.fEnd[0]) {
+ std::string varname = "";
+
+ for (size_t index_keyword = ln.find(' ');
+ ln[index_keyword] != variable.fBegin[0]; ++index_keyword) {
+ if (ln[index_keyword] == ' ') {
+ continue;
+ }
+
+ if (isdigit(ln[index_keyword])) {
+ goto cc_next_loop;
+ }
+
+ varname += ln[index_keyword];
+ }
+
+ if (varname.find(' ') != std::string::npos) {
+ varname.erase(0, varname.find(' '));
+
+ if (variable.fBegin == "extern") {
+ varname.erase(0, varname.find(' '));
+ }
+ }
+
+ if (kRegisterCounter == 5 || kRegisterCounter == 6)
+ ++kRegisterCounter;
+
+ std::string reg = kAsmRegisterPrefix;
+ reg += std::to_string(kRegisterCounter);
+
+ kCompilerVariables.push_back({.fValue = varname});
+ goto cc_check_done;
+ }
+
+ keyword.push_back(ln[string_index]);
+ }
+
+ goto cc_next_loop;
+
+ cc_check_done:
+
+ // skip digit value.
+ if (isdigit(keyword[0]) || keyword[0] == '"') {
+ goto cc_next_loop;
+ }
+
+ while (keyword.find(' ') != std::string::npos)
+ keyword.erase(keyword.find(' '), 1);
+
+ for (auto &var : kCompilerVariables) {
+ if (var.fValue.find(keyword) != std::string::npos) {
+ err_str.clear();
+ goto cc_next;
+ }
+ }
+
+ for (auto &fn : kCompilerFunctions) {
+ if (fn.find(keyword[0]) != std::string::npos) {
+ auto where_begin = fn.find(keyword[0]);
+ auto keyword_begin = 0UL;
+ auto failed = false;
+
+ for (; where_begin < keyword.size(); ++where_begin) {
+ if (fn[where_begin] == '(' && keyword[keyword_begin] == '(') break;
+
+ if (fn[where_begin] != keyword[keyword_begin]) {
+ failed = true;
+ break;
+ }
+
+ ++keyword_begin;
+ }
+
+ if (!failed) {
+ err_str.clear();
+ goto cc_next;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ cc_error_value:
+ if (keyword.find("->") != std::string::npos) return err_str;
+
+ if (keyword.find(".") != std::string::npos) return err_str;
+
+ if (isalnum(keyword[0])) err_str += "\nUndefined value: " + keyword;
+
+ return err_str;
+ }
+
+ cc_next_loop:
+ continue;
+ }
+
+cc_next:
+
+ // extern does not declare anything, it imports a variable.
+ // so that's why it's not declare upper.
+ if (ParserKit::find_word(ln, "extern")) {
+ auto substr = ln.substr(ln.find("extern") + strlen("extern"));
+ kCompilerVariables.push_back({.fValue = substr});
+ }
+
+ if (kShouldHaveBraces && ln.find('{') == std::string::npos) {
+ err_str += "Missing '{' for function ";
+ err_str += kFnName;
+ err_str += "\n";
+
+ kShouldHaveBraces = false;
+ kFnName.clear();
+ } else if (kShouldHaveBraces && ln.find('{') != std::string::npos) {
+ kShouldHaveBraces = false;
+ kFnName.clear();
+ }
+
+ bool type_not_found = true;
+
+ if (ln.find('\'') != std::string::npos) {
+ ln.replace(ln.find('\''), 3, "0");
+ }
+
+ auto first = ln.find('"');
+ if (first != std::string::npos) {
+ auto second = 0UL;
+ bool found_second_quote = false;
+
+ for (size_t i = first + 1; i < ln.size(); ++i) {
+ if (ln[i] == '\"') {
+ found_second_quote = true;
+ second = i;
+
+ break;
+ }
+ }
+
+ if (!found_second_quote) {
+ err_str += "Missing terminating \".";
+ err_str += " here -> " + ln.substr(ln.find('"'), second);
+ }
+ }
+
+ if (ln.find(')') != std::string::npos && ln.find(';') == std::string::npos) {
+ if (ln.find('{') == std::string::npos) {
+ kFnName = ln;
+ kShouldHaveBraces = true;
+
+ goto skip_braces_check;
+ } else if (ln.find('{') != std::string::npos) {
+ kShouldHaveBraces = false;
+ }
+ }
+
+skip_braces_check:
+
+ for (auto &key : kCompilerTypes) {
+ if (ParserKit::find_word(ln, key.fName)) {
+ if (isdigit(ln[ln.find(key.fName) + key.fName.size() + 1])) {
+ err_str += "\nNumber cannot be set for ";
+ err_str += key.fName;
+ err_str += "'s name. here -> ";
+ err_str += ln;
+ }
+
+ if (ln.find(key.fName) == 0 || ln[ln.find(key.fName) - 1] == ' ' ||
+ ln[ln.find(key.fName) - 1] == '\t') {
+ type_not_found = false;
+
+ if (ln[ln.find(key.fName) + key.fName.size()] != ' ') {
+ type_not_found = true;
+
+ if (ln[ln.find(key.fName) + key.fName.size()] == '\t')
+ type_not_found = false;
+
+ goto next;
+ } else if (ln[ln.find(key.fName) + key.fName.size()] != '\t') {
+ type_not_found = true;
+
+ if (ln[ln.find(key.fName) + key.fName.size()] == ' ')
+ type_not_found = false;
+ }
+ }
+
+ next:
+
+ if (ln.find(';') == std::string::npos) {
+ if (ln.find('(') != std::string::npos) {
+ if (ln.find('=') == std::string::npos) continue;
+ }
+
+ err_str += "\nMissing ';', here -> ";
+ err_str += ln;
+ } else {
+ continue;
+ }
+
+ if (ln.find('=') != std::string::npos) {
+ if (ln.find('(') != std::string::npos) {
+ if (ln.find(')') == std::string::npos) {
+ err_str += "\nMissing ')', after '(' here -> ";
+ err_str += ln.substr(ln.find('('));
+ }
+ }
+ }
+ }
+ }
+
+ if (kInBraces && ln.find("struct") != std::string::npos &&
+ ln.find("union") != std::string::npos &&
+ ln.find("enum") != std::string::npos &&
+ ln.find('=') != std::string::npos) {
+ if (ln.find(';') == std::string::npos) {
+ err_str += "\nMissing ';' after struct/union/enum declaration, here -> ";
+ err_str += ln;
+ }
+ }
+
+ if (ln.find(';') != std::string::npos &&
+ ln.find("for") == std::string::npos) {
+ if (ln.find(';') + 1 != ln.size()) {
+ for (int i = 0; i < ln.substr(ln.find(';') + 1).size(); ++i) {
+ if ((ln.substr(ln.find(';') + 1)[i] != ' ') ||
+ (ln.substr(ln.find(';') + 1)[i] != '\t')) {
+ if (auto err = this->Check(ln.substr(ln.find(';') + 1).c_str(), file);
+ !err.empty()) {
+ err_str += "\nUnexpected text after ';' -> ";
+ err_str += ln.substr(ln.find(';'));
+ err_str += err;
+ }
+ }
+ }
+ }
+ }
+
+ if (ln.find('(') != std::string::npos) {
+ if (ln.find(';') == std::string::npos && !ParserKit::find_word(ln, "|") &&
+ !ParserKit::find_word(ln, "||") && !ParserKit::find_word(ln, "&") &&
+ !ParserKit::find_word(ln, "&&") && !ParserKit::find_word(ln, "~")) {
+ bool found_func = false;
+ size_t i = ln.find('(');
+ std::vector<char> opens;
+ std::vector<char> closes;
+
+ for (; i < ln.size(); ++i) {
+ if (ln[i] == ')') {
+ closes.push_back(1);
+ }
+
+ if (ln[i] == '(') {
+ opens.push_back(1);
+ }
+ }
+
+ if (closes.size() != opens.size())
+ err_str += "Unterminated (), here -> " + ln;
+
+ bool space_found = false;
+
+ for (int i = 0; i < ln.size(); ++i) {
+ if (ln[i] == ')' && !space_found) {
+ space_found = true;
+ continue;
+ }
+
+ if (space_found) {
+ if (ln[i] == ' ' && isalnum(ln[i + 1])) {
+ err_str += "\nBad function format here -> ";
+ err_str += ln;
+ }
+ }
+ }
+ }
+
+ if (ln.find('(') < 1) {
+ err_str += "\nMissing identifier before '(' here -> ";
+ err_str += ln;
+ } else {
+ if (type_not_found && ln.find(';') == std::string::npos &&
+ ln.find("if") == std::string::npos &&
+ ln.find("|") == std::string::npos &&
+ ln.find("&") == std::string::npos &&
+ ln.find("(") == std::string::npos &&
+ ln.find(")") == std::string::npos) {
+ err_str += "\n Missing ';' or type, here -> ";
+ err_str += ln;
+ }
+ }
+
+ if (ln.find(')') == std::string::npos) {
+ err_str += "\nMissing ')', after '(' here -> ";
+ err_str += ln.substr(ln.find('('));
+ }
+ } else {
+ if (ln.find("for") != std::string::npos ||
+ ln.find("while") != std::string::npos) {
+ err_str += "\nMissing '(', after \"for\", here -> ";
+ err_str += ln;
+ }
+ }
+
+ if (ln.find('}') != std::string::npos && !kInBraces) {
+ if (!kInStruct && ln.find(';') == std::string::npos) {
+ err_str += "\nMismatched '}', here -> ";
+ err_str += ln;
+ }
+ }
+
+ if (!ln.empty()) {
+ if (ln.find(';') == std::string::npos &&
+ ln.find('{') == std::string::npos &&
+ ln.find('}') == std::string::npos &&
+ ln.find(')') == std::string::npos &&
+ ln.find('(') == std::string::npos &&
+ ln.find(',') == std::string::npos) {
+ if (ln.size() <= 2) return err_str;
+
+ err_str += "\nMissing ';', here -> ";
+ err_str += ln;
+ }
+ }
+
+ return err_str;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief C To Assembly mount-point.
+ */
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+class AssemblyMountpointCLang final : public CompilerKit::AssemblyInterface {
+ public:
+ explicit AssemblyMountpointCLang() = default;
+ ~AssemblyMountpointCLang() override = default;
+
+ MPCC_COPY_DEFAULT(AssemblyMountpointCLang);
+
+ [[maybe_unused]] static Int32 Arch() noexcept {
+ return CompilerKit::AssemblyFactory::kArchPowerPC;
+ }
+
+ Int32 CompileToFormat(std::string &src, Int32 arch) override {
+ if (arch != AssemblyMountpointCLang::Arch()) return -1;
+
+ if (kCompilerBackend == nullptr) return -1;
+
+ /* @brief copy contents wihtout extension */
+ std::string src_file = src.data();
+ std::ifstream src_fp = std::ifstream(src_file, std::ios::in);
+ std::string dest;
+
+ for (auto &ch : src_file) {
+ if (ch == '.') {
+ break;
+ }
+
+ dest += ch;
+ }
+
+ /* According to pef abi. */
+ std::vector<const char *> exts = kAsmFileExts;
+ dest += exts[4];
+
+ kState.fOutputAssembly = std::make_unique<std::ofstream>(dest);
+
+ auto fmt = CompilerKit::current_date();
+
+ (*kState.fOutputAssembly) << "# Path: " << src_file << "\n";
+ (*kState.fOutputAssembly)
+ << "# Language: PowerPC Assembly (Generated from ANSI C)\n";
+ (*kState.fOutputAssembly) << "# Build Date: " << fmt << "\n\n";
+
+ ParserKit::SyntaxLeafList syntax;
+
+ kState.fSyntaxTreeList.push_back(syntax);
+ kState.fSyntaxTree =
+ &kState.fSyntaxTreeList[kState.fSyntaxTreeList.size() - 1];
+
+ std::string line_src;
+
+ while (std::getline(src_fp, line_src)) {
+ if (auto err = kCompilerBackend->Check(line_src.c_str(), src.data());
+ err.empty()) {
+ kCompilerBackend->Compile(line_src, src.data());
+ } else {
+ detail::print_error(err, src.data());
+ }
+ }
+
+ if (kAcceptableErrors > 0) return -1;
+
+ std::vector<std::string> keywords = {"ldw", "stw", "lda", "sta",
+ "add", "dec", "mv"};
+
+ ///
+ /// Replace, optimize, fix assembly output.
+ ///
+
+ for (auto &leaf : kState.fSyntaxTree->fLeafList) {
+ std::vector<std::string> access_keywords = {"->", "."};
+
+ for (auto &access_ident : access_keywords) {
+ if (ParserKit::find_word(leaf.fUserValue, access_ident)) {
+ for (auto &struc : kState.kStructMap) {
+ /// TODO:
+ }
+ }
+ }
+
+ for (auto &keyword : keywords) {
+ if (ParserKit::find_word(leaf.fUserValue, keyword)) {
+ std::size_t cnt = 0UL;
+
+ for (auto &reg : kState.kStackFrame) {
+ std::string needle;
+
+ for (size_t i = 0; i < reg.fName.size(); i++) {
+ if (reg.fName[i] == ' ') {
+ ++i;
+
+ for (; i < reg.fName.size(); i++) {
+ if (reg.fName[i] == ',') {
+ break;
+ }
+
+ if (reg.fName[i] == ' ') continue;
+
+ needle += reg.fName[i];
+ }
+
+ break;
+ }
+ }
+
+ if (ParserKit::find_word(leaf.fUserValue, needle)) {
+ if (leaf.fUserValue.find("import " + needle) != std::string::npos) {
+ std::string range = "import " + needle;
+ leaf.fUserValue.replace(leaf.fUserValue.find("import " + needle), range.size(), needle);
+ }
+
+ if (leaf.fUserValue.find("ldw r6") != std::string::npos) {
+ std::string::difference_type countComma = std::count(
+ leaf.fUserValue.begin(), leaf.fUserValue.end(), ',');
+
+ if (countComma == 1) {
+ leaf.fUserValue.replace(leaf.fUserValue.find("ldw"),
+ strlen("ldw"), "mv");
+ }
+ }
+
+ leaf.fUserValue.replace(leaf.fUserValue.find(needle),
+ needle.size(), reg.fReg);
+
+ ++cnt;
+ }
+ }
+
+ if (cnt > 1 && keyword != "mv" && keyword != "add" &&
+ keyword != "dec") {
+ leaf.fUserValue.replace(leaf.fUserValue.find(keyword),
+ keyword.size(), "mv");
+ }
+ }
+ }
+ }
+
+ for (auto &leaf : kState.fSyntaxTree->fLeafList) {
+ (*kState.fOutputAssembly) << leaf.fUserValue;
+ }
+
+ kState.fSyntaxTree = nullptr;
+
+ kState.fOutputAssembly->flush();
+ kState.fOutputAssembly.reset();
+
+ return kOk;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define kPrintF printf
+#define kSplashCxx() kPrintF(kWhite "%s\n", "cc, v1.15, (c) Mahrouss Logic")
+
+static void cc_print_help() {
+ kSplashCxx();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define kExt ".c"
+
+MPCC_MODULE(NewOSCompilerCLangPowerPC) {
+ kCompilerTypes.push_back({.fName = "void", .fValue = "void"});
+ kCompilerTypes.push_back({.fName = "char", .fValue = "byte"});
+ kCompilerTypes.push_back({.fName = "short", .fValue = "hword"});
+ kCompilerTypes.push_back({.fName = "int", .fValue = "dword"});
+ kCompilerTypes.push_back({.fName = "long", .fValue = "qword"});
+ kCompilerTypes.push_back({.fName = "*", .fValue = "offset"});
+
+ bool skip = false;
+
+ kFactory.Mount(new AssemblyMountpointCLang());
+ kMachine = CompilerKit::AssemblyFactory::kArchPowerPC;
+ kCompilerBackend = new CompilerBackendCLang();
+
+ for (auto index = 1UL; index < argc; ++index) {
+ if (skip) {
+ skip = false;
+ continue;
+ }
+
+ if (argv[index][0] == '-') {
+ if (strcmp(argv[index], "-v") == 0 ||
+ strcmp(argv[index], "-version") == 0) {
+ kSplashCxx();
+ return kOk;
+ }
+
+ if (strcmp(argv[index], "-verbose") == 0) {
+ kState.fVerbose = true;
+
+ continue;
+ }
+
+ if (strcmp(argv[index], "-h") == 0 ||
+ strcmp(argv[index], "-help") == 0) {
+ cc_print_help();
+
+ return kOk;
+ }
+
+ if (strcmp(argv[index], "-dialect") == 0) {
+ if (kCompilerBackend) std::cout << kCompilerBackend->Language() << "\n";
+
+ return kOk;
+ }
+
+ if (strcmp(argv[index], "-fmax-exceptions") == 0) {
+ try {
+ kErrorLimit = std::strtol(argv[index + 1], nullptr, 10);
+ }
+ // catch anything here
+ catch (...) {
+ kErrorLimit = 0;
+ }
+
+ skip = true;
+
+ continue;
+ }
+
+ std::string err = "Unknown command: ";
+ err += argv[index];
+
+ detail::print_error(err, "cc");
+
+ continue;
+ }
+
+ kFileList.emplace_back(argv[index]);
+
+ std::string srcFile = argv[index];
+
+ if (strstr(argv[index], kExt) == nullptr) {
+ if (kState.fVerbose) {
+ detail::print_error(srcFile + " is not a valid C source.\n", "cc");
+ }
+
+ return 1;
+ }
+
+ if (kFactory.Compile(srcFile, kMachine) != kOk) return -1;
+ }
+
+ return kOk;
+}
+
+// Last rev 8-1-24
diff --git a/Sources/ppcasm.cc b/Sources/ppcasm.cc
index 93ee1cb..f37a650 100644
--- a/Sources/ppcasm.cc
+++ b/Sources/ppcasm.cc
@@ -10,14 +10,14 @@
// @file ppcasm.cxx
// @author Amlal El Mahrouss
-// @brief PowerPC 64 Assembler.
+// @brief PowerPC Assembler.
// REMINDER: when dealing with an undefined symbol use (string
// size):LinkerFindSymbol:(string) so that ld will look for it.
/////////////////////////////////////////////////////////////////////////////////////////
-#define __ASM_NEED_PPC64__ 1
+#define __ASM_NEED_PPC__ 1
#include <Headers/AsmKit/Arch/powerpc.hpp>
#include <Headers/ParserKit.hpp>
@@ -29,7 +29,6 @@
#include <vector>
#include <memory>
-
/////////////////////
// ANSI ESCAPE CODES
@@ -44,14 +43,904 @@
#define kStdOut (std::cout << kWhite)
#define kStdErr (std::cout << kRed)
+static char kOutputArch = CompilerKit::kPefArchPowerPC;
+static Boolean kOutputAsBinary = false;
+
+static UInt32 kErrorLimit = 10;
+static UInt32 kAcceptableErrors = 0;
+
+static std::size_t kCounter = 1UL;
+
+static std::uintptr_t kOrigin = kPefBaseOrigin;
+static std::vector<std::pair<std::string, std::uintptr_t>> kOriginLabel;
+
+static bool kVerbose = false;
+
+static std::vector<e64k_num_t> kBytes;
+
+static CompilerKit::AERecordHeader kCurrentRecord{
+ .fName = "", .fKind = CompilerKit::kPefCode, .fSize = 0, .fOffset = 0};
+
+static std::vector<CompilerKit::AERecordHeader> kRecords;
+static std::vector<std::string> kUndefinedSymbols;
+
+static const std::string kUndefinedSymbol = ":UndefinedSymbol:";
+static const std::string kRelocSymbol = ":RuntimeSymbol:";
+
+// \brief forward decl.
+static bool asm_read_attributes(std::string &line);
+
+namespace detail {
+void print_error(std::string reason, const std::string &file) noexcept {
+ if (reason[0] == '\n') reason.erase(0, 1);
+
+ kStdErr << kRed << "[ ppcasm ] " << kWhite
+ << ((file == "ppcasm") ? "internal assembler error "
+ : ("in file, " + file))
+ << kBlank << std::endl;
+ kStdErr << kRed << "[ ppcasm ] " << kWhite << reason << kBlank << std::endl;
+
+ if (kAcceptableErrors > kErrorLimit) std::exit(3);
+
+ ++kAcceptableErrors;
+}
+
+void print_warning(std::string reason, const std::string &file) noexcept {
+ if (reason[0] == '\n') reason.erase(0, 1);
+
+ if (!file.empty()) {
+ kStdOut << kYellow << "[ file ] " << kWhite << file << kBlank << std::endl;
+ }
+
+ kStdOut << kYellow << "[ ppcasm ] " << kWhite << reason << kBlank << std::endl;
+}
+} // namespace detail
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+// @brief PowerPC assembler entrypoint, the program/module starts here.
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MPCC_MODULE(NewOSAssemblerPowerPC) {
+ for (size_t i = 1; i < argc; ++i) {
+ if (argv[i][0] == '-') {
+ if (strcmp(argv[i], "-version") == 0 || strcmp(argv[i], "-v") == 0) {
+ kStdOut << "ppcasm: PowerPC Assembler.\nppcasm: v1.10\nppcasm: Copyright (c) "
+ "2024 Mahrouss Logic.\n";
+ return 0;
+ } else if (strcmp(argv[i], "-h") == 0) {
+ kStdOut << "ppcasm: PowerPC Assembler.\nppcasm: Copyright (c) 2024 Mahrouss "
+ "Logic.\n";
+ kStdOut << "-version: Print program version.\n";
+ kStdOut << "-verbose: Print verbose output.\n";
+ kStdOut << "-binary: Output as flat binary.\n";
+
+ return 0;
+ } else if (strcmp(argv[i], "-binary") == 0) {
+ kOutputAsBinary = true;
+ continue;
+ } else if (strcmp(argv[i], "-verbose") == 0) {
+ kVerbose = true;
+ continue;
+ }
+
+ kStdOut << "ppcasm: ignore " << argv[i] << "\n";
+ continue;
+ }
+
+ if (!std::filesystem::exists(argv[i])) {
+ kStdOut << "ppcasm: can't open: " << argv[i] << std::endl;
+ goto asm_fail_exit;
+ }
+
+ std::string object_output(argv[i]);
+
+ for (auto &ext : kAsmFileExts) {
+ if (object_output.find(ext) != std::string::npos) {
+ object_output.erase(object_output.find(ext), std::strlen(ext));
+ }
+ }
+
+ object_output += kObjectFileExt;
+
+ std::ifstream file_ptr(argv[i]);
+ std::ofstream file_ptr_out(object_output, std::ofstream::binary);
+
+ if (file_ptr_out.bad()) {
+ if (kVerbose) {
+ kStdOut << "ppcasm: error: " << strerror(errno) << "\n";
+ }
+ }
+
+ std::string line;
+
+ CompilerKit::AEHeader hdr{0};
+
+ memset(hdr.fPad, kAEInvalidOpcode, kAEPad);
+
+ hdr.fMagic[0] = kAEMag0;
+ hdr.fMagic[1] = kAEMag1;
+ hdr.fSize = sizeof(CompilerKit::AEHeader);
+ hdr.fArch = kOutputArch;
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+
+ // COMPILATION LOOP
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+
+ CompilerKit::EncoderPowerPC asm64;
+
+ while (std::getline(file_ptr, line)) {
+ if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) {
+ detail::print_error(ln, argv[i]);
+ continue;
+ }
+
+ try {
+ asm_read_attributes(line);
+ asm64.WriteLine(line, argv[i]);
+ } catch (const std::exception &e) {
+ if (kVerbose) {
+ std::string what = e.what();
+ detail::print_warning("exit because of: " + what, "ppcasm");
+ }
+
+ std::filesystem::remove(object_output);
+ goto asm_fail_exit;
+ }
+ }
+
+ if (!kOutputAsBinary) {
+ if (kVerbose) {
+ kStdOut << "ppcasm: Writing object file...\n";
+ }
+
+ // this is the final step, write everything to the file.
+
+ auto pos = file_ptr_out.tellp();
+
+ hdr.fCount = kRecords.size() + kUndefinedSymbols.size();
+
+ file_ptr_out << hdr;
+
+ if (kRecords.empty()) {
+ kStdErr << "ppcasm: At least one record is needed to write an object "
+ "file.\nppcasm: Make one using `export .code64 foo_bar`.\n";
+
+ std::filesystem::remove(object_output);
+ return -1;
+ }
+
+ kRecords[kRecords.size() - 1].fSize = kBytes.size();
+
+ std::size_t record_count = 0UL;
+
+ for (auto &rec : kRecords) {
+ if (kVerbose)
+ kStdOut << "ppcasm: Wrote record " << rec.fName << " to file...\n";
+
+ rec.fFlags |= CompilerKit::kKindRelocationAtRuntime;
+ rec.fOffset = record_count;
+ ++record_count;
+
+ file_ptr_out << rec;
+ }
+
+ // increment once again, so that we won't lie about the kUndefinedSymbols.
+ ++record_count;
+
+ for (auto &sym : kUndefinedSymbols) {
+ CompilerKit::AERecordHeader _record_hdr{0};
+
+ if (kVerbose)
+ kStdOut << "ppcasm: Wrote symbol " << sym << " to file...\n";
+
+ _record_hdr.fKind = kAEInvalidOpcode;
+ _record_hdr.fSize = sym.size();
+ _record_hdr.fOffset = record_count;
+
+ ++record_count;
+
+ memset(_record_hdr.fPad, kAEInvalidOpcode, kAEPad);
+ memcpy(_record_hdr.fName, sym.c_str(), sym.size());
+
+ file_ptr_out << _record_hdr;
+
+ ++kCounter;
+ }
+
+ auto pos_end = file_ptr_out.tellp();
+
+ file_ptr_out.seekp(pos);
+
+ hdr.fStartCode = pos_end;
+ hdr.fCodeSize = kBytes.size();
+
+ file_ptr_out << hdr;
+
+ file_ptr_out.seekp(pos_end);
+ } else {
+ if (kVerbose) {
+ kStdOut << "ppcasm: Write raw binary...\n";
+ }
+ }
+
+ // byte from byte, we write this.
+ for (auto &byte : kBytes) {
+ file_ptr_out.write(reinterpret_cast<const char *>(&byte), sizeof(byte));
+ }
+
+ if (kVerbose) kStdOut << "ppcasm: Wrote file with program in it.\n";
+
+ file_ptr_out.flush();
+ file_ptr_out.close();
+
+ if (kVerbose) kStdOut << "ppcasm: Exit succeeded.\n";
+
+ return 0;
+ }
+
+asm_fail_exit:
+
+ if (kVerbose) kStdOut << "ppcasm: Exit failed.\n";
+
+ return -1;
+}
+
/////////////////////////////////////////////////////////////////////////////////////////
-// @brief PowerPC 64 Assembler entrypoint, the program/module starts here.
+// @brief Check for attributes
+// returns true if any was found.
/////////////////////////////////////////////////////////////////////////////////////////
-MPCC_MODULE(HCoreAssemblerPowerPC64) {
- kStdOut << "ppcasm: Unimplemented assembler.\r\n";
-
- return 0;
+static bool asm_read_attributes(std::string &line) {
+ // import is the opposite of export, it signals to the ld
+ // that we need this symbol.
+ if (ParserKit::find_word(line, "import ")) {
+ if (kOutputAsBinary) {
+ detail::print_error("Invalid import directive in flat binary mode.",
+ "ppcasm");
+ throw std::runtime_error("invalid_import_bin");
+ }
+
+ auto name = line.substr(line.find("import ") + strlen("import "));
+
+ std::string result = std::to_string(name.size());
+ result += kUndefinedSymbol;
+
+ // mangle this
+ for (char &j : name) {
+ if (j == ' ' || j == ',') j = '$';
+ }
+
+ result += name;
+
+ if (name.find(".code64") != std::string::npos) {
+ // data is treated as code.
+ kCurrentRecord.fKind = CompilerKit::kPefCode;
+ } else if (name.find(".data64") != std::string::npos) {
+ // no code will be executed from here.
+ kCurrentRecord.fKind = CompilerKit::kPefData;
+ } else if (name.find(".page_zero") != std::string::npos) {
+ // this is a bss section.
+ kCurrentRecord.fKind = CompilerKit::kPefZero;
+ }
+
+ // this is a special case for the start stub.
+ // we want this so that ld can find it.
+
+ if (name.find("__start") != std::string::npos) {
+ kCurrentRecord.fKind = CompilerKit::kPefCode;
+ }
+
+ // now we can tell the code size of the previous kCurrentRecord.
+
+ if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size();
+
+ memset(kCurrentRecord.fName, 0, kAESymbolLen);
+ memcpy(kCurrentRecord.fName, result.c_str(), result.size());
+
+ ++kCounter;
+
+ memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad);
+
+ kRecords.emplace_back(kCurrentRecord);
+
+ return true;
+ }
+ // export is a special keyword used by ppcasm to tell the AE output stage to
+ // mark this section as a header. it currently supports .code64, .data64.,
+ // page_zero
+ else if (ParserKit::find_word(line, "export ")) {
+ if (kOutputAsBinary) {
+ detail::print_error("Invalid export directive in flat binary mode.",
+ "ppcasm");
+ throw std::runtime_error("invalid_export_bin");
+ }
+
+ auto name = line.substr(line.find("export ") + strlen("export "));
+
+ std::string name_copy = name;
+
+ for (char &j : name) {
+ if (j == ' ') j = '$';
+ }
+
+ if (name.find(".code64") != std::string::npos) {
+ // data is treated as code.
+
+ name_copy.erase(name_copy.find(".code64"), strlen(".code64"));
+ kCurrentRecord.fKind = CompilerKit::kPefCode;
+ } else if (name.find(".data64") != std::string::npos) {
+ // no code will be executed from here.
+
+ name_copy.erase(name_copy.find(".data64"), strlen(".data64"));
+ kCurrentRecord.fKind = CompilerKit::kPefData;
+ } else if (name.find(".page_zero") != std::string::npos) {
+ // this is a bss section.
+
+ name_copy.erase(name_copy.find(".page_zero"), strlen(".page_zero"));
+ kCurrentRecord.fKind = CompilerKit::kPefZero;
+ }
+
+ // this is a special case for the start stub.
+ // we want this so that ld can find it.
+
+ if (name == "__start") {
+ kCurrentRecord.fKind = CompilerKit::kPefCode;
+ }
+
+ while (name_copy.find(" ") != std::string::npos)
+ name_copy.erase(name_copy.find(" "), 1);
+
+ kOriginLabel.push_back(std::make_pair(name_copy, kOrigin));
+ ++kOrigin;
+
+ // now we can tell the code size of the previous kCurrentRecord.
+
+ if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size();
+
+ memset(kCurrentRecord.fName, 0, kAESymbolLen);
+ memcpy(kCurrentRecord.fName, name.c_str(), name.size());
+
+ ++kCounter;
+
+ memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad);
+
+ kRecords.emplace_back(kCurrentRecord);
+
+ return true;
+ }
+
+ return false;
}
+
+// \brief algorithms and helpers.
+
+namespace detail::algorithm {
+// \brief authorize a brief set of characters.
+static inline bool is_not_alnum_space(char c) {
+ return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') ||
+ (c == ',') || (c == '(') || (c == ')') || (c == '"') ||
+ (c == '\'') || (c == '[') || (c == ']') || (c == '+') ||
+ (c == '_') || (c == ':') || (c == '@') || (c == '.'));
+}
+
+bool is_valid(const std::string &str) {
+ return find_if(str.begin(), str.end(), is_not_alnum_space) == str.end();
+}
+} // namespace detail::algorithm
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+// @brief Check for line (syntax check)
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+std::string CompilerKit::EncoderPowerPC::CheckLine(
+ std::string &line, const std::string &file) {
+ std::string err_str;
+
+ if (line.empty() || ParserKit::find_word(line, "import") ||
+ ParserKit::find_word(line, "export") || line.find('#') != std::string::npos ||
+ ParserKit::find_word(line, ";")) {
+ if (line.find('#') != std::string::npos) {
+ line.erase(line.find('#'));
+ } else if (line.find(';') != std::string::npos) {
+ line.erase(line.find(';'));
+ } else {
+ // now check the line for validity
+ if (!detail::algorithm::is_valid(line)) {
+ err_str = "Line contains non alphanumeric characters.\nhere -> ";
+ err_str += line;
+ }
+ }
+
+ return err_str;
+ }
+
+ if (!detail::algorithm::is_valid(line)) {
+ err_str = "Line contains non alphanumeric characters.\nhere -> ";
+ err_str += line;
+
+ return err_str;
+ }
+
+ // check for a valid instruction format.
+
+ if (line.find(',') != std::string::npos) {
+ if (line.find(',') + 1 == line.size()) {
+ err_str += "\nInstruction lacks right register, here -> ";
+ err_str += line.substr(line.find(','));
+
+ return err_str;
+ } else {
+ bool nothing_on_right = true;
+
+ if (line.find(',') + 1 > line.size()) {
+ err_str += "\nInstruction not complete, here -> ";
+ err_str += line;
+
+ return err_str;
+ }
+
+ auto substr = line.substr(line.find(',') + 1);
+
+ for (auto &ch : substr) {
+ if (ch != ' ' && ch != '\t') {
+ nothing_on_right = false;
+ }
+ }
+
+ // this means we found nothing after that ',' .
+ if (nothing_on_right) {
+ err_str += "\nInstruction not complete, here -> ";
+ err_str += line;
+
+ return err_str;
+ }
+ }
+ }
+
+ // these do take an argument.
+ std::vector<std::string> operands_inst = {"stw", "ldw", "lda", "sta"};
+
+ // these don't.
+ std::vector<std::string> filter_inst = {"jlr", "jrl", "int"};
+
+ for (auto &opcode64x0 : kOpcodes64x0) {
+ if (line.find(opcode64x0.fName) != std::string::npos) {
+ if (opcode64x0.fFunct7 == kAsmNoArgs) return err_str;
+
+ for (auto &op : operands_inst) {
+ // if only the instruction was found.
+ if (line == op) {
+ err_str += "\nMalformed ";
+ err_str += op;
+ err_str += " instruction, here -> ";
+ err_str += line;
+ }
+ }
+
+ // if it is like that -> addr1, 0x0
+ if (auto it = std::find(filter_inst.begin(), filter_inst.end(),
+ opcode64x0.fName);
+ it == filter_inst.cend()) {
+ if (ParserKit::find_word(line, opcode64x0.fName)) {
+ if (!isspace(line[line.find(opcode64x0.fName) +
+ strlen(opcode64x0.fName)])) {
+ err_str += "\nMissing space between ";
+ err_str += opcode64x0.fName;
+ err_str += " and operands.\nhere -> ";
+ err_str += line;
+ }
+ }
+ }
+
+ return err_str;
+ }
+ }
+
+ err_str += "Unrecognized instruction and operands: " + line;
+
+ return err_str;
+}
+
+bool CompilerKit::EncoderPowerPC::WriteNumber(const std::size_t &pos,
+ std::string &jump_label) {
+ if (!isdigit(jump_label[pos])) return false;
+
+ switch (jump_label[pos + 1]) {
+ case 'x': {
+ if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16);
+ !res) {
+ if (errno != 0) {
+ detail::print_error("invalid hex number: " + jump_label, "ppcasm");
+ throw std::runtime_error("invalid_hex");
+ }
+ }
+
+ CompilerKit::NumberCast64 num(
+ strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16));
+
+ for (char &i : num.number) {
+ kBytes.push_back(i);
+ }
+
+ if (kVerbose) {
+ kStdOut << "ppcasm: found a base 16 number here: "
+ << jump_label.substr(pos) << "\n";
+ }
+
+ return true;
+ }
+ case 'b': {
+ if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2);
+ !res) {
+ if (errno != 0) {
+ detail::print_error("invalid binary number: " + jump_label, "ppcasm");
+ throw std::runtime_error("invalid_bin");
+ }
+ }
+
+ CompilerKit::NumberCast64 num(
+ strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2));
+
+ if (kVerbose) {
+ kStdOut << "ppcasm: found a base 2 number here: "
+ << jump_label.substr(pos) << "\n";
+ }
+
+ for (char &i : num.number) {
+ kBytes.push_back(i);
+ }
+
+ return true;
+ }
+ case 'o': {
+ if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7);
+ !res) {
+ if (errno != 0) {
+ detail::print_error("invalid octal number: " + jump_label, "ppcasm");
+ throw std::runtime_error("invalid_octal");
+ }
+ }
+
+ CompilerKit::NumberCast64 num(
+ strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7));
+
+ if (kVerbose) {
+ kStdOut << "ppcasm: found a base 8 number here: "
+ << jump_label.substr(pos) << "\n";
+ }
+
+ for (char &i : num.number) {
+ kBytes.push_back(i);
+ }
+
+ return true;
+ }
+ default: {
+ break;
+ }
+ }
+
+ /* check for errno and stuff like that */
+ if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) {
+ if (errno != 0) {
+ return false;
+ }
+ }
+
+ CompilerKit::NumberCast64 num(
+ strtol(jump_label.substr(pos).c_str(), nullptr, 10));
+
+ for (char &i : num.number) {
+ kBytes.push_back(i);
+ }
+
+ if (kVerbose) {
+ kStdOut << "ppcasm: found a base 10 number here: " << jump_label.substr(pos)
+ << "\n";
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+// @brief Read and write an instruction to the output array.
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CompilerKit::EncoderPowerPC::WriteLine(std::string &line,
+ const std::string &file) {
+ if (ParserKit::find_word(line, "export ")) return true;
+
+ for (auto &opcode64x0 : kOpcodes64x0) {
+ // strict check here
+ if (ParserKit::find_word(line, opcode64x0.fName) &&
+ detail::algorithm::is_valid(line)) {
+ std::string name(opcode64x0.fName);
+ std::string jump_label, cpy_jump_label;
+
+ kBytes.emplace_back(opcode64x0.fOpcode);
+ kBytes.emplace_back(opcode64x0.fFunct3);
+ kBytes.emplace_back(opcode64x0.fFunct7);
+
+ // check funct7 type.
+ switch (opcode64x0.fFunct7) {
+ // reg to reg means register to register transfer operation.
+ case kAsmRegToReg:
+ case kAsmImmediate: {
+ // \brief how many registers we found.
+ std::size_t found_some = 0UL;
+
+ for (size_t line_index = 0UL; line_index < line.size();
+ line_index++) {
+ if (line[line_index] == kAsmRegisterPrefix[0] &&
+ isdigit(line[line_index + 1])) {
+ std::string register_syntax = kAsmRegisterPrefix;
+ register_syntax += line[line_index + 1];
+
+ if (isdigit(line[line_index + 2]))
+ register_syntax += line[line_index + 2];
+
+ std::string reg_str;
+ reg_str += line[line_index + 1];
+
+ if (isdigit(line[line_index + 2]))
+ reg_str += line[line_index + 2];
+
+ // it ranges from r0 to r19
+ // something like r190 doesn't exist in the instruction set.
+ if (kOutputArch == CompilerKit::kPefArchPowerPC) {
+ if (isdigit(line[line_index + 3]) &&
+ isdigit(line[line_index + 2])) {
+ reg_str += line[line_index + 3];
+ detail::print_error(
+ "invalid register index, r" + reg_str +
+ "\nnote: The PowerPC accepts registers from r0 to r20.",
+ file);
+ throw std::runtime_error("invalid_register_index");
+ }
+ }
+
+ // finally cast to a size_t
+ std::size_t reg_index = strtol(reg_str.c_str(), nullptr, 10);
+
+ if (reg_index > kAsmRegisterLimit) {
+ detail::print_error("invalid register index, r" + reg_str,
+ file);
+ throw std::runtime_error("invalid_register_index");
+ }
+
+ kBytes.emplace_back(reg_index);
+ ++found_some;
+
+ if (kVerbose) {
+ kStdOut << "ppcasm: Register found: " << register_syntax << "\n";
+ kStdOut << "ppcasm: Register amount in instruction: "
+ << found_some << "\n";
+ }
+ }
+ }
+
+ // we're not in immediate addressing, reg to reg.
+ if (opcode64x0.fFunct7 != kAsmImmediate) {
+ // remember! register to register!
+ if (found_some == 1) {
+ detail::print_error(
+ "Unrecognized register found.\ntip: each ppcasm register "
+ "starts with 'r'.\nline: " +
+ line,
+ file);
+ throw std::runtime_error("not_a_register");
+ }
+ }
+
+ if (found_some < 1 && name != "ldw" && name != "lda" &&
+ name != "stw") {
+ detail::print_error(
+ "invalid combination of opcode and registers.\nline: " + line,
+ file);
+ throw std::runtime_error("invalid_comb_op_reg");
+ } else if (found_some == 1 && name == "add") {
+ detail::print_error(
+ "invalid combination of opcode and registers.\nline: " + line,
+ file);
+ throw std::runtime_error("invalid_comb_op_reg");
+ } else if (found_some == 1 && name == "dec") {
+ detail::print_error(
+ "invalid combination of opcode and registers.\nline: " + line,
+ file);
+ throw std::runtime_error("invalid_comb_op_reg");
+ }
+
+ if (found_some > 0 && name == "pop") {
+ detail::print_error(
+ "invalid combination for opcode 'pop'.\ntip: it expects "
+ "nothing.\nline: " +
+ line,
+ file);
+ throw std::runtime_error("invalid_comb_op_pop");
+ }
+ }
+ default:
+ break;
+ }
+
+ // try to fetch a number from the name
+ if (name == "stw" || name == "ldw" || name == "lda" || name == "sta") {
+ auto where_string = name;
+
+ // if we load something, we'd need it's symbol/literal
+ if (name == "stw" || name == "sta" || name == "ldw" || name == "lda" ||
+ name == "sta")
+ where_string = ",";
+
+ jump_label = line;
+
+ auto found_sym = false;
+
+ while (jump_label.find(where_string) != std::string::npos) {
+ jump_label = jump_label.substr(jump_label.find(where_string) +
+ where_string.size());
+
+ while (jump_label.find(" ") != std::string::npos) {
+ jump_label.erase(jump_label.find(" "), 1);
+ }
+
+ if (jump_label[0] != kAsmRegisterPrefix[0] &&
+ !isdigit(jump_label[1])) {
+ if (found_sym) {
+ detail::print_error(
+ "invalid combination of opcode and operands.\nhere -> " +
+ jump_label,
+ file);
+ throw std::runtime_error("invalid_comb_op_ops");
+ } else {
+ // death trap installed.
+ found_sym = true;
+ }
+ }
+ }
+
+ cpy_jump_label = jump_label;
+
+ // replace any spaces with $
+ if (jump_label[0] == ' ') {
+ while (jump_label.find(' ') != std::string::npos) {
+ if (isalnum(jump_label[0]) || isdigit(jump_label[0])) break;
+
+ jump_label.erase(jump_label.find(' '), 1);
+ }
+ }
+
+ if (!this->WriteNumber(0, jump_label)) {
+ // sta expects this: sta 0x000000, r0
+ if (name == "sta") {
+ detail::print_error(
+ "invalid combination of opcode and operands.\nHere ->" + line,
+ file);
+ throw std::runtime_error("invalid_comb_op_ops");
+ }
+ } else {
+ if (name == "sta" &&
+ cpy_jump_label.find("import ") != std::string::npos) {
+ detail::print_error("invalid usage import on 'sta', here: " + line,
+ file);
+ throw std::runtime_error("invalid_sta_usage");
+ }
+ }
+
+ goto asm_write_label;
+ }
+
+ // This is the case where we jump to a label, it is also used as a goto.
+ if (name == "lda" || name == "sta") {
+ asm_write_label:
+ if (cpy_jump_label.find('\n') != std::string::npos)
+ cpy_jump_label.erase(cpy_jump_label.find('\n'), 1);
+
+ if (cpy_jump_label.find("import") != std::string::npos) {
+ cpy_jump_label.erase(cpy_jump_label.find("import"), strlen("import"));
+
+ if (name == "sta") {
+ detail::print_error("import is not allowed on a sta operation.",
+ file);
+ throw std::runtime_error("import_sta_op");
+ } else {
+ goto asm_end_label_cpy;
+ }
+ }
+
+ if (name == "lda" || name == "sta") {
+ for (auto &label : kOriginLabel) {
+ if (cpy_jump_label == label.first) {
+ if (kVerbose) {
+ kStdOut << "ppcasm: Replace label " << cpy_jump_label
+ << " to address: " << label.second << std::endl;
+ }
+
+ CompilerKit::NumberCast64 num(label.second);
+
+ for (auto &num : num.number) {
+ kBytes.push_back(num);
+ }
+
+ goto asm_end_label_cpy;
+ }
+ }
+
+ if (cpy_jump_label[0] == '0') {
+ switch (cpy_jump_label[1]) {
+ case 'x':
+ case 'o':
+ case 'b':
+ if (this->WriteNumber(0, cpy_jump_label))
+ goto asm_end_label_cpy;
+
+ break;
+ default:
+ break;
+ }
+
+ if (isdigit(cpy_jump_label[0])) {
+ if (this->WriteNumber(0, cpy_jump_label)) goto asm_end_label_cpy;
+
+ break;
+ }
+ }
+ }
+
+ if (cpy_jump_label.size() < 1) {
+ detail::print_error("label is empty, can't jump on it.", file);
+ throw std::runtime_error("label_empty");
+ }
+
+ /// don't go any further if:
+ /// load word (ldw) or store word. (stw)
+
+ if (name == "ldw" ||
+ name == "stw")
+ break;
+
+ auto mld_reloc_str = std::to_string(cpy_jump_label.size());
+ mld_reloc_str += kUndefinedSymbol;
+ mld_reloc_str += cpy_jump_label;
+
+ bool ignore_back_slash = false;
+
+ for (auto &reloc_chr : mld_reloc_str) {
+ if (reloc_chr == '\\') {
+ ignore_back_slash = true;
+ continue;
+ }
+
+ if (ignore_back_slash) {
+ ignore_back_slash = false;
+ continue;
+ }
+
+ kBytes.push_back(reloc_chr);
+ }
+
+ kBytes.push_back('\0');
+ goto asm_end_label_cpy;
+ }
+
+ asm_end_label_cpy:
+ ++kOrigin;
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+// Last rev 13-1-24
diff --git a/makefile b/makefile
index 7b087e8..acbe0bc 100644
--- a/makefile
+++ b/makefile
@@ -15,6 +15,7 @@ else
LINK_CC=x86_64-w64-mingw32-g++ -std=c++20
endif
+WINRES=windres
LINK_SRC=Sources/link.cc
LINK_OUTPUT=Output/link.exe
LINK_ALT_OUTPUT=Output/64link.exe
@@ -26,9 +27,13 @@ PP_OUTPUT=Output/bpp.exe
SRC_COMMON=Sources/String.cc Sources/AsmKit.cc
+# C Compiler (PowerPC)
+64X0_CC_SRC=Sources/64x000-cc.cc $(SRC_COMMON)
+64X0_CC_OUTPUT=Output/64x000-cc.exe
+
# C Compiler
-CC_SRC=Sources/cc.cc $(SRC_COMMON)
-CC_OUTPUT=Output/cc.exe
+PPC_CC_SRC=Sources/ppc-cc.cc $(SRC_COMMON)
+PPC_CC_OUTPUT=Output/ppc-cc.exe
# 64x0 Assembler
ASM_SRC=Sources/64asm.cc $(SRC_COMMON)
@@ -38,6 +43,10 @@ ASM_OUTPUT=Output/64asm.exe
IASM_SRC=Sources/i64asm.cc $(SRC_COMMON)
IASM_OUTPUT=Output/i64asm.exe
+# PowerPC Assembler
+PPCASM_SRC=Sources/ppcasm.cc $(SRC_COMMON)
+PPCASM_OUTPUT=Output/ppcasm.exe
+
.PHONY: all
all: pre-processor compiler linker
@echo "make: done."
@@ -48,15 +57,20 @@ pre-processor:
.PHONY: compiler
compiler:
- windres i64asm.rsrc -O coff -o i64asm.obj
- windres 64asm.rsrc -O coff -o 64asm.obj
- $(LINK_CC) $(COMMON_INC) $(CC_SRC) -o $(CC_OUTPUT)
+ $(WINRES) i64asm.rsrc -O coff -o i64asm.obj
+ $(WINRES) 64asm.rsrc -O coff -o 64asm.obj
+ $(WINRES) ppcasm.rsrc -O coff -o ppcasm.obj
+ $(WINRES) 64x000-cc.rsrc -O coff -o 64x000-cc.obj
+ $(WINRES) ppc-cc.rsrc -O coff -o ppc-cc.obj
+ $(LINK_CC) $(COMMON_INC) 64x000-cc.obj $(64X0_CC_SRC) -o $(64X0_CC_OUTPUT)
+ $(LINK_CC) $(COMMON_INC) ppc-cc.obj $(PPC_CC_SRC) -o $(PPC_CC_OUTPUT)
$(LINK_CC) $(COMMON_INC) i64asm.obj $(IASM_SRC) -o $(IASM_OUTPUT)
$(LINK_CC) $(COMMON_INC) 64asm.obj $(ASM_SRC) -o $(ASM_OUTPUT)
+ $(LINK_CC) $(COMMON_INC) ppcasm.obj $(PPCASM_SRC) -o $(PPCASM_OUTPUT)
.PHONY: linker
linker:
- windres link.rsrc -O coff -o link.obj
+ $(WINRES) link.rsrc -O coff -o link.obj
$(LINK_CC) $(COMMON_INC) link.obj $(LINK_SRC) -o $(LINK_OUTPUT)
cp $(LINK_OUTPUT) $(LINK_ALT_OUTPUT)
cp $(LINK_OUTPUT) $(LINK_ALT_2_OUTPUT)
@@ -71,7 +85,8 @@ help:
.PHONY: clean
clean:
- rm -f $(CC_OUTPUT)
+ rm -f $(64X0_CC_OUTPUT)
+ rm -f $(PPC_CC_OUTPUT)
rm -f $(PP_OUTPUT)
rm -f $(ASM_OUTPUT)
rm -f $(IASM_OUTPUT)
diff --git a/ppc-cc.rsrc b/ppc-cc.rsrc
new file mode 100644
index 0000000..934bcca
--- /dev/null
+++ b/ppc-cc.rsrc
@@ -0,0 +1,25 @@
+#include "Headers/Version.hxx"
+
+1 VERSIONINFO
+FILEVERSION 1,0,0,0
+PRODUCTVERSION 1,0,0,0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "080904E4"
+ BEGIN
+ VALUE "CompanyName", "Mahrouss-Logic"
+ VALUE "FileDescription", "NewOS PowerPC C compiler."
+ VALUE "FileVersion", DIST_VERSION
+ VALUE "InternalName", "NewC"
+ VALUE "LegalCopyright", "Mahrouss-Logic"
+ VALUE "OriginalFilename", "ppc-cc.exe"
+ VALUE "ProductName", "NewC"
+ VALUE "ProductVersion", DIST_VERSION
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x809, 1252
+ END
+END \ No newline at end of file