diff options
| author | Amlal El Mahrouss <113760121+Amlal-ElMahrouss@users.noreply.github.com> | 2024-04-05 02:06:40 +0200 |
|---|---|---|
| committer | Amlal El Mahrouss <113760121+Amlal-ElMahrouss@users.noreply.github.com> | 2024-04-05 02:09:16 +0200 |
| commit | 4fd8405b79823c6ce8dcfc3cbe0da29b2a8f5bf2 (patch) | |
| tree | 7606ed7050a026c4a4d2c73b02e22f86540f138f | |
| parent | 0f9388e1c596c13ae3c317d81e7bcfe23626acc2 (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.rsrc | 25 | ||||
| -rw-r--r-- | Examples/ExampleCDialect.S | 21 | ||||
| -rw-r--r-- | Headers/AsmKit/Arch/powerpc.hpp | 73 | ||||
| -rw-r--r-- | Headers/AsmKit/AsmKit.hpp | 18 | ||||
| -rw-r--r-- | Sources/32asm.cc | 2 | ||||
| -rw-r--r-- | Sources/64asm.cc | 2 | ||||
| -rw-r--r-- | Sources/64x000-cc.cc (renamed from Sources/cc.cc) | 6 | ||||
| -rw-r--r-- | Sources/Deprecated/ccplus.cc | 2 | ||||
| -rw-r--r-- | Sources/bpp.cc | 2 | ||||
| -rw-r--r-- | Sources/i64asm.cc | 2 | ||||
| -rw-r--r-- | Sources/link.cc | 2 | ||||
| -rw-r--r-- | Sources/ppc-cc.cc | 1345 | ||||
| -rw-r--r-- | Sources/ppcasm.cc | 905 | ||||
| -rw-r--r-- | makefile | 29 | ||||
| -rw-r--r-- | ppc-cc.rsrc | 25 |
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 ® : 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 @@ -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 |
