diff options
| author | Amlal El Mahrouss <amlal.elmahrouss@icloud.com> | 2024-12-27 07:39:05 +0100 |
|---|---|---|
| committer | Amlal El Mahrouss <amlal.elmahrouss@icloud.com> | 2024-12-27 07:39:05 +0100 |
| commit | f61c814a0a95e98529c96361c992f1a8ea24688a (patch) | |
| tree | 0c5fcb7976f5753149e0b8cc3b974a318e013f61 /dev/LibCompiler | |
| parent | c2046f25120d8c39b36cb81459f3370c8a5f1fa3 (diff) | |
META: Refactor source code.
Signed-off-by: Amlal El Mahrouss <amlal.elmahrouss@icloud.com>
Diffstat (limited to 'dev/LibCompiler')
33 files changed, 14771 insertions, 0 deletions
diff --git a/dev/LibCompiler/AAL/AssemblyInterface.h b/dev/LibCompiler/AAL/AssemblyInterface.h new file mode 100644 index 0000000..245aee9 --- /dev/null +++ b/dev/LibCompiler/AAL/AssemblyInterface.h @@ -0,0 +1,227 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include <LibCompiler/Macros.h> +#include <LibCompiler/Defines.h> +#include <LibCompiler/NFC/String.h> + +#define ASSEMBLY_INTERFACE : public LibCompiler::AssemblyInterface + +namespace LibCompiler +{ + /// @brief Assembly to binary generator class. + /// @note This interface creates according to the CPU target of the child class. + class AssemblyInterface + { + public: + explicit AssemblyInterface() = default; + virtual ~AssemblyInterface() = default; + + TOOLCHAINKIT_COPY_DEFAULT(AssemblyInterface); + + /// @brief compile to object file. + /// @note Example C++ -> MASM -> AE object. + virtual Int32 CompileToFormat(std::string& src, Int32 arch) = 0; + }; + + /// @brief Simple assembly factory + class AssemblyFactory final + { + public: + explicit AssemblyFactory() = default; + ~AssemblyFactory() = default; + + TOOLCHAINKIT_COPY_DEFAULT(AssemblyFactory); + + public: + enum + { + kArchAMD64, + kArch32x0, + kArch64x0, + kArchRISCV, + kArchPowerPC, + kArchUnknown, + }; + + Int32 Compile(std::string& sourceFile, const Int32& arch) noexcept; + + void Mount(AssemblyInterface* mountPtr) noexcept; + AssemblyInterface* Unmount() noexcept; + + private: + AssemblyInterface* fMounted{nullptr}; + }; + + union NumberCastBase + { + NumberCastBase() = default; + ~NumberCastBase() = default; + }; + + union NumberCast64 final + { + NumberCast64() = default; + explicit NumberCast64(UInt64 raw) + : raw(raw) + { + } + + ~NumberCast64() + { + raw = 0; + } + + CharType number[8]; + UInt64 raw; + }; + + union NumberCast32 final + { + NumberCast32() = default; + explicit NumberCast32(UInt32 raw) + : raw(raw) + { + } + + ~NumberCast32() + { + raw = 0; + } + + CharType number[4]; + UInt32 raw; + }; + + union NumberCast16 final + { + NumberCast16() = default; + explicit NumberCast16(UInt16 raw) + : raw(raw) + { + } + + ~NumberCast16() + { + raw = 0; + } + + CharType number[2]; + UInt16 raw; + }; + + union NumberCast8 final + { + NumberCast8() = default; + explicit NumberCast8(UInt8 raw) + : raw(raw) + { + } + + ~NumberCast8() + { + raw = 0; + } + + CharType number; + UInt8 raw; + }; + + class EncoderInterface + { + public: + explicit EncoderInterface() = default; + virtual ~EncoderInterface() = default; + + TOOLCHAINKIT_COPY_DEFAULT(EncoderInterface); + + virtual std::string CheckLine(std::string& line, const std::string& file) = 0; + virtual bool WriteLine(std::string& line, const std::string& file) = 0; + virtual bool WriteNumber(const std::size_t& pos, std::string& from_what) = 0; + }; + +#ifdef __ASM_NEED_AMD64__ + + class EncoderAMD64 final : public EncoderInterface + { + public: + explicit EncoderAMD64() = default; + ~EncoderAMD64() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(EncoderAMD64); + + 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; + + virtual bool WriteNumber16(const std::size_t& pos, std::string& from_what); + virtual bool WriteNumber32(const std::size_t& pos, std::string& from_what); + virtual bool WriteNumber8(const std::size_t& pos, std::string& from_what); + }; + +#endif // __ASM_NEED_AMD64__ + +#ifdef __ASM_NEED_64x0__ + + class Encoder64x0 final : public EncoderInterface + { + public: + explicit Encoder64x0() = default; + ~Encoder64x0() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(Encoder64x0); + + 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_64x0__ + +#ifdef __ASM_NEED_32x0__ + + class Encoder32x0 final : public EncoderInterface + { + public: + explicit Encoder32x0() = default; + ~Encoder32x0() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(Encoder32x0); + + 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__ + +#ifdef __ASM_NEED_PPC__ + + class EncoderPowerPC final : public EncoderInterface + { + public: + explicit EncoderPowerPC() = default; + ~EncoderPowerPC() override = default; + + TOOLCHAINKIT_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 LibCompiler diff --git a/dev/LibCompiler/AAL/CPU/32x0.h b/dev/LibCompiler/AAL/CPU/32x0.h new file mode 100644 index 0000000..828ef8d --- /dev/null +++ b/dev/LibCompiler/AAL/CPU/32x0.h @@ -0,0 +1,97 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include <LibCompiler/Defines.h> + +// @brief 32x0 support. +// @file CPU/32x0.hpp + +#define kAsmOpcodeDecl(__NAME, __OPCODE, __FUNCT3, __FUNCT7) \ + {.fName = __NAME, \ + .fOpcode = __OPCODE, \ + .fFunct3 = __FUNCT3, \ + .fFunct7 = __FUNCT7}, + +#define kAsmImmediate 0x01 +#define kAsmSyscall 0x02 +#define kAsmJump 0x03 +#define kAsmNoArgs 0x04 + +#define kAsmByte 0 +#define kAsmHWord 1 +#define kAsmWord 2 + +struct CpuCode32x0 +{ + const char fName[32]; + uint8_t fOpcode; + uint8_t fSize; + uint8_t fFunct3; + uint8_t fFunct7; +}; + +#define kAsmDWordStr ".dword" /* 64 bit */ +#define kAsmWordStr ".word" /* 32-bit */ +#define kAsmHWordStr ".half" /* 16-bit */ +#define kAsmByteStr ".byte" /* 8-bit */ + +inline std::vector<CpuCode32x0> kOpcodes32x0 = { + kAsmOpcodeDecl("nop", 0b0100011, 0b000, kAsmNoArgs) // nothing to do. (1C) + kAsmOpcodeDecl("jmp", 0b1110011, 0b001, kAsmJump) // jump to branch (2C) + kAsmOpcodeDecl("mov", 0b0100011, 0b101, kAsmImmediate) // move registers (3C) + kAsmOpcodeDecl("psh", 0b0111011, 0b000, kAsmImmediate) // push to sp (2C) + kAsmOpcodeDecl("pop", 0b0111011, 0b001, kAsmImmediate) // pop from sp. (1C) + kAsmOpcodeDecl("lea", 0b0111011, 0b010, + kAsmImmediate) // setup stack and call, store address to CR (1C). + kAsmOpcodeDecl("ret", 0b0111011, 0b110, + kAsmImmediate) // return from procedure (2C). + kAsmOpcodeDecl("uc", 0b0111111, 0b000, kAsmSyscall) // user call (1C) + kAsmOpcodeDecl("kc", 0b0111111, 0b001, kAsmSyscall) // kernel call (1C) + kAsmOpcodeDecl("int", 0b0111111, 0b010, kAsmSyscall) // raise interrupt (1C) +}; + +// \brief 64x0 register prefix +// example: r32, r0 +// r32 -> sp +// r0 -> hw zero + +#define kAsmRegisterPrefix "r" +#define kAsmRegisterLimit 16 +#define kAsmPcRegister 17 +#define kAsmCrRegister 18 +#define kAsmSpRegister 5 + +/* return address register */ +#define kAsmRetRegister 19 + +///////////////////////////////////////////////////////////////////////////// + +// SYSTEM CALL ADDRESSING + +// | OPCODE | FUNCT3 | FUNCT7 | OFF | + +// IMMEDIATE ADDRESSING + +// | OPCODE | FUNCT3 | FUNCT7 | REG | OFF | +// | OPCODE | FUNCT3 | FUNCT7 | REG | OFF | REG | +// | OPCODE | FUNCT3 | FUNCT7 | REG | REG | OFF | + +// REG TO REG ADDRESSING + +// | OPCODE | FUNCT3 | FUNCT7 | REG | REG2 | + +//////////////////////////////// + +// LOAD/CALL INTERRUPTS + +// SET A HANDLER IN ADDRESS: TODO: find one +// DISABLE INTERRUPTS +// PROCESS INTERRUPT +// ENABLE INTERRUPTS + +//////////////////////////////// diff --git a/dev/LibCompiler/AAL/CPU/64x0.h b/dev/LibCompiler/AAL/CPU/64x0.h new file mode 100644 index 0000000..a057805 --- /dev/null +++ b/dev/LibCompiler/AAL/CPU/64x0.h @@ -0,0 +1,106 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include <LibCompiler/Defines.h> +#include <vector> + +// @brief 64x0 support. +// @file CPU/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 CpuOpcode64x0 +{ + const e64k_character_t fName[32]; + e64k_num_t fOpcode; + e64k_num_t fFunct3; + e64k_num_t fFunct7; +}; + +inline std::vector<CpuOpcode64x0> 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("sub", 0b0101011, 0b101, kAsmImmediate) + // add/sub with carry flag + kAsmOpcodeDecl("addc", 0b0101011, 0b110, kAsmImmediate) + kAsmOpcodeDecl("subc", 0b0101011, 0b111, kAsmImmediate) + kAsmOpcodeDecl("sc", 0b1110011, 0b00, kAsmSyscall)}; + +// \brief 64x0 register prefix +// example: r32, r0 +// r32 -> sp +// r0 -> hw zero + +#define kAsmFloatZeroRegister 0 +#define kAsmZeroRegister 0 + +#define kAsmRegisterPrefix "r" +#define kAsmRegisterLimit 30 +#define kAsmPcRegister 17 +#define kAsmCrRegister 18 +#define kAsmSpRegister 5 + +/* return address register */ +#define kAsmRetRegister 19 + +///////////////////////////////////////////////////////////////////////////// + +// SYSTEM CALL/JUMP ADDRESSING + +// | OPCODE | FUNCT3 | FUNCT7 | OFF | + +// IMMEDIATE ADDRESSING + +// | OPCODE | FUNCT3 | FUNCT7 | REG | OFF | +// | OPCODE | FUNCT3 | FUNCT7 | REG | OFF | REG | +// | OPCODE | FUNCT3 | FUNCT7 | REG | REG | OFF | + +// REG TO REG ADDRESSING + +// | OPCODE | FUNCT3 | FUNCT7 | REG | REG2 | + +//////////////////////////////// + +// LOAD/CALL INTERRUPTS + +// SET A HANDLER IN ADDRESS: +// DISABLE INTERRUPTS +// PROCESS INTERRUPT +// ENABLE INTERRUPTS + +//////////////////////////////// diff --git a/dev/LibCompiler/AAL/CPU/amd64.h b/dev/LibCompiler/AAL/CPU/amd64.h new file mode 100644 index 0000000..219968a --- /dev/null +++ b/dev/LibCompiler/AAL/CPU/amd64.h @@ -0,0 +1,56 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include <LibCompiler/Defines.h> + +// @brief AMD64 support. +// @file CPU/amd64.hpp + +#define kAsmOpcodeDecl(__NAME, __OPCODE) {.fName = __NAME, .fOpcode = __OPCODE}, + +typedef char i64_character_t; +typedef uint8_t i64_byte_t; +typedef uint16_t i64_hword_t; +typedef uint32_t i64_word_t; + +struct CpuOpcodeAMD64 +{ + std::string fName; + i64_byte_t fPrefixBytes[4]; + i64_hword_t fOpcode; + i64_hword_t fModReg; + i64_word_t fDisplacment; + i64_word_t fImmediate; +}; + +/// these two are edge cases +#define kAsmIntOpcode 0xCC +#define kasmIntOpcodeAlt 0xCD + +#define kAsmJumpOpcode 0x0F80 +#define kJumpLimit 30 +#define kJumpLimitStandard 0xE3 +#define kJumpLimitStandardLimit 0xEB + +inline std::vector<CpuOpcodeAMD64> kOpcodesAMD64 = { + kAsmOpcodeDecl("int", 0xCD) + kAsmOpcodeDecl("into", 0xCE) + kAsmOpcodeDecl("intd", 0xF1) + kAsmOpcodeDecl("int3", 0xC3) + kAsmOpcodeDecl("iret", 0xCF) + kAsmOpcodeDecl("retf", 0xCB) + kAsmOpcodeDecl("retn", 0xC3) + kAsmOpcodeDecl("ret", 0xC3) + kAsmOpcodeDecl("sti", 0xfb) + kAsmOpcodeDecl("cli", 0xfa) + kAsmOpcodeDecl("hlt", 0xf4) + kAsmOpcodeDecl("nop", 0x90) + kAsmOpcodeDecl("mov", 0x48) + kAsmOpcodeDecl("call", 0xFF)}; + +#define kAsmRegisterLimit 15 diff --git a/dev/LibCompiler/AAL/CPU/arm64.h b/dev/LibCompiler/AAL/CPU/arm64.h new file mode 100644 index 0000000..f2a14ce --- /dev/null +++ b/dev/LibCompiler/AAL/CPU/arm64.h @@ -0,0 +1,26 @@ +/* ------------------------------------------- + +Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include <LibCompiler/Defines.h> + +/// @brief ARM64 encoding support. +/// @file CPU/arm64.hpp + +struct CpuOpcodeArm64; + +/// @brief ARM64 opcode header. +struct CpuOpcodeArm64 final +{ + uint8_t fOpcode; // opcode + uint8_t fRegisterLeft; // left register index + uint8_t fRegisterRight; // right register index + bool fRegisterLeftHooked; + bool fRegisterRightHooked; + uint32_t fImmediateValue; // immediate 32-bit value + bool fImmediateValueHooked; +}; diff --git a/dev/LibCompiler/AAL/CPU/power64.h b/dev/LibCompiler/AAL/CPU/power64.h new file mode 100644 index 0000000..74e0966 --- /dev/null +++ b/dev/LibCompiler/AAL/CPU/power64.h @@ -0,0 +1,1929 @@ +/* ------------------------------------------- + + Some modifications are copyrighted under: + Theater Quality Incorporated + + Original author: + Apple Inc + +------------------------------------------- */ + +#pragma once + +#include <cstdint> + +/// @note Based of: +/// https://opensource.apple.com/source/cctools/cctools-750/as/ppc-opcode.h.auto.html + +/* + * These defines are use in the cpus field of the instructions. If the field + * is zero it can execute on all cpus. The defines are or'ed together. This + * information is used to set the cpusubtype in the resulting object file. + */ +#define CPU601 0x1 +#define IMPL64 0x2 +#define OPTIONAL 0x4 +#define VMX 0x8 +#define CPU970 0x10 /* added to OPTIONAL insts that the 970 has */ +#define CPUMAHROUSS 0x12 /* optional mahrouss insts. */ + +enum optype +{ + NONE, /* no operand */ + JBSR, /* jbsr pseudo op */ + PCREL, /* PC relative (branch offset) */ + BADDR, /* Branch address (sign extended absolute address) */ + D, /* 16 bit displacement */ + DS, /* 14 bit displacement (double word) */ + SI, /* signed 16 bit immediate */ + UI, /* unsigned 16 bit immediate */ + HI, /* high 16 bit immediate (with truncation) */ + GREG, /* general register */ + G0REG, /* general register r1-r31 or 0 */ + FREG, /* float register */ + VREG, /* vector register */ + SGREG, /* segment register */ + SPREG, /* special register (or 10 bit number, 5 bit halves reversed) */ + BCND, /* branch condition opcode */ + CRF, /* condition register field */ + CRFONLY, /* condition register field only no expression allowed */ + sh, /* 6 bit number (0 - 63) (sh field, split and reversed) */ + mb, /* 6 bit number (0 - 63) (mb field, mb5 || mb0:4 reversed) */ + NUM, /* number */ + SNUM, /* signed number */ + NUM0, /* number (where 1<<width is the same as 0) */ + MBE, /* mask defined by MB and ME fields */ + FXM, /* 8-bit mask with only one bit set */ + ZERO /* the number zero */ +}; + +struct op +{ + uint32_t offset : 5; + uint32_t width : 5; + enum optype type : 6; +}; + +struct CpuOpcodePPC +{ + uint32_t opcode; + const char* name; // c++ wants the string to be const, it makes sense here. + struct op ops[5]; + uint32_t cpus; +}; + +#define kOpcodePPCCount (1073U) + +inline CpuOpcodePPC kOpcodesPowerPC[] = { + {0x38000000, "addi", {{21, 5, GREG}, {16, 5, G0REG}, {0, 16, SI}}}, + {0x38000000, "li", {{21, 5, GREG}, {0, 16, SI}}}, + {0x3c000000, "addis", {{21, 5, GREG}, {16, 5, G0REG}, {0, 16, HI}}}, + {0x3c000000, "lis", {{21, 5, GREG}, {0, 16, HI}}}, + {0x30000000, "addic", {{21, 5, GREG}, {16, 5, GREG}, {0, 16, SI}}}, + {0x34000000, "addic.", {{21, 5, GREG}, {16, 5, GREG}, {0, 16, SI}}}, + {0x7c000214, "add", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000215, "add.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000614, "addo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000615, "addo.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000014, "addc", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000015, "addc.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000414, "addco", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000415, "addco.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000114, "adde", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000115, "adde.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000514, "addeo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000515, "addeo.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c0001d4, "addme", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c0001d5, "addme.", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c0005d4, "addmeo", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c0005d5, "addmeo.", {{21, 5, GREG}, {16, 5, GREG}}}, + + {0x7c000194, "addze", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c000195, "addze.", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c000594, "addzeo", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c000595, "addzeo.", {{21, 5, GREG}, {16, 5, GREG}}}, + + {0x70000000, "andi.", {{16, 5, GREG}, {21, 5, GREG}, {0, 16, UI}}}, + {0x74000000, "andis.", {{16, 5, GREG}, {21, 5, GREG}, {0, 16, UI}}}, + {0x7c000038, "and", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000039, "and.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000078, "andc", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000079, "andc.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + + {0x48000000, "b", {{2, 24, PCREL}}}, + {0x48000002, "ba", {{2, 24, BADDR}}}, + {0x48000001, "bl", {{2, 24, PCREL}}}, + {0x48000003, "bla", {{2, 24, BADDR}}}, + + {0x48000001, "jbsr", {{0, 0, JBSR}, {2, 24, PCREL}}}, + {0x48000000, "jmp", {{0, 0, JBSR}, {2, 24, PCREL}}}, + + {0x40000000, "bc", {{21, 5, NUM}, {16, 5, NUM}, {2, 14, PCREL}}}, + {0x40000002, "bca", {{21, 5, NUM}, {16, 5, NUM}, {2, 14, BADDR}}}, + {0x40000001, "bcl", {{21, 5, NUM}, {16, 5, NUM}, {2, 14, PCREL}}}, + {0x40000003, "bcla", {{21, 5, NUM}, {16, 5, NUM}, {2, 14, BADDR}}}, + + {0x4c000420, "bcctr", {{21, 5, NUM}, {16, 5, NUM}}}, + {0x4c000420, "bcctr", {{21, 5, NUM}, {16, 5, NUM}, {11, 2, NUM}}}, + {0x4c000421, "bcctrl", {{21, 5, NUM}, {16, 5, NUM}}}, + {0x4c000421, "bcctrl", {{21, 5, NUM}, {16, 5, NUM}, {11, 2, NUM}}}, + {0x4c000020, "bclr", {{21, 5, NUM}, {16, 5, NUM}}}, + {0x4c000020, "bclr", {{21, 5, NUM}, {16, 5, NUM}, {11, 2, NUM}}}, + {0x4c000021, "bclrl", {{21, 5, NUM}, {16, 5, NUM}}}, + {0x4c000021, "bclrl", {{21, 5, NUM}, {16, 5, NUM}, {11, 2, NUM}}}, + + /* Basic branch mnemonics (assember extended mnemonics) */ + /* { 0x42800000, "b", {{2,14,PCREL}} }, overlaps */ + /* { 0x42800001, "bl", {{2,14,PCREL}} }, overlaps */ + {0x41800000, "bt", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x41800001, "btl", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x40800000, "bf", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x40800001, "bfl", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x42000000, "bdnz", {{2, 14, PCREL}}}, + {0x42000001, "bdnzl", {{2, 14, PCREL}}}, + {0x41000000, "bdnzt", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x41000001, "bdnztl", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x40000000, "bdnzf", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x40000001, "bdnzfl", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x42400000, "bdz", {{2, 14, PCREL}}}, + {0x42400001, "bdzl", {{2, 14, PCREL}}}, + {0x41400000, "bdzt", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x41400001, "bdztl", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x40400000, "bdzf", {{16, 5, BCND}, {2, 14, PCREL}}}, + {0x40400001, "bdzfl", {{16, 5, BCND}, {2, 14, PCREL}}}, + + /* { 0x42800002, "ba", {{2,14,BADDR}} }, overlaps */ + /* { 0x42800003, "bla", {{2,14,BADDR}} }, overlaps */ + {0x41800002, "bta", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x41800003, "btla", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x40800002, "bfa", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x40800003, "bfla", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x42000002, "bdnza", {{2, 14, BADDR}}}, + {0x42000003, "bdnzla", {{2, 14, BADDR}}}, + {0x41000002, "bdnzta", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x41000003, "bdnztla", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x40000002, "bdnzfa", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x40000003, "bdnzfla", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x42400002, "bdza", {{2, 14, BADDR}}}, + {0x42400003, "bdzla", {{2, 14, BADDR}}}, + {0x41400002, "bdzta", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x41400003, "bdztla", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x40400002, "bdzfa", {{16, 5, BCND}, {2, 14, BADDR}}}, + {0x40400003, "bdzfla", {{16, 5, BCND}, {2, 14, BADDR}}}, + + { + 0x4e800020, + "blr", + }, + {0x4e800020, "blr", {{11, 2, NUM}}}, + { + 0x4e800021, + "blrl", + }, + {0x4e800021, "blrl", {{11, 2, NUM}}}, + {0x4d800020, "btlr", {{16, 5, BCND}}}, + {0x4d800020, "btlr", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4d800021, "btlrl", {{16, 5, BCND}}}, + {0x4d800021, "btlrl", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4c800020, "bflr", {{16, 5, BCND}}}, + {0x4c800020, "bflr", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4c800021, "bflrl", {{16, 5, BCND}}}, + {0x4c800021, "bflrl", {{16, 5, BCND}, {11, 2, NUM}}}, + { + 0x4e000020, + "bdnzlr", + }, + {0x4e000020, "bdnzlr", {{11, 2, NUM}}}, + { + 0x4e000021, + "bdnzlrl", + }, + {0x4e000021, "bdnzlrl", {{11, 2, NUM}}}, + {0x4d000020, "bdnztlr", {{16, 5, BCND}}}, + {0x4d000020, "bdnztlr", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4d000021, "bdnztlrl", {{16, 5, BCND}}}, + {0x4d000021, "bdnztlrl", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4c000020, "bdnzflr", {{16, 5, BCND}}}, + {0x4c000020, "bdnzflr", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4c000021, "bdnzflrl", {{16, 5, BCND}}}, + {0x4c000021, "bdnzflrl", {{16, 5, BCND}, {11, 2, NUM}}}, + { + 0x4e400020, + "bdzlr", + }, + {0x4e400020, "bdzlr", {{11, 2, NUM}}}, + { + 0x4e400021, + "bdzlrl", + }, + {0x4e400021, "bdzlrl", {{11, 2, NUM}}}, + {0x4d400020, "bdztlr", {{16, 5, BCND}}}, + {0x4d400020, "bdztlr", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4d400021, "bdztlrl", {{16, 5, BCND}}}, + {0x4d400021, "bdztlrl", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4c400020, "bdzflr", {{16, 5, BCND}}}, + {0x4c400020, "bdzflr", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4c400021, "bdzflrl", {{16, 5, BCND}}}, + {0x4c400021, "bdzflrl", {{16, 5, BCND}, {11, 2, NUM}}}, + + {0x4c000420, "bctr", {{21, 5, NUM}, {16, 5, NUM}}}, + { + 0x4e800420, + "bctr", + }, + {0x4e800420, "bctr", {{11, 2, NUM}}}, + {0x4c000421, "bctrl", {{21, 5, NUM}, {16, 5, NUM}}}, + { + 0x4e800421, + "bctrl", + }, + {0x4e800421, "bctrl", {{11, 2, NUM}}}, + {0x4d800420, "btctr", {{16, 5, BCND}}}, + {0x4d800420, "btctr", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4d800421, "btctrl", {{16, 5, BCND}}}, + {0x4d800421, "btctrl", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4c800420, "bfctr", {{16, 5, BCND}}}, + {0x4c800420, "bfctr", {{16, 5, BCND}, {11, 2, NUM}}}, + {0x4c800421, "bfctrl", {{16, 5, BCND}}}, + {0x4c800421, "bfctrl", {{16, 5, BCND}, {11, 2, NUM}}}, + + /* branch mnemonics incorporating conditions (assember extended mnemonics) + */ + {0x41800000, "blt", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41800000, "blt", {{2, 14, PCREL}}}, + {0x41800001, "bltl", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41800001, "bltl", {{2, 14, PCREL}}}, + {0x40810000, "ble", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40810000, "ble", {{2, 14, PCREL}}}, + {0x40810001, "blel", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40810001, "blel", {{2, 14, PCREL}}}, + {0x41820000, "beq", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41820000, "beq", {{2, 14, PCREL}}}, + {0x41820001, "beql", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41820001, "beql", {{2, 14, PCREL}}}, + {0x40800000, "bge", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40800000, "bge", {{2, 14, PCREL}}}, + {0x40800001, "bgel", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40800001, "bgel", {{2, 14, PCREL}}}, + {0x41810000, "bgt", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41810000, "bgt", {{2, 14, PCREL}}}, + {0x41810001, "bgtl", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41810001, "bgtl", {{2, 14, PCREL}}}, + {0x40800000, "bnl", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40800000, "bnl", {{2, 14, PCREL}}}, + {0x40800001, "bnll", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40800001, "bnll", {{2, 14, PCREL}}}, + {0x40820000, "bne", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40820000, "bne", {{2, 14, PCREL}}}, + {0x40820001, "bnel", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40820001, "bnel", {{2, 14, PCREL}}}, + {0x40810000, "bng", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40810000, "bng", {{2, 14, PCREL}}}, + {0x40810001, "bngl", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40810001, "bngl", {{2, 14, PCREL}}}, + {0x41830000, "bso", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41830000, "bso", {{2, 14, PCREL}}}, + {0x41830001, "bsol", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41830001, "bsol", {{2, 14, PCREL}}}, + {0x40830000, "bns", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40830000, "bns", {{2, 14, PCREL}}}, + {0x40830001, "bnsl", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40830001, "bnsl", {{2, 14, PCREL}}}, + {0x41830000, "bun", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41830000, "bun", {{2, 14, PCREL}}}, + {0x41830001, "bunl", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x41830001, "bunl", {{2, 14, PCREL}}}, + {0x40830000, "bnu", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40830000, "bnu", {{2, 14, PCREL}}}, + {0x40830001, "bnul", {{16, 5, CRF}, {2, 14, PCREL}}}, + {0x40830001, "bnul", {{2, 14, PCREL}}}, + + {0x41800002, "blta", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41800002, "blta", {{2, 14, BADDR}}}, + {0x41800003, "bltla", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41800003, "bltla", {{2, 14, BADDR}}}, + {0x40810002, "blea", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40810002, "blea", {{2, 14, BADDR}}}, + {0x40810003, "blela", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40810003, "blela", {{2, 14, BADDR}}}, + {0x41820002, "beqa", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41820002, "beqa", {{2, 14, BADDR}}}, + {0x41820003, "beqla", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41820003, "beqla", {{2, 14, BADDR}}}, + {0x40800002, "bgea", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40800002, "bgea", {{2, 14, BADDR}}}, + {0x40800003, "bgela", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40800003, "bgela", {{2, 14, BADDR}}}, + {0x41810002, "bgta", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41810002, "bgta", {{2, 14, BADDR}}}, + {0x41810003, "bgtla", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41810003, "bgtla", {{2, 14, BADDR}}}, + {0x40800002, "bnla", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40800002, "bnla", {{2, 14, BADDR}}}, + {0x40800003, "bnlla", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40800003, "bnlla", {{2, 14, BADDR}}}, + {0x40820002, "bnea", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40820002, "bnea", {{2, 14, BADDR}}}, + {0x40820003, "bnela", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40820003, "bnela", {{2, 14, BADDR}}}, + {0x40810002, "bnga", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40810002, "bnga", {{2, 14, BADDR}}}, + {0x40810003, "bngla", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40810003, "bngla", {{2, 14, BADDR}}}, + {0x41830002, "bsoa", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41830002, "bsoa", {{2, 14, BADDR}}}, + {0x41830003, "bsola", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41830003, "bsola", {{2, 14, BADDR}}}, + {0x40830002, "bnsa", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40830002, "bnsa", {{2, 14, BADDR}}}, + {0x40830003, "bnsla", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40830003, "bnsla", {{2, 14, BADDR}}}, + {0x41830002, "buna", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41830002, "buna", {{2, 14, BADDR}}}, + {0x41830003, "bunla", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x41830003, "bunla", {{2, 14, BADDR}}}, + {0x40830002, "bnua", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40830002, "bnua", {{2, 14, BADDR}}}, + {0x40830003, "bnula", {{16, 5, CRF}, {2, 14, BADDR}}}, + {0x40830003, "bnula", {{2, 14, BADDR}}}, + + {0x4d800020, "bltlr", {{16, 5, CRF}}}, + {0x4d800020, "bltlr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d800020, + "bltlr", + }, + {0x4d800021, "bltlrl", {{16, 5, CRF}}}, + {0x4d800021, "bltlrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d800021, + "bltlrl", + }, + {0x4c810020, "blelr", {{16, 5, CRF}}}, + {0x4c810020, "blelr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c810020, + "blelr", + }, + {0x4c810021, "blelrl", {{16, 5, CRF}}}, + {0x4c810021, "blelrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c810021, + "blelrl", + }, + {0x4d820020, "beqlr", {{16, 5, CRF}}}, + {0x4d820020, "beqlr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d820020, + "beqlr", + }, + {0x4d820021, "beqlrl", {{16, 5, CRF}}}, + {0x4d820021, "beqlrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d820021, + "beqlrl", + }, + {0x4c800020, "bgelr", {{16, 5, CRF}}}, + {0x4c800020, "bgelr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c800020, + "bgelr", + }, + {0x4c800021, "bgelrl", {{16, 5, CRF}}}, + {0x4c800021, "bgelrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c800021, + "bgelrl", + }, + {0x4d810020, "bgtlr", {{16, 5, CRF}}}, + {0x4d810020, "bgtlr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d810020, + "bgtlr", + }, + {0x4d810021, "bgtlrl", {{16, 5, CRF}}}, + {0x4d810021, "bgtlrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d810021, + "bgtlrl", + }, + {0x4c800020, "bnllr", {{16, 5, CRF}}}, + {0x4c800020, "bnllr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c800020, + "bnllr", + }, + {0x4c800021, "bnllrl", {{16, 5, CRF}}}, + {0x4c800021, "bnllrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c800021, + "bnllrl", + }, + {0x4c820020, "bnelr", {{16, 5, CRF}}}, + {0x4c820020, "bnelr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c820020, + "bnelr", + }, + {0x4c820021, "bnelrl", {{16, 5, CRF}}}, + {0x4c820021, "bnelrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c820021, + "bnelrl", + }, + {0x4c810020, "bnglr", {{16, 5, CRF}}}, + {0x4c810020, "bnglr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c810020, + "bnglr", + }, + {0x4c810021, "bnglrl", {{16, 5, CRF}}}, + {0x4c810021, "bnglrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c810021, + "bnglrl", + }, + {0x4d830020, "bsolr", {{16, 5, CRF}}}, + {0x4d830020, "bsolr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d830020, + "bsolr", + }, + {0x4d830021, "bsolrl", {{16, 5, CRF}}}, + {0x4d830021, "bsolrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d830021, + "bsolrl", + }, + {0x4c830020, "bnslr", {{16, 5, CRF}}}, + {0x4c830020, "bnslr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c830020, + "bnslr", + }, + {0x4c830021, "bnslrl", {{16, 5, CRF}}}, + {0x4c830021, "bnslrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c830021, + "bnslrl", + }, + {0x4d830020, "bunlr", {{16, 5, CRF}}}, + {0x4d830020, "bunlr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d830020, + "bunlr", + }, + {0x4d830021, "bunlrl", {{16, 5, CRF}}}, + {0x4d830021, "bunlrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d830021, + "bunlrl", + }, + {0x4c830020, "bnulr", {{16, 5, CRF}}}, + {0x4c830020, "bnulr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c830020, + "bnulr", + }, + {0x4c830021, "bnulrl", {{16, 5, CRF}}}, + {0x4c830021, "bnulrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c830021, + "bnulrl", + }, + + {0x4d800420, "bltctr", {{16, 5, CRF}}}, + {0x4d800420, "bltctr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d800420, + "bltctr", + }, + {0x4d800421, "bltctrl", {{16, 5, CRF}}}, + {0x4d800421, "bltctrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d800421, + "bltctrl", + }, + {0x4c810420, "blectr", {{16, 5, CRF}}}, + {0x4c810420, "blectr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c810420, + "blectr", + }, + {0x4c810421, "blectrl", {{16, 5, CRF}}}, + {0x4c810421, "blectrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c810421, + "blectrl", + }, + {0x4d820420, "beqctr", {{16, 5, CRF}}}, + {0x4d820420, "beqctr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d820420, + "beqctr", + }, + {0x4d820421, "beqctrl", {{16, 5, CRF}}}, + {0x4d820421, "beqctrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d820421, + "beqctrl", + }, + {0x4c800420, "bgectr", {{16, 5, CRF}}}, + {0x4c800420, "bgectr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c800420, + "bgectr", + }, + {0x4c800421, "bgectrl", {{16, 5, CRF}}}, + {0x4c800421, "bgectrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c800421, + "bgectrl", + }, + {0x4d810420, "bgtctr", {{16, 5, CRF}}}, + {0x4d810420, "bgtctr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d810420, + "bgtctr", + }, + {0x4d810421, "bgtctrl", {{16, 5, CRF}}}, + {0x4d810421, "bgtctrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d810421, + "bgtctrl", + }, + {0x4c800420, "bnlctr", {{16, 5, CRF}}}, + {0x4c800420, "bnlctr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c800420, + "bnlctr", + }, + {0x4c800421, "bnlctrl", {{16, 5, CRF}}}, + {0x4c800421, "bnlctrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c800421, + "bnlctrl", + }, + {0x4c820420, "bnectr", {{16, 5, CRF}}}, + {0x4c820420, "bnectr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c820420, + "bnectr", + }, + {0x4c820421, "bnectrl", {{16, 5, CRF}}}, + {0x4c820421, "bnectrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c820421, + "bnectrl", + }, + {0x4c810420, "bngctr", {{16, 5, CRF}}}, + {0x4c810420, "bngctr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c810420, + "bngctr", + }, + {0x4c810421, "bngctrl", {{16, 5, CRF}}}, + {0x4c810421, "bngctrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c810421, + "bngctrl", + }, + {0x4d830420, "bsoctr", {{16, 5, CRF}}}, + {0x4d830420, "bsoctr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d830420, + "bsoctr", + }, + {0x4d830421, "bsoctrl", {{16, 5, CRF}}}, + {0x4d830421, "bsoctrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d830421, + "bsoctrl", + }, + {0x4c830420, "bnsctr", {{16, 5, CRF}}}, + {0x4c830420, "bnsctr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c830420, + "bnsctr", + }, + {0x4c830421, "bnsctrl", {{16, 5, CRF}}}, + {0x4c830421, "bnsctrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c830421, + "bnsctrl", + }, + {0x4d830420, "bunctr", {{16, 5, CRF}}}, + {0x4d830420, "bunctr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d830420, + "bunctr", + }, + {0x4d830421, "bunctrl", {{16, 5, CRF}}}, + {0x4d830421, "bunctrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4d830421, + "bunctrl", + }, + {0x4c830420, "bnuctr", {{16, 5, CRF}}}, + {0x4c830420, "bnuctr", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c830420, + "bnuctr", + }, + {0x4c830421, "bnuctrl", {{16, 5, CRF}}}, + {0x4c830421, "bnuctrl", {{16, 5, CRF}, {11, 2, NUM}}}, + { + 0x4c830421, + "bnuctrl", + }, + + {0x2c000000, "cmpi", {{21, 5, CRFONLY}, {16, 5, GREG}, {0, 16, SI}}}, + {0x2c000000, + "cmpi", + {{21, 5, CRFONLY}, {21, 1, NUM}, {16, 5, GREG}, {0, 16, SI}}}, + {0x2c000000, "cmpi", {{23, 3, NUM}, {16, 5, GREG}, {0, 16, SI}}}, + {0x2c000000, + "cmpi", + {{23, 3, NUM}, {21, 1, NUM}, {16, 5, GREG}, {0, 16, SI}}}, + {0x2c000000, "cmpwi", {{16, 5, GREG}, {0, 16, SI}}}, + {0x2c000000, "cmpwi", {{21, 5, CRFONLY}, {16, 5, GREG}, {0, 16, SI}}}, + {0x2c000000, "cmpwi", {{23, 3, NUM}, {16, 5, GREG}, {0, 16, SI}}}, + {0x2c200000, "cmpdi", {{16, 5, GREG}, {0, 16, SI}}}, + {0x2c200000, "cmpdi", {{21, 5, CRFONLY}, {16, 5, GREG}, {0, 16, SI}}}, + {0x2c200000, "cmpdi", {{23, 3, NUM}, {16, 5, GREG}, {0, 16, SI}}}, + + {0x7c000000, "cmp", {{21, 5, CRFONLY}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000000, + "cmp", + {{21, 5, CRFONLY}, {21, 1, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000000, "cmp", {{23, 3, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000000, + "cmp", + {{23, 3, NUM}, {21, 1, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000000, "cmpw", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000000, "cmpw", {{21, 5, CRFONLY}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000000, "cmpw", {{23, 3, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c200000, "cmpd", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7c200000, "cmpd", {{21, 5, CRFONLY}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c200000, "cmpd", {{23, 3, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x28000000, "cmpli", {{21, 5, CRFONLY}, {16, 5, GREG}, {0, 16, UI}}}, + {0x28000000, + "cmpli", + {{21, 5, CRFONLY}, {21, 1, NUM}, {16, 5, GREG}, {0, 16, UI}}}, + {0x28000000, "cmpli", {{23, 3, NUM}, {16, 5, GREG}, {0, 16, UI}}}, + {0x28000000, + "cmpli", + {{23, 3, NUM}, {21, 1, NUM}, {16, 5, GREG}, {0, 16, UI}}}, + {0x28000000, "cmplwi", {{16, 5, GREG}, {0, 16, UI}}}, + {0x28000000, "cmplwi", {{21, 5, CRFONLY}, {16, 5, GREG}, {0, 16, UI}}}, + {0x28000000, "cmplwi", {{23, 3, NUM}, {16, 5, GREG}, {0, 16, UI}}}, + {0x28200000, "cmpldi", {{16, 5, GREG}, {0, 16, UI}}}, + {0x28200000, "cmpldi", {{21, 5, CRFONLY}, {16, 5, GREG}, {0, 16, UI}}}, + {0x28200000, "cmpldi", {{23, 3, NUM}, {16, 5, GREG}, {0, 16, UI}}}, + + {0x7c000040, "cmpl", {{21, 5, CRFONLY}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000040, + "cmpl", + {{21, 5, CRFONLY}, {21, 1, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000040, "cmpl", {{23, 3, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000040, + "cmpl", + {{23, 3, NUM}, {21, 1, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000040, "cmplw", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000040, "cmplw", {{21, 5, CRFONLY}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000040, "cmplw", {{23, 3, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c200040, "cmpld", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7c200040, "cmpld", {{21, 5, CRFONLY}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c200040, "cmpld", {{23, 3, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000034, "cntlzw", {{16, 5, GREG}, {21, 5, GREG}}}, + {0x7c000035, "cntlzw.", {{16, 5, GREG}, {21, 5, GREG}}}, + {0x7c000074, "cntlzd", {{16, 5, GREG}, {21, 5, GREG}}, IMPL64}, + {0x7c000075, "cntlzd.", {{16, 5, GREG}, {21, 5, GREG}}, IMPL64}, + + {0x4c000202, "crand", {{21, 5, NUM}, {16, 5, NUM}, {11, 5, NUM}}}, + {0x4c000102, "crandc", {{21, 5, NUM}, {16, 5, NUM}, {11, 5, NUM}}}, + {0x4c000242, "creqv", {{21, 5, NUM}, {16, 5, NUM}, {11, 5, NUM}}}, + {0x4c0001c2, "crnand", {{21, 5, NUM}, {16, 5, NUM}, {11, 5, NUM}}}, + {0x4c000042, "crnor", {{21, 5, NUM}, {16, 5, NUM}, {11, 5, NUM}}}, + {0x4c000382, "cror", {{21, 5, NUM}, {16, 5, NUM}, {11, 5, NUM}}}, + {0x4c000342, "crorc", {{21, 5, NUM}, {16, 5, NUM}, {11, 5, NUM}}}, + {0x4c000182, "crxor", {{21, 5, NUM}, {16, 5, NUM}, {11, 5, NUM}}}, + + {0x7c0003d2, "divd", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7c0003d3, + "divd.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c0007d2, + "divdo", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c0007d3, + "divdo.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + + {0x7c000392, + "divdu", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c000393, + "divdu.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c000792, + "divduo", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c000793, + "divduo.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + + {0x7c0003d6, "divw", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c0003d7, "divw.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c0007d6, "divwo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c0007d7, "divwo.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000396, "divwu", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000397, "divwu.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000796, "divwuo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000797, "divwuo.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000238, "eqv", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000239, "eqv.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000774, "extsb", {{16, 5, GREG}, {21, 5, GREG}}}, + {0x7c000775, "extsb.", {{16, 5, GREG}, {21, 5, GREG}}}, + {0x7c000734, "extsh", {{16, 5, GREG}, {21, 5, GREG}}}, + {0x7c000735, "extsh.", {{16, 5, GREG}, {21, 5, GREG}}}, + {0x7c0007b4, "extsw", {{16, 5, GREG}, {21, 5, GREG}}, IMPL64}, + {0x7c0007b5, "extsw.", {{16, 5, GREG}, {21, 5, GREG}}, IMPL64}, + + {0xfc00002a, "fadd", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xfc00002b, "fadd.", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xec00002a, "fadds", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xec00002b, "fadds.", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xfc000028, "fsub", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xfc000029, "fsub.", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xec000028, "fsubs", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xec000029, "fsubs.", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xfc000032, "fmul", {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}}}, + {0xfc000033, "fmul.", {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}}}, + {0xec000032, "fmuls", {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}}}, + {0xec000033, "fmuls.", {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}}}, + {0xfc000024, "fdiv", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xfc000025, "fdiv.", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xec000024, "fdivs", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xec000025, "fdivs.", {{21, 5, FREG}, {16, 5, FREG}, {11, 5, FREG}}}, + + {0xfc00003a, + "fmadd", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xfc00003b, + "fmadd.", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xec00003a, + "fmadds", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xec00003b, + "fmadds.", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xfc000038, + "fmsub", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xfc000039, + "fmsub.", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xec000038, + "fmsubs", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xec000039, + "fmsubs.", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xfc00003e, + "fnmadd", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xfc00003f, + "fnmadd.", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xec00003e, + "fnmadds", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xec00003f, + "fnmadds.", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xfc00003c, + "fnmsub", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xfc00003d, + "fnmsub.", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xec00003c, + "fnmsubs", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xec00003d, + "fnmsubs.", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + + {0xfc000090, "fmr", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000091, "fmr.", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000210, "fabs", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000211, "fabs.", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000050, "fneg", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000051, "fneg.", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000110, "fnabs", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000111, "fnabs.", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xec000030, "fres", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xec000031, "fres.", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000018, "frsp", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000019, "frsp.", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000034, "frsqrte", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc000035, "frsqrte.", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc00002e, + "fsel", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xfc00002f, + "fsel.", + {{21, 5, FREG}, {16, 5, FREG}, {6, 5, FREG}, {11, 5, FREG}}}, + {0xfc00002c, "fsqrt", {{21, 5, FREG}, {11, 5, FREG}}, OPTIONAL | CPU970}, + {0xfc00002d, "fsqrt.", {{21, 5, FREG}, {11, 5, FREG}}, OPTIONAL | CPU970}, + {0xec00002c, "fsqrts", {{21, 5, FREG}, {11, 5, FREG}}, OPTIONAL | CPU970}, + {0xec00002d, "fsqrts.", {{21, 5, FREG}, {11, 5, FREG}}, OPTIONAL | CPU970}, + {0xfc00065c, "fctid", {{21, 5, FREG}, {11, 5, FREG}}, IMPL64}, + {0xfc00065d, "fctid.", {{21, 5, FREG}, {11, 5, FREG}}, IMPL64}, + {0xfc00065e, "fctidz", {{21, 5, FREG}, {11, 5, FREG}}, IMPL64}, + {0xfc00065f, "fctidz.", {{21, 5, FREG}, {11, 5, FREG}}, IMPL64}, + {0xfc00001c, "fctiw", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc00001d, "fctiw.", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc00001e, "fctiwz", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc00001f, "fctiwz.", {{21, 5, FREG}, {11, 5, FREG}}}, + {0xfc00069c, "fcfid", {{21, 5, FREG}, {11, 5, FREG}}, IMPL64}, + {0xfc00069d, "fcfid.", {{21, 5, FREG}, {11, 5, FREG}}, IMPL64}, + + {0xfc000000, "fcmpu", {{21, 5, CRFONLY}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xfc000000, "fcmpu", {{23, 3, NUM}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xfc000040, "fcmpo", {{21, 5, CRFONLY}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xfc000040, "fcmpo", {{23, 3, NUM}, {16, 5, FREG}, {11, 5, FREG}}}, + {0xfc00048e, "mffs", {{21, 5, FREG}}}, + {0xfc00048f, "mffs.", {{21, 5, FREG}}}, + {0xfc000080, "mcrfs", {{21, 5, CRFONLY}, {18, 5, NUM}}}, + {0xfc000080, "mcrfs", {{23, 3, NUM}, {18, 5, NUM}}}, + {0xfc00010c, "mtfsfi", {{23, 3, NUM}, {12, 4, NUM}}}, + {0xfc00010d, "mtfsfi.", {{23, 3, NUM}, {12, 4, NUM}}}, + {0xfc00058e, "mtfsf", {{17, 8, NUM}, {11, 5, FREG}}}, + {0xfc00058f, "mtfsf.", {{17, 8, NUM}, {11, 5, FREG}}}, + {0xfc00008c, "mtfsb0", {{21, 5, NUM}}}, + {0xfc00008d, "mtfsb0.", {{21, 5, NUM}}}, + {0xfc00004c, "mtfsb1", {{21, 5, NUM}}}, + {0xfc00004d, "mtfsb1.", {{21, 5, NUM}}}, + + {0x88000000, "lbz", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0x7c0000ae, "lbzx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x8c000000, "lbzu", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0x7c0000ee, "lbzux", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0xa0000000, "lhz", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0x7c00022e, "lhzx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0xa4000000, "lhzu", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0x7c00026e, "lhzux", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0xa8000000, "lha", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0x7c0002ae, "lhax", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0xac000000, "lhau", {{21, 5, GREG}, {0, 16, D}, {16, 5, GREG}}}, + {0x7c0002ee, "lhaux", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x80000000, "lwz", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0x7c00002e, "lwzx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x84000000, "lwzu", {{21, 5, GREG}, {0, 16, D}, {16, 5, GREG}}}, + {0x7c00006e, "lwzux", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0xe8000002, "lwa", {{21, 5, GREG}, {2, 14, DS}, {16, 5, G0REG}}, IMPL64}, + {0x7c0002aa, + "lwax", + {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}, + IMPL64}, + {0x7c0002ea, + "lwaux", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0xe8000000, "ld", {{21, 5, GREG}, {2, 14, DS}, {16, 5, G0REG}}, IMPL64}, + {0x7c00002a, "ldx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}, IMPL64}, + {0xe8000001, "ldu", {{21, 5, GREG}, {2, 14, DS}, {16, 5, GREG}}, IMPL64}, + {0x7c00006a, "ldux", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + + {0xb8000000, "lmw", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0xbc000000, "stmw", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + + {0x7c00062c, "lhbrx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00042c, "lwbrx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00042a, "lswx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c000028, "lwarx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c0000a8, + "ldarx", + {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}, + IMPL64}, + + {0x7c00022a, + "lscbx", + {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}, + CPU601}, + {0x7c00022b, + "lscbx.", + {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}, + CPU601}, + + {0x7c0004aa, "lswi", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, NUM0}}}, + + {0xc0000000, "lfs", {{21, 5, FREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0xc4000000, "lfsu", {{21, 5, FREG}, {0, 16, D}, {16, 5, GREG}}}, + {0x7c00042e, "lfsx", {{21, 5, FREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00046e, "lfsux", {{21, 5, FREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0xc8000000, "lfd", {{21, 5, FREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0xcc000000, "lfdu", {{21, 5, FREG}, {0, 16, D}, {16, 5, GREG}}}, + {0x7c0004ae, "lfdx", {{21, 5, FREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c0004ee, "lfdux", {{21, 5, FREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x38000000, "la", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + + {0x4c000000, "mcrf", {{21, 5, CRFONLY}, {16, 5, CRFONLY}}}, + {0x4c000000, "mcrf", {{23, 3, NUM}, {18, 3, NUM}}}, + + {0x7c0002a6, "mfspr", {{21, 5, GREG}, {11, 10, SPREG}}}, + {0x7c0003a6, "mtspr", {{11, 10, SPREG}, {21, 5, GREG}}}, + {0x7c000120, "mtcrf", {{12, 8, FXM}, {21, 5, GREG}}}, + {0x7c000120, "mtocrf", {{12, 8, FXM}, {21, 5, GREG}}}, + {0x7c000400, "mcrxr", {{21, 5, CRFONLY}}}, + {0x7c000400, "mcrxr", {{23, 3, NUM}}}, + {0x7c000026, "mfcr", {{21, 5, GREG}}}, + {0x7c100026, "mfcr", {{21, 5, GREG}, {12, 8, FXM}}}, + {0x7c100026, "mfocrf", {{21, 5, GREG}, {12, 8, FXM}}}, + + /* Move to/from spr mnemonics (assember extended mnemonics) */ + {0x7c0102a6, "mfxer", {{21, 5, GREG}}}, + {0x7c0802a6, "mflr", {{21, 5, GREG}}}, + {0x7c0902a6, "mfctr", {{21, 5, GREG}}}, + {0x7c0103a6, "mtxer", {{21, 5, GREG}}}, + {0x7c0803a6, "mtlr", {{21, 5, GREG}}}, + {0x7c0903a6, "mtctr", {{21, 5, GREG}}}, + {0x7c0002a6, "mfmq", {{21, 5, GREG}}}, + {0x7c0502a6, "mfrtcl", {{21, 5, GREG}}}, + {0x7c0402a6, "mfrtcu", {{21, 5, GREG}}}, + {0x7c0003a6, "mtmq", {{21, 5, GREG}}}, + {0x7c1503a6, "mtrtcl", {{21, 5, GREG}}}, + {0x7c1403a6, "mtrtcu", {{21, 5, GREG}}}, + +#ifdef NRW_COMPILER + {0x7c0001d6, "mull", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c0001d7, "mull.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c0005d6, "mullo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c0005d7, "mullo.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, +#endif /* NRW_COMPILER */ + {0x7c0001d6, "mullw", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c0001d7, "mullw.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c0005d6, "mullwo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c0005d7, "mullwo.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000092, + "mulhd", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c000093, + "mulhd.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + +#ifdef NRW_COMPILER + {0x7c000096, "mulwd", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000097, "mulwd.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, +#endif /* NRW_COMPILER */ + {0x7c000096, "mulhw", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000097, "mulhw.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000012, + "mulhdu", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c000013, + "mulhdu.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + + {0x7c000016, "mulhwu", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000017, "mulhwu.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c0001d2, + "mulld", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c0001d3, + "mulld.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c0005d2, + "mulldo", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + {0x7c0005d3, + "mulldo.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + + {0x7c0003b8, "nand", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c0003b9, "nand.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + + {0x7c0000d0, "neg", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c0000d1, "neg.", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c0004d0, "nego", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c0004d1, "nego.", {{21, 5, GREG}, {16, 5, GREG}}}, + + {0x7c0000f8, "nor", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c0000f9, "nor.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + + /* Miscellaneous mnemonics (assember extended mnemonics) */ + { + 0x60000000, + "nop", + }, + + {0x60000000, "ori", {{16, 5, GREG}, {21, 5, GREG}, {0, 16, UI}}}, + {0x60000000, "ori", {{16, 5, ZERO}, {21, 5, ZERO}, {0, 16, ZERO}}}, + {0x64000000, "oris", {{16, 5, GREG}, {21, 5, GREG}, {0, 16, UI}}}, + {0x7c000378, "or", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000379, "or.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + + /// @brief Move register + {0x7c000378, "mr", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000338, "orc", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000339, "orc.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + + {0x78000000, + "rldicl", + {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}, {0, 0, mb}}, + IMPL64}, + {0x78000001, + "rldicl.", + {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}, {0, 0, mb}}, + IMPL64}, + {0x78000004, + "rldicr", + {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}, {0, 0, mb}}, + IMPL64}, + {0x78000005, + "rldicr.", + {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}, {0, 0, mb}}, + IMPL64}, + {0x78000008, + "rldic", + {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}, {0, 0, mb}}, + IMPL64}, + {0x78000009, + "rldic.", + {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}, {0, 0, mb}}, + IMPL64}, + {0x7800000c, + "rldimi", + {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}, {0, 0, mb}}, + IMPL64}, + {0x7800000d, + "rldimi.", + {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}, {0, 0, mb}}, + IMPL64}, + {0x78000010, + "rldcl", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}, {0, 0, mb}}, + IMPL64}, + {0x78000011, + "rldcl.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}, {0, 0, mb}}, + IMPL64}, + {0x78000012, + "rldcr", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}, {0, 0, mb}}, + IMPL64}, + {0x78000013, + "rldcr.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}, {0, 0, mb}}, + IMPL64}, + + {0x54000000, + "rlwinm", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM0}, {6, 5, MBE}, {1, 5, MBE}}}, + {0x54000001, + "rlwinm.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM0}, {6, 5, MBE}, {1, 5, MBE}}}, + {0x5c000000, + "rlwnm", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}, {6, 5, MBE}, {1, 5, MBE}}}, + {0x5c000001, + "rlwnm.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}, {6, 5, MBE}, {1, 5, MBE}}}, + {0x50000000, + "rlwimi", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM0}, {6, 5, MBE}, {1, 5, MBE}}}, + {0x50000001, + "rlwimi.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM0}, {6, 5, MBE}, {1, 5, MBE}}}, + + { + 0x44000002, + "sc", + }, + {0x4c000024, "rfid", {{0}}, IMPL64 | OPTIONAL}, + + {0x7c000030, "slw", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000031, "slw.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000036, "sld", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7c000037, "sld.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, IMPL64}, + + {0x7c000430, "srw", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000431, "srw.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000436, "srd", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7c000437, "srd.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, IMPL64}, + + {0x7c000670, "srawi", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}}, + {0x7c000671, "srawi.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}}, + {0x7c000674, "sradi", {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}}, IMPL64}, + {0x7c000675, "sradi.", {{16, 5, GREG}, {21, 5, GREG}, {0, 0, sh}}, IMPL64}, + + {0x7c000630, "sraw", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000631, "sraw.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000634, "srad", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7c000635, + "srad.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + + {0x98000000, "stb", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0x9c000000, "stbu", {{21, 5, GREG}, {0, 16, D}, {16, 5, GREG}}}, + {0x7c0001ae, "stbx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c0001ee, "stbux", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0xb0000000, "sth", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0xb4000000, "sthu", {{21, 5, GREG}, {0, 16, D}, {16, 5, GREG}}}, + {0x7c00032e, "sthx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00036e, "sthux", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x90000000, "stw", {{21, 5, GREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0x94000000, "stwu", {{21, 5, GREG}, {0, 16, D}, {16, 5, GREG}}}, + {0x7c00012e, "stwx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00016e, "stwux", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0xf8000000, "std", {{21, 5, GREG}, {2, 14, DS}, {16, 5, G0REG}}, IMPL64}, + {0xf8000001, "stdu", {{21, 5, GREG}, {2, 14, DS}, {16, 5, GREG}}, IMPL64}, + {0x7c00012a, + "stdx", + {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}, + IMPL64}, + {0x7c00016a, + "stdux", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + IMPL64}, + + {0x7c00072c, "sthbrx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00052c, "stwbrx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00052a, "stswx", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00012d, "stwcx.", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c0001ad, + "stdcx.", + {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}, + IMPL64}, + + {0x7c0005aa, "stswi", {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, NUM0}}}, + + { + 0x7c0007ae, + "stfiwx", + {{21, 5, FREG}, {16, 5, G0REG}, {11, 5, GREG}}, + }, + + {0xd0000000, "stfs", {{21, 5, FREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0xd4000000, "stfsu", {{21, 5, FREG}, {0, 16, D}, {16, 5, GREG}}}, + {0x7c00052e, "stfsx", {{21, 5, FREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00056e, "stfsux", {{21, 5, FREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0xd8000000, "stfd", {{21, 5, FREG}, {0, 16, D}, {16, 5, G0REG}}}, + {0xdc000000, "stfdu", {{21, 5, FREG}, {0, 16, D}, {16, 5, GREG}}}, + {0x7c0005ae, "stfdx", {{21, 5, FREG}, {16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c0005ee, "stfdux", {{21, 5, FREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x20000000, "subfic", {{21, 5, GREG}, {16, 5, GREG}, {0, 16, SI}}}, + {0x7c000050, "sub", {{21, 5, GREG}, {11, 5, GREG}, {16, 5, GREG}}}, + {0x7c000051, "sub.", {{21, 5, GREG}, {11, 5, GREG}, {16, 5, GREG}}}, + {0x7c000450, "subo", {{21, 5, GREG}, {11, 5, GREG}, {16, 5, GREG}}}, + {0x7c000451, "subo.", {{21, 5, GREG}, {11, 5, GREG}, {16, 5, GREG}}}, + {0x7c000050, "subf", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000051, "subf.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000450, "subfo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000451, "subfo.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000010, "subc", {{21, 5, GREG}, {11, 5, GREG}, {16, 5, GREG}}}, + {0x7c000011, "subc.", {{21, 5, GREG}, {11, 5, GREG}, {16, 5, GREG}}}, + {0x7c000410, "subco", {{21, 5, GREG}, {11, 5, GREG}, {16, 5, GREG}}}, + {0x7c000411, "subco.", {{21, 5, GREG}, {11, 5, GREG}, {16, 5, GREG}}}, + {0x7c000010, "subfc", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000011, "subfc.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000410, "subfco", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000411, "subfco.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c000110, "subfe", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000111, "subfe.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000510, "subfeo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000511, "subfeo.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}}, + + {0x7c0001d0, "subfme", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c0001d1, "subfme.", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c0005d0, "subfmeo", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c0005d1, "subfmeo.", {{21, 5, GREG}, {16, 5, GREG}}}, + + {0x7c000190, "subfze", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c000191, "subfze.", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c000590, "subfzeo", {{21, 5, GREG}, {16, 5, GREG}}}, + {0x7c000591, "subfzeo.", {{21, 5, GREG}, {16, 5, GREG}}}, + + { + 0x7c0004ac, + "sync", + }, + {0x7c0004ac, "sync", {{21, 2, NUM}}}, + { + 0x7c2004ac, + "lwsync", + }, + { + 0x7c4004ac, + "ptesync", + }, + + {0x08000000, "tdi", {{21, 5, NUM}, {16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x0a000000, "tdlti", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x0a800000, "tdlei", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x08800000, "tdeqi", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x09800000, "tdgei", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x09000000, "tdgti", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x09800000, "tdnli", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x0b000000, "tdnei", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x0a800000, "tdngi", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x08400000, "tdllti", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x08c00000, "tdllei", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x08a00000, "tdlgei", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x08200000, "tdlgti", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x08a00000, "tdlnli", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + {0x08c00000, "tdlngi", {{16, 5, GREG}, {0, 16, SI}}, IMPL64}, + + {0x7c000088, "td", {{21, 5, NUM}, {16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7e000088, "tdlt", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7e800088, "tdle", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7c800088, "tdeq", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7d800088, "tdge", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7d000088, "tdgt", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7d800088, "tdnl", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7f000088, "tdne", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7e800088, "tdng", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7c400088, "tdllt", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7cc00088, "tdlle", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7ca00088, "tdlge", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7c200088, "tdlgt", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7ca00088, "tdlnl", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + {0x7cc00088, "tdlng", {{16, 5, GREG}, {11, 5, GREG}}, IMPL64}, + + {0x0c000000, "twi", {{21, 5, NUM}, {16, 5, GREG}, {0, 16, SI}}}, + {0x0e000000, "twlti", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0e800000, "twlei", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0c800000, "tweqi", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0d800000, "twgei", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0d000000, "twgti", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0d800000, "twnli", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0f000000, "twnei", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0e800000, "twngi", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0c400000, "twllti", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0cc00000, "twllei", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0ca00000, "twlgei", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0c200000, "twlgti", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0ca00000, "twlnli", {{16, 5, GREG}, {0, 16, SI}}}, + {0x0cc00000, "twlngi", {{16, 5, GREG}, {0, 16, SI}}}, + + {0x7c000008, "tw", {{21, 5, NUM}, {16, 5, GREG}, {11, 5, GREG}}}, + {0x7c000008, "tw", {{21, 5, NUM}, {16, 5, ZERO}, {11, 5, ZERO}}}, + {0x7e000008, "twlt", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7e800008, "twle", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7c800008, "tweq", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7d800008, "twge", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7d000008, "twgt", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7d800008, "twnl", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7f000008, "twne", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7e800008, "twng", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7c400008, "twllt", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7cc00008, "twlle", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7ca00008, "twlge", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7c200008, "twlgt", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7ca00008, "twlnl", {{16, 5, GREG}, {11, 5, GREG}}}, + {0x7cc00008, "twlng", {{16, 5, GREG}, {11, 5, GREG}}}, + { + 0x7fe00008, + "trap", + }, + + {0x68000000, "xori", {{16, 5, GREG}, {21, 5, GREG}, {0, 16, UI}}}, + {0x6c000000, "xoris", {{16, 5, GREG}, {21, 5, GREG}, {0, 16, UI}}}, + {0x7c000278, "xor", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000279, "xor.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}}, + + /* Cache Management Instructions (from book II) */ + {0x7c0007ac, "icbi", {{16, 5, G0REG}, {11, 5, GREG}}}, + { + 0x4c00012c, + "isync", + }, + {0x7c00022c, "dcbt", {{16, 5, G0REG}, {11, 5, GREG}}}, + { + 0x7c00022c, + "dcbt", + {{16, 5, G0REG}, {11, 5, GREG}, {21, 4, NUM}}, + }, + {0x7c0001ec, "dcbtst", {{16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00022c, + "dcbt128", + {{16, 5, G0REG}, {11, 5, GREG}, {21, 4, NUM}}, + IMPL64 | OPTIONAL}, + {0x7c0007ec, "dcbz", {{16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c2007ec, "dcbzl", {{16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c2007ec, "dcbz128", {{16, 5, G0REG}, {11, 5, GREG}}, IMPL64 | OPTIONAL}, + {0x7c00006c, "dcbst", {{16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c0000ac, "dcbf", {{16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c00026c, + "eciwx", + {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}, + OPTIONAL | CPU970}, + {0x7c00036c, + "ecowx", + {{21, 5, GREG}, {16, 5, G0REG}, {11, 5, GREG}}, + OPTIONAL | CPU970}, + { + 0x7c0006ac, + "eieio", + }, + /* Instructions (from book III) */ + { + 0x4c000064, + "rfi", + }, + {0x7c000124, "mtmsr", {{21, 5, GREG}}}, + {0x7c000164, "mtmsrd", {{21, 5, GREG}}, IMPL64 | OPTIONAL}, + {0x7c000164, "mtmsrd", {{21, 5, GREG}, {16, 1, NUM}}, IMPL64 | OPTIONAL}, + {0x7c0000a6, "mfmsr", {{21, 5, GREG}}}, + {0x7c0005ec, "dcba", {{16, 5, G0REG}, {11, 5, GREG}}, OPTIONAL}, + {0x7c0003ac, "dcbi", {{16, 5, G0REG}, {11, 5, GREG}}}, + {0x7c0001a4, "mtsr", {{16, 4, SGREG}, {21, 5, GREG}}}, + {0x7c0004a6, "mfsr", {{21, 5, GREG}, {16, 4, SGREG}}}, + {0x7c0001e4, "mtsrin", {{21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000526, "mfsrin", {{21, 5, GREG}, {11, 5, GREG}}}, + {0x7c000364, "slbie", {{11, 5, GREG}}, IMPL64 | OPTIONAL}, + {0x7c0003e4, "slbia", {{0}}, IMPL64 | OPTIONAL}, + {0x7c000324, "slbmte", {{21, 5, GREG}, {11, 5, GREG}}, IMPL64 | OPTIONAL}, + {0x7c0006a6, "slbmfev", {{21, 5, GREG}, {11, 5, GREG}}, IMPL64 | OPTIONAL}, + {0x7c000726, "slbmfee", {{21, 5, GREG}, {11, 5, GREG}}, IMPL64 | OPTIONAL}, + {0x7c000264, "tlbie", {{11, 5, GREG}}, OPTIONAL | CPU970}, + {0x7c000264, + "tlbie", + {{11, 5, GREG}, {21, 1, NUM}}, + IMPL64 | OPTIONAL | CPU970}, + {0x7c000224, "tlbiel", {{11, 5, GREG}}, IMPL64 | OPTIONAL}, + {0x7c0002e4, "tlbia", {{0}}, OPTIONAL | CPU970}, + {0x7c00046c, "tlbsync", {{0}}, OPTIONAL | CPU970}, + {0x7c1c43a6, "mttbl", {{21, 5, GREG}}}, + {0x7c1d43a6, "mttbu", {{21, 5, GREG}}}, + {0x7c0002e6, "mftb", {{21, 5, GREG}, {11, 10, SPREG}}}, + {0x7c0c42e6, "mftb", {{21, 5, GREG}}}, + {0x7c0d42e6, "mftbu", {{21, 5, GREG}}}, + {0x00000200, "attn", {{11, 15, NUM}}, OPTIONAL | CPU970}, + + /* Instructions (from book IV) */ + {0x24000000, "dozi", {{21, 5, GREG}, {16, 5, GREG}, {0, 16, SI}}, CPU601}, + {0x7c000210, "doz", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000211, "doz.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000610, "dozo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000611, + "dozo.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c0002d0, "abs", {{21, 5, GREG}, {16, 5, GREG}}, CPU601}, + {0x7c0002d1, "abs.", {{21, 5, GREG}, {16, 5, GREG}}, CPU601}, + {0x7c0006d0, "abso", {{21, 5, GREG}, {16, 5, GREG}}, CPU601}, + {0x7c0006d1, "abso.", {{21, 5, GREG}, {16, 5, GREG}}, CPU601}, + + {0x7c0003d0, "nabs", {{21, 5, GREG}, {16, 5, GREG}}, CPU601}, + {0x7c0003d1, "nabs.", {{21, 5, GREG}, {16, 5, GREG}}, CPU601}, + {0x7c0007d0, "nabso", {{21, 5, GREG}, {16, 5, GREG}}, CPU601}, + {0x7c0007d1, "nabso.", {{21, 5, GREG}, {16, 5, GREG}}, CPU601}, + + {0x1c000000, "mulli", {{21, 5, GREG}, {16, 5, GREG}, {0, 16, SI}}}, + + {0x7c0000d6, "mul", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c0000d7, "mul.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c0004d6, "mulo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c0004d7, + "mulo.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c000296, "div", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000297, "div.", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000696, "divo", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000697, + "divo.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c0002d6, "divs", {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c0002d7, + "divs.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + CPU601}, + {0x7c0006d6, + "divso", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + CPU601}, + {0x7c0006d7, + "divso.", + {{21, 5, GREG}, {16, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x58000000, + "rlmi", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}, {6, 5, MBE}, {1, 5, MBE}}, + CPU601}, + {0x58000001, + "rlmi.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}, {6, 5, MBE}, {1, 5, MBE}}, + CPU601}, + + {0x7c000432, "rrib", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000433, + "rrib.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c00003a, + "maskg", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + {0x7c00003b, + "maskg.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c00043a, + "maskir", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + {0x7c00043b, + "maskir.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c000130, "slq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000131, "slq.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + + {0x7c000530, "srq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000531, "srq.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + + {0x7c000170, "sliq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, CPU601}, + {0x7c000171, "sliq.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, CPU601}, + + {0x7c000570, "sriq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, CPU601}, + {0x7c000571, "sriq.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, CPU601}, + + {0x7c0001f0, "slliq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, CPU601}, + {0x7c0001f1, + "slliq.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, + CPU601}, + + {0x7c0005f0, "srliq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, CPU601}, + {0x7c0005f1, + "srliq.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, + CPU601}, + + {0x7c0001b0, "sllq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c0001b1, + "sllq.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c0005b0, "srlq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c0005b1, + "srlq.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c000132, "sle", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000133, "sle.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + + {0x7c000532, "sre", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000533, "sre.", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + + {0x7c0001b2, "sleq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c0001b3, + "sleq.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c0005b2, "sreq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c0005b3, + "sreq.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c000770, "sraiq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, CPU601}, + {0x7c000771, + "sraiq.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, NUM}}, + CPU601}, + + {0x7c000730, "sraq", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000731, + "sraq.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + {0x7c000732, "srea", {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, CPU601}, + {0x7c000733, + "srea.", + {{16, 5, GREG}, {21, 5, GREG}, {11, 5, GREG}}, + CPU601}, + + /* Added from the POWER 601 book */ + {0x7c000426, "clcs", {{21, 5, GREG}, {16, 5, GREG}}, CPU601}, + + /* Added from the POWER 603 book. + * These are really 603 specific instructions but we mark them as OPTIONAL + * so that the -force_cpusubtype_ALL flag as to be used. This makes it so + * only 601 instructions will cause the cputype to be set to other an ALL. + */ + {0x7c0007a4, "tlbld", {{11, 5, GREG}}, OPTIONAL}, + {0x7c0007e4, "tlbli", {{11, 5, GREG}}, OPTIONAL}, + + /* VMX Instructions */ + {0x7c00000e, "lvebx", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + {0x7c00004e, "lvehx", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + {0x7c00008e, "lvewx", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + {0x7c0000ce, "lvx", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + {0x7c0002ce, "lvxl", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + + {0x7c00010e, "stvebx", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + {0x7c00014e, "stvehx", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + {0x7c00018e, "stvewx", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + {0x7c0001ce, "stvx", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + {0x7c0003ce, "stvxl", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + + {0x7c00000c, "lvsl", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + {0x7c00004c, "lvsr", {{21, 5, VREG}, {16, 5, G0REG}, {11, 5, GREG}}, VMX}, + + {0x10000644, "mtvscr", {{11, 5, VREG}}, VMX}, + {0x10000604, "mfvscr", {{21, 5, VREG}}, VMX}, + + {0x7c0002ac, "dst", {{16, 5, GREG}, {11, 5, GREG}, {21, 2, NUM}}, VMX}, + {0x7e0002ac, "dstt", {{16, 5, GREG}, {11, 5, GREG}, {21, 2, NUM}}, VMX}, + {0x7c0002ec, "dstst", {{16, 5, GREG}, {11, 5, GREG}, {21, 2, NUM}}, VMX}, + {0x7e0002ec, "dststt", {{16, 5, GREG}, {11, 5, GREG}, {21, 2, NUM}}, VMX}, + {0x7c00066c, "dss", {{21, 2, NUM}}, VMX}, + {0x7e00066c, "dssall", {{0}}, VMX}, + + {0x10000000, "vaddubm", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000200, "vaddubs", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000300, "vaddsbs", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000040, "vadduhm", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000240, "vadduhs", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000340, "vaddshs", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000080, "vadduwm", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000280, "vadduws", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000380, "vaddsws", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000000a, "vaddfp", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000180, "vaddcuw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000400, "vsububm", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000600, "vsububs", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000700, "vsubsbs", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000440, "vsubuhm", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000640, "vsubuhs", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000740, "vsubshs", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000480, "vsubuwm", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000680, "vsubuws", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000780, "vsubsws", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000004a, "vsubfp", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000580, "vsubcuw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000008, "vmuloub", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000108, "vmulosb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000048, "vmulouh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000148, "vmulosh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000208, "vmuleub", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000308, "vmulesb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000248, "vmuleuh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000348, "vmulesh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000020, + "vmhaddshs", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + {0x10000021, + "vmhraddshs", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + {0x10000022, + "vmladduhm", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + {0x1000002e, + "vmaddfp", + {{21, 5, VREG}, {16, 5, VREG}, {6, 5, VREG}, {11, 5, VREG}}, + VMX}, + + {0x10000024, + "vmsumubm", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + {0x10000025, + "vmsummbm", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + {0x10000026, + "vmsumuhm", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + {0x10000027, + "vmsumuhs", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + {0x10000028, + "vmsumshm", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + {0x10000029, + "vmsumshs", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + + {0x10000788, "vsumsws", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000688, + "vsum2sws", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + + {0x10000608, + "vsum4ubs", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000708, + "vsum4sbs", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000648, + "vsum4shs", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + + {0x10000402, "vavgub", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000442, "vavguh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000482, "vavguw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000502, "vavgsb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000542, "vavgsh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000582, "vavgsw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000404, "vand", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000484, "vor", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100004c4, "vxor", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000444, "vandc", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000504, "vnor", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000004, "vrlb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000044, "vrlh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000084, "vrlw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000104, "vslb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000144, "vslh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000184, "vslw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100001c4, "vsl", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000204, "vsrb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000304, "vsrab", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000244, "vsrh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000344, "vsrah", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000284, "vsrw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000384, "vsraw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100002c4, "vsr", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000206, + "vcmpgtub", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000606, + "vcmpgtub.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000306, + "vcmpgtsb", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000706, + "vcmpgtsb.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000246, + "vcmpgtuh", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000646, + "vcmpgtuh.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000346, + "vcmpgtsh", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000746, + "vcmpgtsh.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000286, + "vcmpgtuw", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000686, + "vcmpgtuw.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000386, + "vcmpgtsw", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000786, + "vcmpgtsw.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x100002c6, + "vcmpgtfp", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x100006c6, + "vcmpgtfp.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + + {0x10000006, + "vcmpequb", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000406, + "vcmpequb.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000046, + "vcmpequh", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000446, + "vcmpequh.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000086, + "vcmpequw", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x10000486, + "vcmpequw.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x100000c6, + "vcmpeqfp", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x100004c6, + "vcmpeqfp.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + + {0x100001c6, + "vcmpgefp", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + {0x100005c6, + "vcmpgefp.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + + {0x100003c6, "vcmpbfp", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100007c6, + "vcmpbfp.", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, + VMX}, + + {0x1000002a, + "vsel", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + + {0x1000000e, "vpkuhum", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000008e, "vpkuhus", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000010e, "vpkshus", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000018e, "vpkshss", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000004e, "vpkuwum", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100000ce, "vpkuwus", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000014e, "vpkswus", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100001ce, "vpkswss", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000030e, "vpkpx", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x1000020e, "vupkhsb", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000024e, "vupkhsh", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000034e, "vupkhpx", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x1000028e, "vupklsb", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100002ce, "vupklsh", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100003ce, "vupklpx", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x1000000c, "vmrghb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000004c, "vmrghh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000008c, "vmrghw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x1000010c, "vmrglb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000014c, "vmrglh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000018c, "vmrglw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x1000020c, "vspltb", {{21, 5, VREG}, {11, 5, VREG}, {16, 5, NUM}}, VMX}, + {0x1000024c, "vsplth", {{21, 5, VREG}, {11, 5, VREG}, {16, 5, NUM}}, VMX}, + {0x1000028c, "vspltw", {{21, 5, VREG}, {11, 5, VREG}, {16, 5, NUM}}, VMX}, + + {0x1000030c, "vspltisb", {{21, 5, VREG}, {16, 5, SNUM}}, VMX}, + {0x1000034c, "vspltish", {{21, 5, VREG}, {16, 5, SNUM}}, VMX}, + {0x1000038c, "vspltisw", {{21, 5, VREG}, {16, 5, SNUM}}, VMX}, + + {0x1000002b, + "vperm", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 5, VREG}}, + VMX}, + + {0x1000002c, + "vsldoi", + {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}, {6, 4, NUM}}, + VMX}, + + {0x1000040c, "vslo", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000044c, "vsro", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000002, "vmaxub", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000102, "vmaxsb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000042, "vmaxuh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000142, "vmaxsh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000082, "vmaxuw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000182, "vmaxsw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000040a, "vmaxfp", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x10000202, "vminub", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000302, "vminsb", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000242, "vminuh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000342, "vminsh", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000282, "vminuw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x10000382, "vminsw", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000044a, "vminfp", {{21, 5, VREG}, {16, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x1000010a, "vrefp", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000014a, "vrsqrtefp", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100001ca, "vlogefp", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000018a, "vexptefp", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x1000002f, + "vnmsubfp", + {{21, 5, VREG}, {16, 5, VREG}, {6, 5, VREG}, {11, 5, VREG}}, + VMX}, + + {0x1000020a, "vrfin", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000024a, "vrfiz", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x1000028a, "vrfip", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + {0x100002ca, "vrfim", {{21, 5, VREG}, {11, 5, VREG}}, VMX}, + + {0x1000038a, "vctuxs", {{21, 5, VREG}, {11, 5, VREG}, {16, 5, NUM}}, VMX}, + {0x100003ca, "vctsxs", {{21, 5, VREG}, {11, 5, VREG}, {16, 5, NUM}}, VMX}, + + {0x1000030a, "vcfux", {{21, 5, VREG}, {11, 5, VREG}, {16, 5, NUM}}, VMX}, + {0x1000034a, "vcfsx", {{21, 5, VREG}, {11, 5, VREG}, {16, 5, NUM}}, VMX}, + + {0, ""} /* end of table marker */ +}; + +#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/dev/LibCompiler/Defines.h b/dev/LibCompiler/Defines.h new file mode 100644 index 0000000..ceb6c45 --- /dev/null +++ b/dev/LibCompiler/Defines.h @@ -0,0 +1,172 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#ifndef __TOOLCHAINKIT_DEFINES_H__ +#define __TOOLCHAINKIT_DEFINES_H__ + +#ifndef Yes +#define Yes true +#endif // ifndef Yes + +#ifndef No +#define No false +#endif // ifndef No + +#ifndef YES +#define YES true +#endif // ifndef YES + +#ifndef NO +#define NO false +#endif // ifndef NO + +#define SizeType size_t + +#define VoidPtr void* +#define voidPtr VoidPtr + +#define UIntPtr uintptr_t + +#define Int64 int64_t +#define UInt64 uint64_t + +#define Int32 int +#define UInt32 unsigned + +#define Bool bool + +#define Int16 int16_t +#define UInt16 uint16_t + +#define Int8 int8_t +#define UInt8 uint8_t + +#define CharType char +#define Boolean bool + +#include <filesystem> +#include <cstdint> +#include <cassert> +#include <cstring> +#include <iostream> +#include <utility> +#include <cctype> +#include <cstdio> +#include <fstream> +#include <memory> +#include <string> +#include <vector> +#include <vector> +#include <new> + +#define nullPtr std::nullptr_t + +#define MUST_PASS(E) assert(E) + +#ifndef __FORCE_STRLEN +#define __FORCE_STRLEN 1 + +#define string_length(len) strlen(len) +#endif + +#ifndef __FORCE_MEMCPY +#define __FORCE_MEMCPY 1 + +#define rt_copy_memory(dst, src, len) memcpy(dst, src, len) +#endif + +#define TOOLCHAINKIT_COPY_DELETE(KLASS) \ + KLASS& operator=(const KLASS&) = delete; \ + KLASS(const KLASS&) = delete; + +#define TOOLCHAINKIT_COPY_DEFAULT(KLASS) \ + KLASS& operator=(const KLASS&) = default; \ + KLASS(const KLASS&) = default; + +#define TOOLCHAINKIT_MOVE_DELETE(KLASS) \ + KLASS& operator=(KLASS&&) = delete; \ + KLASS(KLASS&&) = delete; + +#define TOOLCHAINKIT_MOVE_DEFAULT(KLASS) \ + KLASS& operator=(KLASS&&) = default; \ + KLASS(KLASS&&) = default; + +#define TK_IMPORT_C extern "C" +#define TK_IMPORT extern + +#include <ctime> +#include <fstream> +#include <string> +#include <vector> + +namespace LibCompiler +{ + inline constexpr int kBaseYear = 1900; + + typedef std::string String; + + inline String current_date() noexcept + { + auto time_data = time(nullptr); + auto time_struct = gmtime(&time_data); + + String fmt = std::to_string(kBaseYear + time_struct->tm_year); + + fmt += "-"; + fmt += std::to_string(time_struct->tm_mon + 1); + fmt += "-"; + fmt += std::to_string(time_struct->tm_mday); + + return fmt; + } + + inline bool to_str(CharType* str, Int32 limit, Int32 base) noexcept + { + if (limit == 0) + return false; + + Int32 copy_limit = limit; + Int32 cnt = 0; + Int32 ret = base; + + while (limit != 1) + { + ret = ret % 10; + str[cnt] = ret; + + ++cnt; + --limit; + --ret; + } + + str[copy_limit] = '\0'; + return true; + } + + using String = std::basic_string<CharType>; +} // namespace LibCompiler + +#define PACKED __attribute__((packed)) + +typedef char char_type; + +#define kObjectFileExt ".obj" +#define kBinaryFileExt ".bin" + +#define kAsmFileExts \ + { \ + ".64x", ".32x", ".masm", ".s", ".S", ".asm", ".x64" \ + } + +#define kAsmFileExtsMax 7 + +#define TOOLCHAINKIT_MODULE(name) extern "C" int name(int argc, char** argv) + +#ifdef MSVC +#pragma scalar_storage_order big - endian +#endif // ifdef MSVC + +#endif /* ifndef __TOOLCHAINKIT_DEFINES_H__ */ diff --git a/dev/LibCompiler/Macros.h b/dev/LibCompiler/Macros.h new file mode 100644 index 0000000..91ad8de --- /dev/null +++ b/dev/LibCompiler/Macros.h @@ -0,0 +1,33 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +/// @brief provide support for Macros.hpp header. + +#ifndef _MACROS_H_ +#define _MACROS_H_ + +#define TOOLCHAINKIT_COPY_DELETE(KLASS) \ + KLASS& operator=(const KLASS&) = delete; \ + KLASS(const KLASS&) = delete; + +#define TOOLCHAINKIT_COPY_DEFAULT(KLASS) \ + KLASS& operator=(const KLASS&) = default; \ + KLASS(const KLASS&) = default; + +#define TOOLCHAINKIT_MOVE_DELETE(KLASS) \ + KLASS& operator=(KLASS&&) = delete; \ + KLASS(KLASS&&) = delete; + +#define TOOLCHAINKIT_MOVE_DEFAULT(KLASS) \ + KLASS& operator=(KLASS&&) = default; \ + KLASS(KLASS&&) = default; + +/// @note xxxx is the error placeholder, in hexadecimal. +#define TOOLCHAINKIT_ERROR_PREFIX_CXX "CXXxxxx" +#define TOOLCHAINKIT_ERROR_PREFIX_CL "CLxxxx" +#define TOOLCHAINKIT_ERROR_PREFIX_ASM "ASMxxxx" + +#endif /* ifndef _MACROS_H_ */ diff --git a/dev/LibCompiler/NFC/AE.h b/dev/LibCompiler/NFC/AE.h new file mode 100644 index 0000000..962a7c3 --- /dev/null +++ b/dev/LibCompiler/NFC/AE.h @@ -0,0 +1,143 @@ +/* + * ======================================================== + * + * LibCompiler + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include <LibCompiler/Defines.h> + +#define kAEMag0 'A' +#define kAEMag1 'E' + +#define kAESymbolLen (255) +#define kAEPad (8) +#define kAEMagLen (2) +#define kAENullType (0x00) + +// Advanced Executable File Format for MetroLink. +// Reloctable by offset is the default strategy. +// You can also relocate at runtime but that's up to the operating system +// loader. + +namespace LibCompiler +{ + // @brief Advanced Executable Header + // One thing to keep in mind. + // This object format, is reloctable. + typedef struct AEHeader final + { + CharType fMagic[kAEMagLen]; + CharType fArch; + CharType fSubArch; + SizeType fCount; + CharType fSize; + SizeType fStartCode; + SizeType fCodeSize; + CharType fPad[kAEPad]; + } PACKED AEHeader, *AEHeaderPtr; + + // @brief Advanced Executable Record. + // Could be data, code or bss. + // fKind must be filled with PEF fields. + + typedef struct AERecordHeader final + { + CharType fName[kAESymbolLen]; + SizeType fKind; + SizeType fSize; + SizeType fFlags; + UIntPtr fOffset; + CharType fPad[kAEPad]; + } PACKED AERecordHeader, *AERecordHeaderPtr; + + enum + { + kKindRelocationByOffset = 0x23f, + kKindRelocationAtRuntime = 0x34f, + }; +} // namespace LibCompiler + +// provide operator<< for AE + +inline std::ofstream& operator<<(std::ofstream& fp, LibCompiler::AEHeader& container) +{ + fp.write((char*)&container, sizeof(LibCompiler::AEHeader)); + + return fp; +} + +inline std::ofstream& operator<<(std::ofstream& fp, + LibCompiler::AERecordHeader& container) +{ + fp.write((char*)&container, sizeof(LibCompiler::AERecordHeader)); + + return fp; +} + +inline std::ifstream& operator>>(std::ifstream& fp, LibCompiler::AEHeader& container) +{ + fp.read((char*)&container, sizeof(LibCompiler::AEHeader)); + return fp; +} + +inline std::ifstream& operator>>(std::ifstream& fp, + LibCompiler::AERecordHeader& container) +{ + fp.read((char*)&container, sizeof(LibCompiler::AERecordHeader)); + return fp; +} + +namespace LibCompiler::Utils +{ + /** + * @brief AE Reader protocol + * + */ + class AEReadableProtocol final + { + public: + std::ifstream FP; + + public: + explicit AEReadableProtocol() = default; + ~AEReadableProtocol() = default; + + TOOLCHAINKIT_COPY_DELETE(AEReadableProtocol); + + /** + * @brief Read AE Record headers. + * + * @param raw the containing buffer + * @param sz it's size (1 = one AERecordHeader, 2 two AERecordHeader(s)) + * @return AERecordHeaderPtr + */ + AERecordHeaderPtr Read(char* raw, std::size_t sz) + { + if (!raw) + return nullptr; + + return this->_Read<AERecordHeader>(raw, sz * sizeof(AERecordHeader)); + } + + private: + /** + * @brief Implementation of Read for raw classes. + * + * @tparam TypeClass The class to read. + * @param raw the buffer + * @param sz the size + * @return TypeClass* the returning class. + */ + template <typename TypeClass> + TypeClass* _Read(char* raw, std::size_t sz) + { + FP.read(raw, std::streamsize(sz)); + return reinterpret_cast<TypeClass*>(raw); + } + }; +} // namespace LibCompiler::Utils diff --git a/dev/LibCompiler/NFC/ErrorID.h b/dev/LibCompiler/NFC/ErrorID.h new file mode 100644 index 0000000..e41410e --- /dev/null +++ b/dev/LibCompiler/NFC/ErrorID.h @@ -0,0 +1,22 @@ +/* + * ======================================================== + * + * LibCompiler + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include <LibCompiler/Defines.h> +#include <LibCompiler/NFC/ErrorOr.h> + +#define TOOLCHAINKIT_EXEC_ERROR -30 +#define TOOLCHAINKIT_FILE_NOT_FOUND -31 +#define TOOLCHAINKIT_DIR_NOT_FOUND -32 +#define TOOLCHAINKIT_FILE_EXISTS -33 +#define TOOLCHAINKIT_TOO_LONG -34 +#define TOOLCHAINKIT_INVALID_DATA -35 +#define TOOLCHAINKIT_UNIMPLEMENTED -36 +#define TOOLCHAINKIT_FAT_ERROR -37 diff --git a/dev/LibCompiler/NFC/ErrorOr.h b/dev/LibCompiler/NFC/ErrorOr.h new file mode 100644 index 0000000..18bac3f --- /dev/null +++ b/dev/LibCompiler/NFC/ErrorOr.h @@ -0,0 +1,61 @@ +/* + * ======================================================== + * + * LibCompiler + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include <LibCompiler/Defines.h> +#include <LibCompiler/NFC/Ref.h> + +namespace LibCompiler +{ + using ErrorT = UInt32; + + template <typename T> + class ErrorOr final + { + public: + ErrorOr() = default; + ~ErrorOr() = default; + + public: + explicit ErrorOr(Int32 err) + : mId(err) + { + } + + explicit ErrorOr(nullPtr Null) + { + } + + explicit ErrorOr(T Class) + : mRef(Class) + { + } + + ErrorOr& operator=(const ErrorOr&) = default; + ErrorOr(const ErrorOr&) = default; + + Ref<T> Leak() + { + return mRef; + } + + operator bool() + { + return mRef; + } + + private: + Ref<T> mRef; + Int32 mId{0}; + }; + + using ErrorOrAny = ErrorOr<voidPtr>; + +} // namespace LibCompiler diff --git a/dev/LibCompiler/NFC/PEF.h b/dev/LibCompiler/NFC/PEF.h new file mode 100644 index 0000000..aec3c64 --- /dev/null +++ b/dev/LibCompiler/NFC/PEF.h @@ -0,0 +1,144 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include <LibCompiler/Defines.h> + +// @file PEF.hpp +// @brief Preferred Executable Format + +#define kPefMagic "Joy!" +#define kPefMagicFat "yoJ!" + +#define kPefExt ".o" +#define kPefDylibExt ".dylib" +#define kPefLibExt ".lib" +#define kPefObjectExt ".obj" +#define kPefDebugExt ".dbg" +#define kPefDriverExt ".sys" + +#define kPefZero128 ".zero128" +#define kPefCode128 ".code128" +#define kPefData128 ".data128" + +#define kPefZero64 ".zero64" +#define kPefCode64 ".code64" +#define kPefData64 ".data64" + +#define kPefMagicLen (5) + +#define kPefVersion (3) +#define kPefNameLen (255) + +#define kPefBaseOrigin (0x40000000) + +#define kPefStart "__ImageStart" + +namespace LibCompiler +{ + enum + { + kPefArchStart = 99, + kPefArchIntel86S = 100, + kPefArchAMD64, + kPefArchRISCV, + kPefArch64000, /* 64x0 RISC architecture. */ + kPefArch32000, + kPefArchPowerPC, /* 64-bit POWER architecture. */ + kPefArchARM64, + kPefArchCount = (kPefArchPowerPC - kPefArchIntel86S) + 1, + kPefArchInvalid = 0xFF, + }; + + enum + { + kPefSubArchAMD, + kPefSubArchIntel, + kPefSubArchARM, + kPefSubArchGeneric, + kPefSubArchIBM, + }; + + enum + { + kPefKindExec = 1, /* .o */ + kPefKindDylib = 2, /* .dylib */ + kPefKindObject = 4, /* .obj */ + kPefKindDebug = 5, /* .dbg */ + kPefKindDriver = 6, + kPefKindCount, + }; + + /* PEF container */ + typedef struct PEFContainer final + { + CharType Magic[kPefMagicLen]; + UInt32 Linker; + UInt32 Version; + UInt32 Kind; + UInt32 Abi; + UInt32 Cpu; + UInt32 SubCpu; /* Cpu specific information */ + UIntPtr Start; /* Origin of code */ + SizeType HdrSz; /* Size of header */ + SizeType Count; /* container header count */ + } PACKED PEFContainer; + + /* First PEFCommandHeader starts after PEFContainer */ + /* Last container is __exec_end */ + + /* PEF executable section and commands. */ + + typedef struct PEFCommandHeader final + { + CharType Name[kPefNameLen]; /* container name */ + UInt32 Cpu; /* container cpu */ + UInt32 SubCpu; /* container sub-cpu */ + UInt32 Flags; /* container flags */ + UInt16 Kind; /* container kind */ + UIntPtr Offset; /* file offset */ + SizeType Size; /* file size */ + } PACKED PEFCommandHeader; + + enum + { + kPefCode = 0xC, + kPefData = 0xD, + kPefZero = 0xE, + kPefLinkerID = 0x1, + kPefCount = 4, + kPefInvalid = 0xFF, + }; +} // namespace LibCompiler + +inline std::ofstream& operator<<(std::ofstream& fp, + LibCompiler::PEFContainer& container) +{ + fp.write((char*)&container, sizeof(LibCompiler::PEFContainer)); + return fp; +} + +inline std::ofstream& operator<<(std::ofstream& fp, + LibCompiler::PEFCommandHeader& container) +{ + fp.write((char*)&container, sizeof(LibCompiler::PEFCommandHeader)); + return fp; +} + +inline std::ifstream& operator>>(std::ifstream& fp, + LibCompiler::PEFContainer& container) +{ + fp.read((char*)&container, sizeof(LibCompiler::PEFContainer)); + return fp; +} + +inline std::ifstream& operator>>(std::ifstream& fp, + LibCompiler::PEFCommandHeader& container) +{ + fp.read((char*)&container, sizeof(LibCompiler::PEFCommandHeader)); + return fp; +} diff --git a/dev/LibCompiler/NFC/Ref.h b/dev/LibCompiler/NFC/Ref.h new file mode 100644 index 0000000..f76f676 --- /dev/null +++ b/dev/LibCompiler/NFC/Ref.h @@ -0,0 +1,103 @@ + +/* + * ======================================================== + * + * LibCompiler + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include <LibCompiler/Defines.h> + +namespace LibCompiler +{ + // @author EL Mahrouss Amlal + // @brief Reference holder class, refers to a pointer of data in static memory. + template <typename T> + class Ref final + { + public: + explicit Ref() = default; + + ~Ref() + { + if (m_Strong) + { + (*m_Class).~T(); + } + } + + TOOLCHAINKIT_COPY_DEFAULT(Ref); + + public: + explicit Ref(T cls, const Bool& strong = false) + : m_Class(&cls), m_Strong(strong) + { + } + + Ref& operator=(T ref) + { + *m_Class = ref; + return *this; + } + + public: + T* operator->() const + { + return m_Class; + } + + T& Leak() + { + return *m_Class; + } + + T operator*() + { + return *m_Class; + } + + Bool IsStrong() const + { + return m_Strong; + } + + operator bool() + { + return *m_Class; + } + + private: + T* m_Class{nullptr}; + Bool m_Strong{false}; + }; + + // @author EL Mahrouss Amlal + // @brief Non null Reference holder class, refers to a pointer of data in static memory. + template <typename T> + class NonNullRef final + { + public: + explicit NonNullRef() = delete; + + explicit NonNullRef(T* ref) + : m_Ref(ref, true) + { + } + + Ref<T>& operator->() + { + MUST_PASS(m_Ref); + return m_Ref; + } + + NonNullRef& operator=(const NonNullRef<T>& ref) = delete; + NonNullRef(const NonNullRef<T>& ref) = default; + + private: + Ref<T> m_Ref{nullptr}; + }; +} // namespace LibCompiler diff --git a/dev/LibCompiler/NFC/String.h b/dev/LibCompiler/NFC/String.h new file mode 100644 index 0000000..958da8e --- /dev/null +++ b/dev/LibCompiler/NFC/String.h @@ -0,0 +1,90 @@ +/* + * ======================================================== + * + * LibCompiler + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include <LibCompiler/Defines.h> +#include <LibCompiler/NFC/ErrorOr.h> + +namespace LibCompiler +{ + /** + * @brief StringView class, contains a C string and manages it. + * @note No need to manage it it's getting deleted by default. + */ + + class StringView final + { + public: + explicit StringView() = delete; + + explicit StringView(SizeType Sz) noexcept + : m_Sz(Sz) + { + m_Data = new CharType[Sz]; + assert(m_Data); + } + + ~StringView() noexcept + { + if (m_Data) + { + memset(m_Data, 0, m_Sz); + delete[] m_Data; + + m_Data = nullptr; + } + } + + TOOLCHAINKIT_COPY_DEFAULT(StringView); + + CharType* Data(); + const CharType* CData() const; + SizeType Length() const; + + bool operator==(const CharType* rhs) const; + bool operator!=(const CharType* rhs) const; + + bool operator==(const StringView& rhs) const; + bool operator!=(const StringView& rhs) const; + + StringView& operator+=(const CharType* rhs); + StringView& operator+=(const StringView& rhs); + + operator bool() + { + return m_Data && m_Data[0] != 0; + } + + bool operator!() + { + return !m_Data || m_Data[0] == 0; + } + + private: + CharType* m_Data{nullptr}; + SizeType m_Sz{0}; + SizeType m_Cur{0}; + + friend class StringBuilder; + }; + + /** + * @brief StringBuilder class + * @note These results shall call delete[] after they're used. + */ + struct StringBuilder final + { + static StringView Construct(const CharType* data); + static const char* FromInt(const char* fmt, int n); + static const char* FromBool(const char* fmt, bool n); + static const char* Format(const char* fmt, const char* from); + static bool Equals(const char* lhs, const char* rhs); + }; +} // namespace LibCompiler diff --git a/dev/LibCompiler/NFC/XCOFF.h b/dev/LibCompiler/NFC/XCOFF.h new file mode 100644 index 0000000..0594855 --- /dev/null +++ b/dev/LibCompiler/NFC/XCOFF.h @@ -0,0 +1,41 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + + File: XCOFF.hpp + Purpose: XCOFF for NewOS. + + Revision History: + + 04/07/24: Added file (amlel) + +------------------------------------------- */ + +#ifndef __XCOFF__ +#define __XCOFF__ + +#include <LibCompiler/Defines.h> + +#define kXCOFF64Magic 0x01F7 + +#define kXCOFFRelFlg 0x0001 +#define kXCOFFExecutable 0x0002 +#define kXCOFFLnno 0x0004 +#define kXCOFFLSyms 0x0008 + +namespace LibCompiler +{ + /// @brief XCoff identification header. + typedef struct XCoffFileHeader + { + UInt16 fMagic; + UInt16 fTarget; + UInt16 fNumSecs; + UInt32 fTimeDat; + UIntPtr fSymPtr; + UInt32 fNumSyms; + UInt16 fOptHdr; // ?: Number of bytes in optional header + } XCoffFileHeader; +} // namespace LibCompiler + +#endif // ifndef __XCOFF__ diff --git a/dev/LibCompiler/Parser.h b/dev/LibCompiler/Parser.h new file mode 100644 index 0000000..35e0c45 --- /dev/null +++ b/dev/LibCompiler/Parser.h @@ -0,0 +1,177 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include <LibCompiler/AAL/AssemblyInterface.h> + +namespace LibCompiler +{ + inline auto kInvalidFrontend = "NoLang"; + + /// @brief Compiler backend, implements a frontend, such as C, C++... + /// See Toolchain, for some examples. + class ICompilerFrontend + { + public: + explicit ICompilerFrontend() = default; + virtual ~ICompilerFrontend() = default; + + TOOLCHAINKIT_COPY_DEFAULT(ICompilerFrontend); + + // NOTE: cast this to your user defined ast. + typedef void* AstType; + + //! @brief Compile a syntax tree ouf of the text. + //! Also takes the source file name for metadata. + + virtual bool Compile(const std::string text, const std::string file) = 0; + + //! @brief What language are we dealing with? + virtual const char* Language() + { + return kInvalidFrontend; + } + + virtual bool IsValid() + { + return strcmp(this->Language(), kInvalidFrontend); + } + }; + + struct SyntaxLeafList; + struct SyntaxLeafList; + struct CompilerKeyword; + + /// we want to do that because to separate keywords. + enum KeywordKind + { + eKeywordKindNamespace, + eKeywordKindFunctionStart, + eKeywordKindFunctionEnd, + eKeywordKindVariable, + eKeywordKindVariablePtr, + eKeywordKindType, + eKeywordKindTypePtr, + eKeywordKindExpressionBegin, + eKeywordKindExpressionEnd, + eKeywordKindArgSeparator, + eKeywordKindBodyStart, + eKeywordKindBodyEnd, + eKeywordKindClass, + eKeywordKindPtrAccess, + eKeywordKindAccess, + eKeywordKindIf, + eKeywordKindElse, + eKeywordKindElseIf, + eKeywordKindVariableAssign, + eKeywordKindVariableDec, + eKeywordKindVariableInc, + eKeywordKindConstant, + eKeywordKindTypedef, + eKeywordKindEndInstr, + eKeywordKindSpecifier, + eKeywordKindInvalid, + eKeywordKindReturn, + eKeywordKindCommentInline, + eKeywordKindCommentMultiLineStart, + eKeywordKindCommentMultiLineEnd, + eKeywordKindEq, + eKeywordKindNotEq, + eKeywordKindGreaterEq, + eKeywordKindLessEq, + eKeywordKindPtr, + }; + + /// \brief Compiler keyword information struct. + struct CompilerKeyword + { + std::string keyword_name; + KeywordKind keyword_kind = eKeywordKindInvalid; + }; + struct SyntaxLeafList final + { + struct SyntaxLeaf final + { + Int32 fUserType; +#ifdef TK_USE_STRUCTS + CompilerKeyword fUserData; +#else + std::string fUserData; +#endif + + std::string fUserValue; + struct SyntaxLeaf* fNext; + }; + + std::vector<SyntaxLeaf> fLeafList; + SizeType fNumLeafs; + + size_t SizeOf() + { + return fNumLeafs; + } + std::vector<SyntaxLeaf>& Get() + { + return fLeafList; + } + SyntaxLeaf& At(size_t index) + { + return fLeafList[index]; + } + }; + + /// find the perfect matching word in a haystack. + /// \param haystack base string + /// \param needle the string we search for. + /// \return if we found it or not. + inline bool find_word(const std::string& haystack, + const std::string& needle) noexcept + { + auto index = haystack.find(needle); + + // check for needle validity. + if (index == std::string::npos) + return false; + + // declare lambda + auto not_part_of_word = [&](int index) { + if (std::isspace(haystack[index]) || std::ispunct(haystack[index])) + return true; + + if (index <= 0 || index >= haystack.size()) + return true; + + return false; + }; + + return not_part_of_word(index - 1) && not_part_of_word(index + needle.size()); + } + + /// find a word within strict conditions and returns a range of it. + /// \param haystack + /// \param needle + /// \return position of needle. + inline std::size_t find_word_range(const std::string& haystack, + const std::string& needle) noexcept + { + auto index = haystack.find(needle); + + // check for needle validity. + if (index == std::string::npos) + return false; + + if (!isalnum((haystack[index + needle.size() + 1])) && + !isdigit(haystack[index + needle.size() + 1]) && + !isalnum((haystack[index - needle.size() - 1])) && + !isdigit(haystack[index - needle.size() - 1])) + { + return index; + } + + return false; + } +} // namespace LibCompiler diff --git a/dev/LibCompiler/ReadMe.md b/dev/LibCompiler/ReadMe.md new file mode 100644 index 0000000..4bc01b8 --- /dev/null +++ b/dev/LibCompiler/ReadMe.md @@ -0,0 +1,7 @@ +# NDK Compiler DLL. + +## Compiler, Assembler and Linker DLL. + +This contains a set of tools necessary to compile a ZKA program. + +###### Copyright (C) 2024 Theater Quality Inc, all rights reserved. diff --git a/dev/LibCompiler/UUID.h b/dev/LibCompiler/UUID.h new file mode 100644 index 0000000..74584b4 --- /dev/null +++ b/dev/LibCompiler/UUID.h @@ -0,0 +1,983 @@ +#ifndef STDUUID_H +#define STDUUID_H + +#include <array> +#include <atomic> +#include <chrono> +#include <cstring> +#include <functional> +#include <iomanip> +#include <iterator> +#include <memory> +#include <numeric> +#include <optional> +#include <random> +#include <sstream> +#include <string> +#include <string_view> +#include <type_traits> + +#ifdef __cplusplus + +#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define LIBUUID_CPP20_OR_GREATER +#endif + +#endif + +#ifdef LIBUUID_CPP20_OR_GREATER +#include <span> +#else +#include <gsl/span> +#endif + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifdef UUID_SYSTEM_GENERATOR +#include <objbase.h> +#endif + +#ifdef UUID_TIME_GENERATOR +#include <iphlpapi.h> +#pragma comment(lib, "IPHLPAPI.lib") +#endif + +#elif defined(__linux__) || defined(__unix__) + +#ifdef UUID_SYSTEM_GENERATOR +#include <uuid/uuid.h> +#endif + +#elif defined(__APPLE__) + +#ifdef UUID_SYSTEM_GENERATOR +#include <CoreFoundation/CFUUID.h> +#endif + +#endif + +namespace uuids +{ +#ifdef __cpp_lib_span + template <class ElementType, std::size_t Extent> + using span = std::span<ElementType, Extent>; +#else + template <class ElementType, std::ptrdiff_t Extent> + using span = gsl::span<ElementType, Extent>; +#endif + + namespace Details + { + template <typename TChar> + [[nodiscard]] constexpr inline unsigned char hex2char(TChar const ch) noexcept + { + if (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9')) + return static_cast<unsigned char>(ch - static_cast<TChar>('0')); + if (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f')) + return static_cast<unsigned char>(10 + ch - static_cast<TChar>('a')); + if (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F')) + return static_cast<unsigned char>(10 + ch - static_cast<TChar>('A')); + return 0; + } + + template <typename TChar> + [[nodiscard]] constexpr inline bool is_hex(TChar const ch) noexcept + { + return (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9')) || + (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f')) || + (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F')); + } + + template <typename TChar> + [[nodiscard]] constexpr std::basic_string_view<TChar> to_string_view( + TChar const* str) noexcept + { + if (str) + return str; + return {}; + } + + template <typename StringType> + [[nodiscard]] constexpr std::basic_string_view<typename StringType::value_type, + typename StringType::traits_type> + to_string_view(StringType const& str) noexcept + { + return str; + } + + class sha1 + { + public: + using digest32_t = uint32_t[5]; + using digest8_t = uint8_t[20]; + + static constexpr unsigned int block_bytes = 64; + + [[nodiscard]] inline static uint32_t left_rotate( + uint32_t value, size_t const count) noexcept + { + return (value << count) ^ (value >> (32 - count)); + } + + sha1() + { + reset(); + } + + void reset() noexcept + { + m_digest[0] = 0x67452301; + m_digest[1] = 0xEFCDAB89; + m_digest[2] = 0x98BADCFE; + m_digest[3] = 0x10325476; + m_digest[4] = 0xC3D2E1F0; + m_blockByteIndex = 0; + m_byteCount = 0; + } + + void process_byte(uint8_t octet) + { + this->m_block[this->m_blockByteIndex++] = octet; + ++this->m_byteCount; + if (m_blockByteIndex == block_bytes) + { + this->m_blockByteIndex = 0; + process_block(); + } + } + + void process_block(void const* const start, void const* const end) + { + const uint8_t* begin = static_cast<const uint8_t*>(start); + const uint8_t* finish = static_cast<const uint8_t*>(end); + while (begin != finish) + { + process_byte(*begin); + begin++; + } + } + + void process_bytes(void const* const data, size_t const len) + { + const uint8_t* block = static_cast<const uint8_t*>(data); + process_block(block, block + len); + } + + uint32_t const* get_digest(digest32_t digest) + { + size_t const bitCount = this->m_byteCount * 8; + process_byte(0x80); + if (this->m_blockByteIndex > 56) + { + while (m_blockByteIndex != 0) + { + process_byte(0); + } + while (m_blockByteIndex < 56) + { + process_byte(0); + } + } + else + { + while (m_blockByteIndex < 56) + { + process_byte(0); + } + } + process_byte(0); + process_byte(0); + process_byte(0); + process_byte(0); + process_byte(static_cast<unsigned char>((bitCount >> 24) & 0xFF)); + process_byte(static_cast<unsigned char>((bitCount >> 16) & 0xFF)); + process_byte(static_cast<unsigned char>((bitCount >> 8) & 0xFF)); + process_byte(static_cast<unsigned char>((bitCount)&0xFF)); + + memcpy(digest, m_digest, 5 * sizeof(uint32_t)); + return digest; + } + + uint8_t const* get_digest_bytes(digest8_t digest) + { + digest32_t d32; + get_digest(d32); + size_t di = 0; + digest[di++] = static_cast<uint8_t>(d32[0] >> 24); + digest[di++] = static_cast<uint8_t>(d32[0] >> 16); + digest[di++] = static_cast<uint8_t>(d32[0] >> 8); + digest[di++] = static_cast<uint8_t>(d32[0] >> 0); + + digest[di++] = static_cast<uint8_t>(d32[1] >> 24); + digest[di++] = static_cast<uint8_t>(d32[1] >> 16); + digest[di++] = static_cast<uint8_t>(d32[1] >> 8); + digest[di++] = static_cast<uint8_t>(d32[1] >> 0); + + digest[di++] = static_cast<uint8_t>(d32[2] >> 24); + digest[di++] = static_cast<uint8_t>(d32[2] >> 16); + digest[di++] = static_cast<uint8_t>(d32[2] >> 8); + digest[di++] = static_cast<uint8_t>(d32[2] >> 0); + + digest[di++] = static_cast<uint8_t>(d32[3] >> 24); + digest[di++] = static_cast<uint8_t>(d32[3] >> 16); + digest[di++] = static_cast<uint8_t>(d32[3] >> 8); + digest[di++] = static_cast<uint8_t>(d32[3] >> 0); + + digest[di++] = static_cast<uint8_t>(d32[4] >> 24); + digest[di++] = static_cast<uint8_t>(d32[4] >> 16); + digest[di++] = static_cast<uint8_t>(d32[4] >> 8); + digest[di++] = static_cast<uint8_t>(d32[4] >> 0); + + return digest; + } + + private: + void process_block() + { + uint32_t w[80]; + for (size_t i = 0; i < 16; i++) + { + w[i] = static_cast<uint32_t>(m_block[i * 4 + 0] << 24); + w[i] |= static_cast<uint32_t>(m_block[i * 4 + 1] << 16); + w[i] |= static_cast<uint32_t>(m_block[i * 4 + 2] << 8); + w[i] |= static_cast<uint32_t>(m_block[i * 4 + 3]); + } + for (size_t i = 16; i < 80; i++) + { + w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); + } + + uint32_t a = m_digest[0]; + uint32_t b = m_digest[1]; + uint32_t c = m_digest[2]; + uint32_t d = m_digest[3]; + uint32_t e = m_digest[4]; + + for (std::size_t i = 0; i < 80; ++i) + { + uint32_t f = 0; + uint32_t k = 0; + + if (i < 20) + { + f = (b & c) | (~b & d); + k = 0x5A827999; + } + else if (i < 40) + { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } + else if (i < 60) + { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } + else + { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = left_rotate(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = left_rotate(b, 30); + b = a; + a = temp; + } + + m_digest[0] += a; + m_digest[1] += b; + m_digest[2] += c; + m_digest[3] += d; + m_digest[4] += e; + } + + private: + digest32_t m_digest; + uint8_t m_block[64]; + size_t m_blockByteIndex; + size_t m_byteCount; + }; + + template <typename CharT> + inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000"; + + template <> + inline constexpr wchar_t empty_guid<wchar_t>[37] = + L"00000000-0000-0000-0000-000000000000"; + + template <typename CharT> + inline constexpr CharT guid_encoder[17] = "0123456789abcdef"; + + template <> + inline constexpr wchar_t guid_encoder<wchar_t>[17] = L"0123456789abcdef"; + } // namespace Details + + // -------------------------------------------------------------------------------------------------------------------------- + // UUID format https://tools.ietf.org/html/rfc4122 + // -------------------------------------------------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------------------------------------------------- + // Field NDR Data Type Octet # Note + // -------------------------------------------------------------------------------------------------------------------------- + // time_low unsigned long 0 - 3 The low field + // of the timestamp. time_mid unsigned short 4 - 5 + // The middle field of the timestamp. time_hi_and_version unsigned + // short 6 - 7 The high field of the timestamp multiplexed + // with the version number. clock_seq_hi_and_reserved unsigned small 8 + // The high field of the clock sequence multiplexed with the variant. + // clock_seq_low unsigned small 9 The low + // field of the clock sequence. node character 10 + // - 15 The spatially unique node identifier. + // -------------------------------------------------------------------------------------------------------------------------- + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time_low | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time_mid | time_hi_and_version | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |clk_seq_hi_res | clk_seq_low | node (0-1) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | node (2-5) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // -------------------------------------------------------------------------------------------------------------------------- + // enumerations + // -------------------------------------------------------------------------------------------------------------------------- + + // indicated by a bit pattern in octet 8, marked with N in + // xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx + enum class uuid_variant + { + // NCS backward compatibility (with the obsolete Apollo Network Computing + // System 1.5 UUID format) N bit pattern: 0xxx > the first 6 octets of the + // UUID are a 48-bit timestamp (the number of 4 microsecond units of time + // since 1 Jan 1980 UTC); > the next 2 octets are reserved; > the next octet + // is the "address family"; > the final 7 octets are a 56-bit host ID in the + // form specified by the address family + ncs, + + // RFC 4122/DCE 1.1 + // N bit pattern: 10xx + // > big-endian byte order + rfc, + + // Microsoft Corporation backward compatibility + // N bit pattern: 110x + // > little endian byte order + // > formely used in the Component Object Model (COM) library + microsoft, + + // reserved for possible future definition + // N bit pattern: 111x + reserved + }; + + // indicated by a bit pattern in octet 6, marked with M in + // xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx + enum class uuid_version + { + none = 0, // only possible for nil or invalid uuids + time_based = 1, // The time-based version specified in RFC 4122 + dce_security = 2, // DCE Security version, with embedded POSIX UIDs. + name_based_md5 = + 3, // The name-based version specified in RFS 4122 with MD5 hashing + random_number_based = 4, // The randomly or pseudo-randomly generated version + // specified in RFS 4122 + name_based_sha1 = + 5 // The name-based version specified in RFS 4122 with SHA1 hashing + }; + + // Forward declare uuid & to_string so that we can declare to_string as a friend + // later. + class uuid; + template <class CharT = char, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT>> + std::basic_string<CharT, Traits, Allocator> to_string(uuid const& id); + + // -------------------------------------------------------------------------------------------------------------------------- + // uuid class + // -------------------------------------------------------------------------------------------------------------------------- + class uuid + { + public: + using value_type = uint8_t; + + constexpr uuid() noexcept = default; + + uuid(value_type (&arr)[16]) noexcept + { + std::copy(std::cbegin(arr), std::cend(arr), std::begin(data)); + } + + constexpr uuid(std::array<value_type, 16> const& arr) noexcept + : data{arr} + { + } + + explicit uuid(span<value_type, 16> bytes) + { + std::copy(std::cbegin(bytes), std::cend(bytes), std::begin(data)); + } + + template <typename ForwardIterator> + explicit uuid(ForwardIterator first, ForwardIterator last) + { + if (std::distance(first, last) == 16) + std::copy(first, last, std::begin(data)); + } + + [[nodiscard]] constexpr uuid_variant variant() const noexcept + { + if ((data[8] & 0x80) == 0x00) + return uuid_variant::ncs; + else if ((data[8] & 0xC0) == 0x80) + return uuid_variant::rfc; + else if ((data[8] & 0xE0) == 0xC0) + return uuid_variant::microsoft; + else + return uuid_variant::reserved; + } + + [[nodiscard]] constexpr uuid_version version() const noexcept + { + if ((data[6] & 0xF0) == 0x10) + return uuid_version::time_based; + else if ((data[6] & 0xF0) == 0x20) + return uuid_version::dce_security; + else if ((data[6] & 0xF0) == 0x30) + return uuid_version::name_based_md5; + else if ((data[6] & 0xF0) == 0x40) + return uuid_version::random_number_based; + else if ((data[6] & 0xF0) == 0x50) + return uuid_version::name_based_sha1; + else + return uuid_version::none; + } + + [[nodiscard]] constexpr bool is_nil() const noexcept + { + for (size_t i = 0; i < data.size(); ++i) + if (data[i] != 0) + return false; + return true; + } + + void swap(uuid& other) noexcept + { + data.swap(other.data); + } + + [[nodiscard]] inline span<std::byte const, 16> as_bytes() const + { + return span<std::byte const, 16>( + reinterpret_cast<std::byte const*>(data.data()), 16); + } + + template <typename StringType> + [[nodiscard]] constexpr static bool is_valid_uuid( + StringType const& in_str) noexcept + { + auto str = Details::to_string_view(in_str); + bool firstDigit = true; + size_t hasBraces = 0; + size_t index = 0; + + if (str.empty()) + return false; + + if (str.front() == '{') + hasBraces = 1; + if (hasBraces && str.back() != '}') + return false; + + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) + { + if (str[i] == '-') + continue; + + if (index >= 16 || !Details::is_hex(str[i])) + { + return false; + } + + if (firstDigit) + { + firstDigit = false; + } + else + { + index++; + firstDigit = true; + } + } + + if (index < 16) + { + return false; + } + + return true; + } + + template <typename StringType> + [[nodiscard]] constexpr static std::optional<uuid> from_string( + StringType const& in_str) noexcept + { + auto str = Details::to_string_view(in_str); + bool firstDigit = true; + size_t hasBraces = 0; + size_t index = 0; + + std::array<uint8_t, 16> data{{0}}; + + if (str.empty()) + return {}; + + if (str.front() == '{') + hasBraces = 1; + if (hasBraces && str.back() != '}') + return {}; + + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) + { + if (str[i] == '-') + continue; + + if (index >= 16 || !Details::is_hex(str[i])) + { + return {}; + } + + if (firstDigit) + { + data[index] = static_cast<uint8_t>(Details::hex2char(str[i]) << 4); + firstDigit = false; + } + else + { + data[index] = + static_cast<uint8_t>(data[index] | Details::hex2char(str[i])); + index++; + firstDigit = true; + } + } + + if (index < 16) + { + return {}; + } + + return uuid{data}; + } + + private: + std::array<value_type, 16> data{{0}}; + + friend bool operator==(uuid const& lhs, uuid const& rhs) noexcept; + friend bool operator<(uuid const& lhs, uuid const& rhs) noexcept; + + template <class Elem, class Traits> + friend std::basic_ostream<Elem, Traits>& operator<<( + std::basic_ostream<Elem, Traits>& s, uuid const& id); + + template <class CharT, class Traits, class Allocator> + friend std::basic_string<CharT, Traits, Allocator> to_string(uuid const& id); + + friend std::hash<uuid>; + }; + + // -------------------------------------------------------------------------------------------------------------------------- + // operators and non-member functions + // -------------------------------------------------------------------------------------------------------------------------- + + [[nodiscard]] inline bool operator==(uuid const& lhs, + uuid const& rhs) noexcept + { + return lhs.data == rhs.data; + } + + [[nodiscard]] inline bool operator!=(uuid const& lhs, + uuid const& rhs) noexcept + { + return !(lhs == rhs); + } + + [[nodiscard]] inline bool operator<(uuid const& lhs, uuid const& rhs) noexcept + { + return lhs.data < rhs.data; + } + + template <class CharT, class Traits, class Allocator> + [[nodiscard]] inline std::basic_string<CharT, Traits, Allocator> to_string( + uuid const& id) + { + std::basic_string<CharT, Traits, Allocator> uustr{Details::empty_guid<CharT>}; + + for (size_t i = 0, index = 0; i < 36; ++i) + { + if (i == 8 || i == 13 || i == 18 || i == 23) + { + continue; + } + uustr[i] = Details::guid_encoder<CharT>[id.data[index] >> 4 & 0x0f]; + uustr[++i] = Details::guid_encoder<CharT>[id.data[index] & 0x0f]; + index++; + } + + return uustr; + } + + template <class Elem, class Traits> + std::basic_ostream<Elem, Traits>& operator<<( + std::basic_ostream<Elem, Traits>& s, uuid const& id) + { + s << to_string(id); + return s; + } + + inline void swap(uuids::uuid& lhs, uuids::uuid& rhs) noexcept + { + lhs.swap(rhs); + } + + // -------------------------------------------------------------------------------------------------------------------------- + // namespace IDs that could be used for generating name-based uuids + // -------------------------------------------------------------------------------------------------------------------------- + + // Name string is a fully-qualified domain name + static uuid uuid_namespace_dns{{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, + 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, + 0xc8}}; + + // Name string is a URL + static uuid uuid_namespace_url{{0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, + 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, + 0xc8}}; + + // Name string is an ISO OID (See https://oidref.com/, + // https://en.wikipedia.org/wiki/Object_identifier) + static uuid uuid_namespace_oid{{0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, + 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, + 0xc8}}; + + // Name string is an X.500 DN, in DER or a text output format (See + // https://en.wikipedia.org/wiki/X.500, + // https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One) + static uuid uuid_namespace_x500{{0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, + 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, + 0xc8}}; + + // -------------------------------------------------------------------------------------------------------------------------- + // uuid generators + // -------------------------------------------------------------------------------------------------------------------------- + +#ifdef UUID_SYSTEM_GENERATOR + class uuid_system_generator + { + public: + using result_type = uuid; + + uuid operator()() + { +#ifdef _WIN32 + + GUID newId; + HRESULT hr = ::CoCreateGuid(&newId); + + if (FAILED(hr)) + { + throw std::system_error(hr, std::system_category(), + "CoCreateGuid failed"); + } + + std::array<uint8_t, 16> bytes = { + {static_cast<unsigned char>((newId.Data1 >> 24) & 0xFF), + static_cast<unsigned char>((newId.Data1 >> 16) & 0xFF), + static_cast<unsigned char>((newId.Data1 >> 8) & 0xFF), + static_cast<unsigned char>((newId.Data1) & 0xFF), + + (unsigned char)((newId.Data2 >> 8) & 0xFF), + (unsigned char)((newId.Data2) & 0xFF), + + (unsigned char)((newId.Data3 >> 8) & 0xFF), + (unsigned char)((newId.Data3) & 0xFF), + + newId.Data4[0], newId.Data4[1], newId.Data4[2], newId.Data4[3], + newId.Data4[4], newId.Data4[5], newId.Data4[6], newId.Data4[7]}}; + + return uuid{std::begin(bytes), std::end(bytes)}; + +#elif defined(__linux__) || defined(__unix__) + + uuid_t id; + uuid_generate(id); + + std::array<uint8_t, 16> bytes = {{id[0], id[1], id[2], id[3], id[4], id[5], + id[6], id[7], id[8], id[9], id[10], + id[11], id[12], id[13], id[14], id[15]}}; + + return uuid{std::begin(bytes), std::end(bytes)}; + +#elif defined(__APPLE__) + auto newId = CFUUIDCreate(NULL); + auto bytes = CFUUIDGetUUIDBytes(newId); + CFRelease(newId); + + std::array<uint8_t, 16> arrbytes = { + {bytes.byte0, bytes.byte1, bytes.byte2, bytes.byte3, bytes.byte4, + bytes.byte5, bytes.byte6, bytes.byte7, bytes.byte8, bytes.byte9, + bytes.byte10, bytes.byte11, bytes.byte12, bytes.byte13, bytes.byte14, + bytes.byte15}}; + return uuid{std::begin(arrbytes), std::end(arrbytes)}; +#else + return uuid{}; +#endif + } + }; +#endif + + template <typename UniformRandomNumberGenerator> + class basic_uuid_random_generator + { + public: + using engine_type = UniformRandomNumberGenerator; + + explicit basic_uuid_random_generator(engine_type& gen) + : generator(&gen, [](auto) {}) + { + } + explicit basic_uuid_random_generator(engine_type* gen) + : generator(gen, [](auto) {}) + { + } + + [[nodiscard]] uuid operator()() + { + alignas(uint32_t) uint8_t bytes[16]; + for (int i = 0; i < 16; i += 4) + *reinterpret_cast<uint32_t*>(bytes + i) = distribution(*generator); + + // variant must be 10xxxxxx + bytes[8] &= 0xBF; + bytes[8] |= 0x80; + + // version must be 0100xxxx + bytes[6] &= 0x4F; + bytes[6] |= 0x40; + + return uuid{std::begin(bytes), std::end(bytes)}; + } + + private: + std::uniform_int_distribution<uint32_t> distribution; + std::shared_ptr<UniformRandomNumberGenerator> generator; + }; + + using uuid_random_generator = basic_uuid_random_generator<std::mt19937>; + + class uuid_name_generator + { + public: + explicit uuid_name_generator(uuid const& namespace_uuid) noexcept + : nsuuid(namespace_uuid) + { + } + + template <typename StringType> + [[nodiscard]] uuid operator()(StringType const& name) + { + reset(); + process_characters(Details::to_string_view(name)); + return make_uuid(); + } + + private: + void reset() + { + hasher.reset(); + std::byte bytes[16]; + auto nsbytes = nsuuid.as_bytes(); + std::copy(std::cbegin(nsbytes), std::cend(nsbytes), bytes); + hasher.process_bytes(bytes, 16); + } + + template <typename CharT, typename Traits> + void process_characters(std::basic_string_view<CharT, Traits> const str) + { + for (uint32_t c : str) + { + hasher.process_byte(static_cast<uint8_t>(c & 0xFF)); + if constexpr (!std::is_same_v<CharT, char>) + { + hasher.process_byte(static_cast<uint8_t>((c >> 8) & 0xFF)); + hasher.process_byte(static_cast<uint8_t>((c >> 16) & 0xFF)); + hasher.process_byte(static_cast<uint8_t>((c >> 24) & 0xFF)); + } + } + } + + [[nodiscard]] uuid make_uuid() + { + Details::sha1::digest8_t digest; + hasher.get_digest_bytes(digest); + + // variant must be 0b10xxxxxx + digest[8] &= 0xBF; + digest[8] |= 0x80; + + // version must be 0b0101xxxx + digest[6] &= 0x5F; + digest[6] |= 0x50; + + return uuid{digest, digest + 16}; + } + + private: + uuid nsuuid; + Details::sha1 hasher; + }; + +#ifdef UUID_TIME_GENERATOR + // !!! DO NOT USE THIS IN PRODUCTION + // this implementation is unreliable for good uuids + class uuid_time_generator + { + using mac_address = std::array<unsigned char, 6>; + + std::optional<mac_address> device_address; + + [[nodiscard]] bool get_mac_address() + { + if (device_address.has_value()) + { + return true; + } + +#ifdef _WIN32 + DWORD len = 0; + auto ret = GetAdaptersInfo(nullptr, &len); + if (ret != ERROR_BUFFER_OVERFLOW) + return false; + std::vector<unsigned char> buf(len); + auto pips = reinterpret_cast<PIP_ADAPTER_INFO>(&buf.front()); + ret = GetAdaptersInfo(pips, &len); + if (ret != ERROR_SUCCESS) + return false; + mac_address addr; + std::copy(pips->Address, pips->Address + 6, std::begin(addr)); + device_address = addr; +#endif + + return device_address.has_value(); + } + + [[nodiscard]] long long get_time_intervals() + { + auto start = std::chrono::system_clock::from_time_t(time_t(-12219292800)); + auto diff = std::chrono::system_clock::now() - start; + auto ns = + std::chrono::duration_cast<std::chrono::nanoseconds>(diff).count(); + return ns / 100; + } + + [[nodiscard]] static unsigned short get_clock_sequence() + { + static std::mt19937 clock_gen(std::random_device{}()); + static std::uniform_int_distribution<unsigned short> clock_dis; + static std::atomic_ushort clock_sequence = clock_dis(clock_gen); + return clock_sequence++; + } + + public: + [[nodiscard]] uuid operator()() + { + if (get_mac_address()) + { + std::array<uuids::uuid::value_type, 16> data; + + auto tm = get_time_intervals(); + + auto clock_seq = get_clock_sequence(); + + auto ptm = reinterpret_cast<uuids::uuid::value_type*>(&tm); + + memcpy(&data[0], ptm + 4, 4); + memcpy(&data[4], ptm + 2, 2); + memcpy(&data[6], ptm, 2); + + memcpy(&data[8], &clock_seq, 2); + + // variant must be 0b10xxxxxx + data[8] &= 0xBF; + data[8] |= 0x80; + + // version must be 0b0001xxxx + data[6] &= 0x1F; + data[6] |= 0x10; + + memcpy(&data[10], &device_address.value()[0], 6); + + return uuids::uuid{std::cbegin(data), std::cend(data)}; + } + + return {}; + } + }; +#endif +} // namespace uuids + +namespace std +{ + template <> + struct hash<uuids::uuid> + { + using argument_type = uuids::uuid; + using result_type = std::size_t; + + [[nodiscard]] result_type operator()(argument_type const& uuid) const + { +#ifdef UUID_HASH_STRING_BASED + std::hash<std::string> hasher; + return static_cast<result_type>(hasher(uuids::to_string(uuid))); +#else + uint64_t l = static_cast<uint64_t>(uuid.data[0]) << 56 | + static_cast<uint64_t>(uuid.data[1]) << 48 | + static_cast<uint64_t>(uuid.data[2]) << 40 | + static_cast<uint64_t>(uuid.data[3]) << 32 | + static_cast<uint64_t>(uuid.data[4]) << 24 | + static_cast<uint64_t>(uuid.data[5]) << 16 | + static_cast<uint64_t>(uuid.data[6]) << 8 | + static_cast<uint64_t>(uuid.data[7]); + uint64_t h = static_cast<uint64_t>(uuid.data[8]) << 56 | + static_cast<uint64_t>(uuid.data[9]) << 48 | + static_cast<uint64_t>(uuid.data[10]) << 40 | + static_cast<uint64_t>(uuid.data[11]) << 32 | + static_cast<uint64_t>(uuid.data[12]) << 24 | + static_cast<uint64_t>(uuid.data[13]) << 16 | + static_cast<uint64_t>(uuid.data[14]) << 8 | + static_cast<uint64_t>(uuid.data[15]); + + if constexpr (sizeof(result_type) > 4) + { + return result_type(l ^ h); + } + else + { + uint64_t hash64 = l ^ h; + return result_type(uint32_t(hash64 >> 32) ^ uint32_t(hash64)); + } +#endif + } + }; +} // namespace std + +#endif /* STDUUID_H */
\ No newline at end of file diff --git a/dev/LibCompiler/Version.h b/dev/LibCompiler/Version.h new file mode 100644 index 0000000..7fd0ffe --- /dev/null +++ b/dev/LibCompiler/Version.h @@ -0,0 +1,15 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#define kDistVersion "v1.1.0" +#define kDistVersionBCD 0x0110 + +#define ToString(X) Stringify(X) +#define Stringify(X) #X + +#define kDistRelease ToString(kDistReleaseBranch) diff --git a/dev/LibCompiler/src/Assembler32x0.cc b/dev/LibCompiler/src/Assembler32x0.cc new file mode 100644 index 0000000..3a5e030 --- /dev/null +++ b/dev/LibCompiler/src/Assembler32x0.cc @@ -0,0 +1,50 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +/// bugs: 0 + +///////////////////////////////////////////////////////////////////////////////////////// + +// @file 32asm.cxx +// @author EL Mahrouss Amlal +// @brief 32x0 Assembler. + +// REMINDER: when dealing with an undefined symbol use (string +// size):LinkerFindSymbol:(string) so that ld will look for it. + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_32x0__ 1 + +#include <LibCompiler/AAL/CPU/32x0.h> +#include <LibCompiler/Parser.h> +#include <LibCompiler/NFC/AE.h> +#include <LibCompiler/NFC/PEF.h> + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" +#define kYellow "\e[0;33m" + +#define kStdOut (std::cout << kWhite) +#define kStdErr (std::cout << kRed) + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief 32x0 Assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +TOOLCHAINKIT_MODULE(ZKAAssemblerMain32000) +{ + return 0; +} diff --git a/dev/LibCompiler/src/Assembler64x0.cc b/dev/LibCompiler/src/Assembler64x0.cc new file mode 100644 index 0000000..968b6a5 --- /dev/null +++ b/dev/LibCompiler/src/Assembler64x0.cc @@ -0,0 +1,1117 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +/// bugs: 0 + +///////////////////////////////////////////////////////////////////////////////////////// + +// @file Assembler64x0.cxx +// @author EL Mahrouss Amlal +// @brief 64x000 Assembler. + +// REMINDER: when dealing with an undefined symbol use (string +// size):LinkerFindSymbol:(string) so that ld will look for it. + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_64x0__ 1 + +#include <LibCompiler/AAL/CPU/64x0.h> +#include <LibCompiler/Parser.h> +#include <LibCompiler/NFC/AE.h> +#include <LibCompiler/NFC/PEF.h> +#include <algorithm> +#include <filesystem> +#include <fstream> +#include <iostream> +#include <vector> + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" +#define kYellow "\e[0;33m" + +#define kStdOut (std::cout << kWhite) +#define kStdErr (std::cout << kRed) + +static char kOutputArch = LibCompiler::kPefArch64000; +static Boolean kOutputAsBinary = false; + +static UInt32 kErrorLimit = 10; +static UInt32 kAcceptableErrors = 0; + +constexpr auto c64x0IPAlignment = 0x4U; + +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 LibCompiler::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = LibCompiler::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector<LibCompiler::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 Details +{ + void print_error(std::string reason, std::string file) noexcept + { + if (reason[0] == '\n') + reason.erase(0, 1); + + kStdErr << kRed << "[ TQC++ ] " << kWhite + << ((file == "LibCompiler") ? "InternalErrorException: " + : ("FileException{ " + file + " }: ")) + << kBlank << std::endl; + kStdErr << kRed << "[ TQC++ ] " << kWhite << reason << kBlank << std::endl; + + if (kAcceptableErrors > kErrorLimit) + std::exit(3); + + ++kAcceptableErrors; + } + + void print_warning(std::string reason, std::string file) noexcept + { + if (reason[0] == '\n') + reason.erase(0, 1); + + if (!file.empty()) + { + kStdOut << kYellow << "[ TQC++ ] " << kWhite << file << kBlank << std::endl; + } + + kStdOut << kYellow << "[ TQC++ ] " << kWhite << reason << kBlank << std::endl; + } +} // namespace Details + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief 64x0 assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +TOOLCHAINKIT_MODULE(AssemblerMain64x0) +{ + for (size_t i = 1; i < argc; ++i) + { + if (argv[i][0] == '-') + { + if (strcmp(argv[i], "--ver") == 0 || strcmp(argv[i], "--v") == 0) + { + kStdOut << "Assembler64x0: 64x0 Assembler.\nAssembler64x0: v1.10\nAssembler64x0: Copyright (c) " + "Theater Quality Inc\n"; + return 0; + } + else if (strcmp(argv[i], "--h") == 0) + { + kStdOut << "Assembler64x0: 64x0 Assembler.\nAssembler64x0: 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"; + kStdOut << "--64xxx: Compile for a subset of the X64000.\n"; + + return 0; + } + else if (strcmp(argv[i], "--binary") == 0) + { + kOutputAsBinary = true; + continue; + } + else if (strcmp(argv[i], "--verbose") == 0) + { + kVerbose = true; + continue; + } + + kStdOut << "Assembler64x0: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) + { + kStdOut << "Assembler64x0: 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 += kOutputAsBinary ? kBinaryFileExt : 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 << "Assembler64x0: error: " << strerror(errno) << "\n"; + } + } + + std::string line; + + LibCompiler::AEHeader hdr{0}; + + memset(hdr.fPad, kAENullType, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(LibCompiler::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + LibCompiler::Encoder64x0 asm64; + + while (std::getline(file_ptr, line)) + { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) + { + Details::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(); + Details::print_warning("exit because of: " + what, "LibCompiler"); + } + + std::filesystem::remove(object_output); + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) + { + if (kVerbose) + { + kStdOut << "Assembler64x0: 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 << "Assembler64x0: At least one record is needed to write an object " + "file.\nAssembler64x0: Make one using `public_segment .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 << "Assembler64x0: Wrote record " << rec.fName << " to file...\n"; + + rec.fFlags |= LibCompiler::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) + { + LibCompiler::AERecordHeader _record_hdr{0}; + + if (kVerbose) + kStdOut << "Assembler64x0: Wrote symbol " << sym << " to file...\n"; + + _record_hdr.fKind = kAENullType; + _record_hdr.fSize = sym.size(); + _record_hdr.fOffset = record_count; + + ++record_count; + + memset(_record_hdr.fPad, kAENullType, 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 << "Assembler64x0: 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 << "Assembler64x0: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) + kStdOut << "Assembler64x0: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) + kStdOut << "Assembler64x0: Exit failed.\n"; + + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string& line) +{ + // extern_segment is the opposite of public_segment, it signals to the ld + // that we need this symbol. + if (LibCompiler::find_word(line, "extern_segment")) + { + if (kOutputAsBinary) + { + Details::print_error("Invalid extern_segment directive in flat binary mode.", + "LibCompiler"); + throw std::runtime_error("invalid_extern_segment_bin"); + } + + auto name = line.substr(line.find("extern_segment") + strlen("extern_segment")); + + /// sanity check to avoid stupid linker errors. + if (name.size() == 0) + { + Details::print_error("Invalid extern_segment", "power-as"); + throw std::runtime_error("invalid_extern_segment"); + } + + 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 = LibCompiler::kPefCode; + } + else if (name.find(".data64") != std::string::npos) + { + // no code will be executed from here. + kCurrentRecord.fKind = LibCompiler::kPefData; + } + else if (name.find(".zero64") != std::string::npos) + { + // this is a bss section. + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) + { + kCurrentRecord.fKind = LibCompiler::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, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // public_segment is a special keyword used by Assembler64x0 to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64., + // .zero64 + else if (LibCompiler::find_word(line, "public_segment")) + { + if (kOutputAsBinary) + { + Details::print_error("Invalid public_segment directive in flat binary mode.", + "LibCompiler"); + throw std::runtime_error("invalid_public_segment_bin"); + } + + auto name = line.substr(line.find("public_segment") + strlen("public_segment")); + + 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 = LibCompiler::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 = LibCompiler::kPefData; + } + else if (name.find(".zero64") != std::string::npos) + { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) + { + kCurrentRecord.fKind = LibCompiler::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, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace Details::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_64x0(const std::string& str) + { + return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); + } +} // namespace Details::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string LibCompiler::Encoder64x0::CheckLine(std::string& line, + const std::string& file) +{ + std::string err_str; + + if (line.empty() || LibCompiler::find_word(line, "extern_segment") || + LibCompiler::find_word(line, "public_segment") || + line.find('#') != std::string::npos || LibCompiler::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 (!Details::algorithm::is_valid_64x0(line)) + { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + } + } + + return err_str; + } + + if (!Details::algorithm::is_valid_64x0(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 (LibCompiler::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: " + line; + + return err_str; +} + +bool LibCompiler::Encoder64x0::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) + { + Details::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex_number"); + } + } + + LibCompiler::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char& i : num.number) + { + kBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "Assembler64x0: 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) + { + Details::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) + { + kStdOut << "Assembler64x0: 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) + { + Details::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) + { + kStdOut << "Assembler64x0: 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; + } + } + + LibCompiler::NumberCast64 num( + strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char& i : num.number) + { + kBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "Assembler64x0: found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool LibCompiler::Encoder64x0::WriteLine(std::string& line, + const std::string& file) +{ + if (LibCompiler::find_word(line, "public_segment ")) + return true; + + for (auto& opcode64x0 : kOpcodes64x0) + { + // strict check here + if (LibCompiler::find_word(line, opcode64x0.fName) && + Details::algorithm::is_valid_64x0(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 == LibCompiler::kPefArch64000) + { + if (isdigit(line[line_index + 3]) && + isdigit(line[line_index + 2])) + { + reg_str += line[line_index + 3]; + Details::print_error( + "invalid register index, r" + reg_str + + "\nnote: The 64x0 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) + { + Details::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 << "Assembler64x0: Register found: " << register_syntax << "\n"; + kStdOut << "Assembler64x0: 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) + { + Details::print_error( + "Too few registers.\ntip: each Assembler64x0 register " + "starts with 'r'.\nline: " + + line, + file); + throw std::runtime_error("not_a_register"); + } + } + + if (found_some < 1 && name != "ldw" && name != "lda" && + name != "stw") + { + Details::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") + { + Details::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 == "sub") + { + Details::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") + { + Details::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) + { + Details::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") + { + Details::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("extern_segment ") != std::string::npos) + { + Details::print_error("invalid usage extern_segment 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("extern_segment") != std::string::npos) + { + cpy_jump_label.erase(cpy_jump_label.find("extern_segment"), strlen("extern_segment")); + + if (name == "sta") + { + Details::print_error("extern_segment is not allowed on a sta operation.", + file); + throw std::runtime_error("extern_segment_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 << "Assembler64x0: Replace label " << cpy_jump_label + << " to address: " << label.second << std::endl; + } + + LibCompiler::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) + { + Details::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 += c64x0IPAlignment; + + break; + } + } + + return true; +} + +// Last rev 13-1-24 diff --git a/dev/LibCompiler/src/AssemblerAMD64.cc b/dev/LibCompiler/src/AssemblerAMD64.cc new file mode 100644 index 0000000..365163d --- /dev/null +++ b/dev/LibCompiler/src/AssemblerAMD64.cc @@ -0,0 +1,1481 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @file AssemblerAMD64.cc +/// @author EL Mahrouss Amlal +/// @brief AMD64 Assembler. +/// REMINDER: when dealing with an undefined symbol use (string +/// size):LinkerFindSymbol:(string) so that ld will look for it. + +///////////////////////////////////////////////////////////////////////////////////////// + +/// bugs: 0 + +/// feature request: 1 +/// Encode registers in mov, add, xor... + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_AMD64__ 1 + +#define kAssemblerPragmaSymStr "#" +#define kAssemblerPragmaSym '#' + +#include <LibCompiler/AAL/CPU/amd64.h> +#include <LibCompiler/Parser.h> +#include <LibCompiler/NFC/AE.h> +#include <LibCompiler/NFC/PEF.h> +#include <algorithm> +#include <cstdlib> +#include <filesystem> +#include <fstream> +#include <iostream> +#include <vector> + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" +#define kYellow "\e[0;33m" + +#define kStdOut (std::cout << kWhite) +#define kStdErr (std::cout << kRed) + +static char kOutputArch = LibCompiler::kPefArchAMD64; +static Boolean kOutputAsBinary = false; + +static UInt32 kErrorLimit = 10; +static UInt32 kAcceptableErrors = 0; + +constexpr auto kIPAlignement = 0x4U; + +static std::size_t kCounter = 1UL; + +static std::uintptr_t kOrigin = kPefBaseOrigin; +static std::vector<std::pair<std::string, std::uintptr_t>> kOriginLabel; + +/// @brief keep it simple by default. +static std::int32_t kRegisterBitWidth = 16U; + +static bool kVerbose = false; + +static std::vector<i64_byte_t> kAppBytes; + +static LibCompiler::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = LibCompiler::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector<LibCompiler::AERecordHeader> kRecords; +static std::vector<std::string> kDefinedSymbols; +static std::vector<std::string> kUndefinedSymbols; + +static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; + +// \brief forward decl. +static bool asm_read_attributes(std::string& line); + +#include <AsmUtils.h> + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief AMD64 assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +TOOLCHAINKIT_MODULE(AssemblerAMD64) +{ + //////////////// CPU OPCODES BEGIN //////////////// + + std::string opcodes_jump[kJumpLimit] = { + "ja", "jae", "jb", "jbe", "jc", "je", "jg", "jge", "jl", "jle", + "jna", "jnae", "jnb", "jnbe", "jnc", "jne", "jng", "jnge", "jnl", "jnle", + "jno", "jnp", "jns", "jnz", "jo", "jp", "jpe", "jpo", "js", "jz"}; + + for (i64_hword_t i = 0; i < kJumpLimit; i++) + { + CpuOpcodeAMD64 code{ + .fName = opcodes_jump[i], + .fOpcode = static_cast<i64_hword_t>(kAsmJumpOpcode + i)}; + kOpcodesAMD64.push_back(code); + } + + CpuOpcodeAMD64 code{.fName = "jcxz", .fOpcode = 0xE3}; + kOpcodesAMD64.push_back(code); + + for (i64_hword_t i = kJumpLimitStandard; i < kJumpLimitStandardLimit; i++) + { + CpuOpcodeAMD64 code{.fName = "jmp", .fOpcode = i}; + kOpcodesAMD64.push_back(code); + } + + CpuOpcodeAMD64 lahf{.fName = "lahf", .fOpcode = 0x9F}; + kOpcodesAMD64.push_back(lahf); + + CpuOpcodeAMD64 lds{.fName = "lds", .fOpcode = 0xC5}; + kOpcodesAMD64.push_back(lds); + + CpuOpcodeAMD64 lea{.fName = "lea", .fOpcode = 0x8D}; + kOpcodesAMD64.push_back(lea); + + CpuOpcodeAMD64 nop{.fName = "nop", .fOpcode = 0x90}; + kOpcodesAMD64.push_back(nop); + + //////////////// CPU OPCODES END //////////////// + + for (size_t i = 1; i < argc; ++i) + { + if (argv[i][0] == '-') + { + if (strcmp(argv[i], "--amd64:ver") == 0 || strcmp(argv[i], "--amd64:v") == 0) + { + kStdOut << "AssemblerAMD64: AMD64 Assembler Driver.\nAssemblerAMD64: v1.10\nAssemblerAMD64: Copyright " + "(c) Theater Quality Inc\n"; + return 0; + } + else if (strcmp(argv[i], "--amd64:h") == 0) + { + kStdOut << "AssemblerAMD64: AMD64 Assembler Driver.\nAssemblerAMD64: Copyright (c) 2024 " + "Theater Quality Inc\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], "--amd64:binary") == 0) + { + kOutputAsBinary = true; + continue; + } + else if (strcmp(argv[i], "--amd64:verbose") == 0) + { + kVerbose = true; + continue; + } + + kStdOut << "AssemblerAMD64: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) + { + kStdOut << "AssemblerAMD64: can't open: " << argv[i] << std::endl; + goto asm_fail_exit; + } + + std::string object_output(argv[i]); + std::string asm_input(argv[i]); + + for (auto& ext : kAsmFileExts) + { + if (object_output.ends_with(ext)) + { + object_output.erase(object_output.find(ext), std::strlen(ext)); + break; + } + } + + object_output += kOutputAsBinary ? kBinaryFileExt : kObjectFileExt; + + std::ifstream file_ptr(argv[i]); + std::ofstream file_ptr_out(object_output, std::ofstream::binary); + + kStdOut << "AssemblerAMD64: Assembling: " << argv[i] << "\n"; + + if (file_ptr_out.bad()) + { + if (kVerbose) + { + kStdOut << "AssemblerAMD64: error: " << strerror(errno) << "\n"; + } + + return 1; + } + + std::string line; + + LibCompiler::AEHeader hdr{0}; + + memset(hdr.fPad, kAENullType, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(LibCompiler::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + LibCompiler::EncoderAMD64 asm64; + + if (kVerbose) + { + kStdOut << "Compiling: " + asm_input << "\n"; + kStdOut << "From: " + line << "\n"; + } + + while (std::getline(file_ptr, line)) + { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) + { + Details::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(); + Details::print_warning("exit because of: " + what, "LibCompiler"); + } + + try + { + std::filesystem::remove(object_output); + } + catch (...) + { + } + + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) + { + if (kVerbose) + { + kStdOut << "AssemblerAMD64: 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 << "AssemblerAMD64: At least one record is needed to write an object " + "file.\nAssemblerAMD64: Make one using `public_segment .code64 foo_bar`.\n"; + + std::filesystem::remove(object_output); + return 1; + } + + kRecords[kRecords.size() - 1].fSize = kAppBytes.size(); + + std::size_t record_count = 0UL; + + for (auto& rec : kRecords) + { + if (kVerbose) + kStdOut << "AssemblerAMD64: Wrote record " << rec.fName << " to file...\n"; + + rec.fFlags |= LibCompiler::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) + { + LibCompiler::AERecordHeader _record_hdr{0}; + + if (kVerbose) + kStdOut << "AssemblerAMD64: Wrote symbol " << sym << " to file...\n"; + + _record_hdr.fKind = kAENullType; + _record_hdr.fSize = sym.size(); + _record_hdr.fOffset = record_count; + + ++record_count; + + memset(_record_hdr.fPad, kAENullType, 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 = kAppBytes.size(); + + file_ptr_out << hdr; + + file_ptr_out.seekp(pos_end); + } + else + { + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Write raw binary...\n"; + } + } + + // byte from byte, we write this. + for (auto& byte : kAppBytes) + { + if (byte == 0) + continue; + + if (byte == 0xFF) + { + byte = 0; + } + + file_ptr_out << reinterpret_cast<const char*>(&byte)[0]; + } + + if (kVerbose) + kStdOut << "AssemblerAMD64: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) + kStdOut << "AssemblerAMD64: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) + kStdOut << "AssemblerAMD64: Exit failed.\n"; + + return 1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string& line) +{ + // extern_segment is the opposite of public_segment, it signals to the ld + // that we need this symbol. + if (LibCompiler::find_word(line, "extern_segment")) + { + if (kOutputAsBinary) + { + Details::print_error("Invalid directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_extern_segment_bin"); + } + + auto name = line.substr(line.find("extern_segment") + strlen("extern_segment") + 1); + + if (name.size() == 0) + { + Details::print_error("Invalid extern_segment", "power-as"); + throw std::runtime_error("invalid_extern_segment"); + } + + 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(kPefCode64) != std::string::npos) + { + // data is treated as code. + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + else if (name.find(kPefData64) != std::string::npos) + { + // no code will be executed from here. + kCurrentRecord.fKind = LibCompiler::kPefData; + } + else if (name.find(kPefZero64) != std::string::npos) + { + // this is a bss section. + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) + { + kCurrentRecord.fKind = LibCompiler::kPefCode; + } + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) + kRecords[kRecords.size() - 1].fSize = kAppBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, result.c_str(), result.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // public_segment is a special keyword used by AssemblerAMD64 to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64 and + // .zero64. + else if (LibCompiler::find_word(line, "public_segment")) + { + if (kOutputAsBinary) + { + Details::print_error("Invalid directive in flat binary mode.", "LibCompiler"); + throw std::runtime_error("invalid_public_segment_bin"); + } + + auto name = line.substr(line.find("public_segment") + strlen("public_segment") + 1); + + std::string name_copy = name; + + for (char& j : name) + { + if (j == ' ') + j = '$'; + } + + if (std::find(kDefinedSymbols.begin(), kDefinedSymbols.end(), name) != + kDefinedSymbols.end()) + { + Details::print_error("Symbol already defined.", "LibCompiler"); + throw std::runtime_error("invalid_public_segment_bin"); + } + + kDefinedSymbols.push_back(name); + + if (name.find(".code64") != std::string::npos) + { + // data is treated as code. + + name_copy.erase(name_copy.find(".code64"), strlen(".code64")); + kCurrentRecord.fKind = LibCompiler::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 = LibCompiler::kPefData; + } + else if (name.find(".zero64") != std::string::npos) + { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) + { + kCurrentRecord.fKind = LibCompiler::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 = kAppBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, name.c_str(), name.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace Details::algorithm +{ + // \brief authorize a brief set of characters. + static inline bool is_not_valid(char c) + { + if ((isalpha(c) || isdigit(c)) || ((c == ' ') || (c == '\t') || + (c == ',') || (c == '(') || (c == ')') || (c == '"') || (c == '*') || + (c == '\'') || (c == '[') || (c == ']') || (c == '+') || + (c == '_') || (c == ':') || (c == '@') || (c == '.') || (c == '#') || (c == ';'))) + return false; + + return true; + } + + bool is_valid_amd64(const std::string& str) + { + return std::find_if(str.begin(), str.end(), is_not_valid) == str.end(); + } +} // namespace Details::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string LibCompiler::EncoderAMD64::CheckLine(std::string& line, + const std::string& file) +{ + std::string err_str; + + if (line.empty() || LibCompiler::find_word(line, "extern_segment") || + LibCompiler::find_word(line, "public_segment") || + LibCompiler::find_word(line, kAssemblerPragmaSymStr) || + LibCompiler::find_word(line, ";") || line[0] == kAssemblerPragmaSym) + { + if (line.find(';') != std::string::npos) + { + line.erase(line.find(';')); + } + else + { + // now check the line for validity + if (!Details::algorithm::is_valid_amd64(line)) + { + err_str = "Line contains non valid 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; + } + } + } + for (auto& opcodeAMD64 : kOpcodesAMD64) + { + if (LibCompiler::find_word(line, opcodeAMD64.fName)) + { + return err_str; + } + } + + err_str += "\nUnrecognized instruction -> " + line; + + return err_str; +} + +bool LibCompiler::EncoderAMD64::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) + { + Details::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + LibCompiler::NumberCast64 num = LibCompiler::NumberCast64( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: 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) + { + Details::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast64 num = LibCompiler::NumberCast64( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 2 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); + !res) + { + if (errno != 0) + { + Details::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast64 num = LibCompiler::NumberCast64( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 8 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.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; + } + } + + LibCompiler::NumberCast64 num = LibCompiler::NumberCast64( + strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +bool LibCompiler::EncoderAMD64::WriteNumber32(const std::size_t& pos, + std::string& jump_label) +{ + if (!isdigit(jump_label[pos])) + return false; + + switch (jump_label[pos + 1]) + { + case 'x': { + auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); + res += kOrigin; + + if (errno != 0) + { + return false; + } + + LibCompiler::NumberCast32 num = LibCompiler::NumberCast32(res); + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 16 number here: " + << jump_label.substr(pos) << "\n"; + } + + return true; + } + case 'b': { + auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); + res += kOrigin; + + if (errno != 0) + { + return false; + } + + LibCompiler::NumberCast32 num = LibCompiler::NumberCast32(res); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 2 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + case 'o': { + auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); + res += kOrigin; + + if (errno != 0) + { + return false; + } + + LibCompiler::NumberCast32 num = LibCompiler::NumberCast32(res); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 8 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + default: { + break; + } + } + + auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 10); + res += kOrigin; + + if (errno != 0) + { + return false; + } + + LibCompiler::NumberCast32 num = LibCompiler::NumberCast32(res); + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +bool LibCompiler::EncoderAMD64::WriteNumber16(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) + { + Details::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + LibCompiler::NumberCast16 num = LibCompiler::NumberCast16( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: 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) + { + Details::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast16 num = LibCompiler::NumberCast16( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 2 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); + !res) + { + if (errno != 0) + { + Details::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast16 num = LibCompiler::NumberCast16( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 8 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.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; + } + } + + LibCompiler::NumberCast16 num = LibCompiler::NumberCast16( + strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +bool LibCompiler::EncoderAMD64::WriteNumber8(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) + { + Details::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + LibCompiler::NumberCast8 num = LibCompiler::NumberCast8( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + kAppBytes.push_back(num.number); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: 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) + { + Details::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast8 num = LibCompiler::NumberCast8( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 2 number here: " + << jump_label.substr(pos) << "\n"; + } + + kAppBytes.push_back(num.number); + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); + !res) + { + if (errno != 0) + { + Details::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast8 num = LibCompiler::NumberCast8( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 8 number here: " + << jump_label.substr(pos) << "\n"; + } + + kAppBytes.push_back(num.number); + + 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; + } + } + + LibCompiler::NumberCast8 num = LibCompiler::NumberCast8( + strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + kAppBytes.push_back(num.number); + + if (kVerbose) + { + kStdOut << "AssemblerAMD64: Found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool LibCompiler::EncoderAMD64::WriteLine(std::string& line, + const std::string& file) +{ + if (LibCompiler::find_word(line, "public_segment ")) + return true; + + struct RegMapAMD64 + { + std::string fName; + i64_byte_t fModRM; + }; + + std::vector<RegMapAMD64> kRegisterList{ + {.fName = "ax", .fModRM = 0x0}, + {.fName = "cx", .fModRM = 1}, + {.fName = "dx", .fModRM = 0x2}, + {.fName = "bx", .fModRM = 3}, + {.fName = "sp", .fModRM = 0x4}, + {.fName = "bp", .fModRM = 5}, + {.fName = "si", .fModRM = 0x6}, + {.fName = "di", .fModRM = 7}, + {.fName = "r8", .fModRM = 8}, + {.fName = "r13", .fModRM = 9}, + {.fName = "r9", .fModRM = 10}, + {.fName = "r14", .fModRM = 11}, + {.fName = "r10", .fModRM = 12}, + {.fName = "r15", .fModRM = 13}, + {.fName = "r11", .fModRM = 14}, + }; + + bool foundInstruction = false; + + for (auto& opcodeAMD64 : kOpcodesAMD64) + { + // strict check here + if (LibCompiler::find_word(line, opcodeAMD64.fName) && + Details::algorithm::is_valid_amd64(line)) + { + foundInstruction = true; + std::string name(opcodeAMD64.fName); + + /// Move instruction handler. + if (line.find(name) != std::string::npos && + name == "mov") + { + std::string substr = line.substr(line.find(name) + name.size()); + + uint64_t bits = kRegisterBitWidth; + + if (substr.find(",") == std::string::npos) + { + Details::print_error("Syntax error: missing right operand.", "LibCompiler"); + throw std::runtime_error("syntax_err"); + } + + bool onlyOneReg = true; + + std::vector<RegMapAMD64> currentRegList; + + for (auto& reg : kRegisterList) + { + std::vector<char> regExt = {'e', 'r'}; + + for (auto& ext : regExt) + { + std::string registerName; + + if (bits > 16) + registerName.push_back(ext); + + registerName += reg.fName; + + while (line.find(registerName) != std::string::npos) + { + line.erase(line.find(registerName), registerName.size()); + + if (bits == 16) + { + if (registerName[0] == 'r') + { + Details::print_error( + "invalid size for register, current bit width is: " + + std::to_string(kRegisterBitWidth), + file); + throw std::runtime_error("invalid_reg_size"); + } + } + + currentRegList.push_back( + {.fName = registerName, .fModRM = reg.fModRM}); + } + } + } + + if (currentRegList.size() > 1) + onlyOneReg = false; + + bool hasRBasedRegs = false; + + if (!onlyOneReg) + { + /// very tricky to understand. + /// but this checks for a r8 through r15 register. + if (currentRegList[0].fName[0] == 'r' || + currentRegList[1].fName[0] == 'r') + { + if (isdigit(currentRegList[0].fName[1]) && + isdigit(currentRegList[1].fName[1])) + { + kAppBytes.emplace_back(0x4d); + hasRBasedRegs = true; + } + else if (isdigit(currentRegList[0].fName[1]) || + isdigit(currentRegList[1].fName[1])) + { + kAppBytes.emplace_back(0x4c); + hasRBasedRegs = true; + } + } + } + + if (bits == 64 || bits == 32) + { + if (!hasRBasedRegs && bits >= 32) + { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + } + + if (!onlyOneReg) + kAppBytes.emplace_back(0x89); + } + else if (bits == 16) + { + if (hasRBasedRegs) + { + Details::print_error( + "Invalid combination of operands and registers.", "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + else + { + kAppBytes.emplace_back(0x66); + kAppBytes.emplace_back(0x89); + } + } + + if (onlyOneReg) + { + auto num = GetNumber32(line, ","); + + for (auto& num_idx: num.number) + { + if (num_idx == 0) + num_idx = 0xFF; + } + + auto modrm = (0x3 << 6 | + currentRegList[0].fModRM); + + kAppBytes.emplace_back(0xC7); // prefixed before placing the modrm and then the number. + kAppBytes.emplace_back(modrm); + kAppBytes.emplace_back(num.number[0]); + kAppBytes.emplace_back(num.number[1]); + kAppBytes.emplace_back(num.number[2]); + kAppBytes.emplace_back(num.number[3]); + + break; + } + + if (currentRegList[1].fName[0] == 'r' && + currentRegList[0].fName[0] == 'e') + { + Details::print_error("Invalid combination of operands and registers.", + "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + + if (currentRegList[0].fName[0] == 'r' && + currentRegList[1].fName[0] == 'e') + { + Details::print_error("Invalid combination of operands and registers.", + "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + + if (bits == 16) + { + if (currentRegList[0].fName[0] == 'r' || + currentRegList[0].fName[0] == 'e') + { + Details::print_error("Invalid combination of operands and registers.", + "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + + if (currentRegList[1].fName[0] == 'r' || + currentRegList[1].fName[0] == 'e') + { + Details::print_error("Invalid combination of operands and registers.", + "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + } + else + { + if (currentRegList[0].fName[0] != 'r' || + currentRegList[0].fName[0] == 'e') + { + Details::print_error("Invalid combination of operands and registers.", + "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + + if (currentRegList[1].fName[0] != 'r' || + currentRegList[1].fName[0] == 'e') + { + Details::print_error("Invalid combination of operands and registers.", + "LibCompiler"); + throw std::runtime_error("comb_op_reg"); + } + } + + /// encode register using the modrm encoding. + + auto modrm = (0x3 << 6 | currentRegList[1].fModRM << 3 | + currentRegList[0].fModRM); + + kAppBytes.emplace_back(modrm); + + break; + } + else if (name == "int" || name == "into" || name == "intd") + { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + this->WriteNumber8(line.find(name) + name.size() + 1, line); + + break; + } + else if (name == "jmp" || name == "call") + { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + + if (!this->WriteNumber32(line.find(name) + name.size() + 1, line)) + { + throw std::runtime_error("BUG: WriteNumber32"); + } + + break; + } + else + { + kAppBytes.emplace_back(opcodeAMD64.fOpcode); + + break; + } + } + } + + if (line[0] == kAssemblerPragmaSym) + { + if (foundInstruction) + { + Details::print_error("Syntax error: " + line, "LibCompiler"); + throw std::runtime_error("syntax_err"); + } + + if (line.find("bits 64") != std::string::npos) + { + kRegisterBitWidth = 64U; + } + else if (line.find("bits 32") != std::string::npos) + { + kRegisterBitWidth = 32U; + } + else if (line.find("bits 16") != std::string::npos) + { + kRegisterBitWidth = 16U; + } + else if (line.find("org") != std::string::npos) + { + size_t base[] = {10, 16, 2, 7}; + + for (size_t i = 0; i < 4; i++) + { + if (kOrigin = strtol( + (line.substr(line.find("org") + strlen("org") + 1)).c_str(), + nullptr, base[i]); + kOrigin) + { + if (errno != 0) + { + continue; + } + else + { + if (kVerbose) + { + kStdOut << "AssemblerAMD64: origin set: " << kOrigin << std::endl; + } + + break; + } + } + } + } + } + /// write a dword + else if (line.find(".dword") != std::string::npos) + { + this->WriteNumber32(line.find(".dword") + strlen(".dword") + 1, line); + } + /// write a long + else if (line.find(".long") != std::string::npos) + { + this->WriteNumber(line.find(".long") + strlen(".long") + 1, line); + } + /// write a 16-bit number + else if (line.find(".word") != std::string::npos) + { + this->WriteNumber16(line.find(".word") + strlen(".word") + 1, line); + } + + kOrigin += kIPAlignement; + + return true; +} + +// Last rev 13-1-24 diff --git a/dev/LibCompiler/src/AssemblerPower.cc b/dev/LibCompiler/src/AssemblerPower.cc new file mode 100644 index 0000000..a0b5739 --- /dev/null +++ b/dev/LibCompiler/src/AssemblerPower.cc @@ -0,0 +1,1096 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @file AssemblerPower.cxx +/// @author EL Mahrouss Amlal +/// @brief POWER Assembler. + +/// REMINDER: when dealing with an undefined symbol use (string +/// size):LinkerFindSymbol:(string) so that li will look for it. + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_PPC__ 1 + +#include <LibCompiler/NFC/ErrorID.h> +#include <LibCompiler/AAL/CPU/power64.h> +#include <LibCompiler/NFC/PEF.h> +#include <LibCompiler/Parser.h> +#include <LibCompiler/NFC/AE.h> +#include <LibCompiler/Version.h> +#include <filesystem> +#include <algorithm> +#include <iostream> +#include <fstream> +#include <vector> + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" +#define kYellow "\e[0;33m" + +#define kStdOut (std::cout << kWhite) +#define kStdErr (std::cout << kRed) + +constexpr auto cPowerIPAlignment = 0x4U; + +static CharType kOutputArch = LibCompiler::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<uint8_t> kBytes; + +static LibCompiler::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = LibCompiler::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector<LibCompiler::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); + +/// Do not move it on top! it uses the assembler detail namespace! +#include <AsmUtils.h> + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @brief POWER assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +TOOLCHAINKIT_MODULE(AssemblerMainPower64) +{ + for (size_t i = 1; i < argc; ++i) + { + if (argv[i][0] == '-') + { + if (strcmp(argv[i], "--ver") == 0 || strcmp(argv[i], "--v") == 0) + { + kStdOut << "AssemblerPower: POWER64 Assembler Driver.\nAssemblerPower: " << kDistVersion << "\nAssemblerPower: " + "Copyright (c) " + "Theater Quality Inc\n"; + return 0; + } + else if (strcmp(argv[i], "--h") == 0) + { + kStdOut << "AssemblerPower: POWER64 Assembler Driver.\nAssemblerPower: Copyright (c) 2024 " + "Theater Quality Inc\n"; + kStdOut << "--version,/v: 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 << "AssemblerPower: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) + { + kStdOut << "AssemblerPower: 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 += kOutputAsBinary ? kBinaryFileExt : 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 << "AssemblerPower: error: " << strerror(errno) << "\n"; + } + } + + std::string line; + + LibCompiler::AEHeader hdr{0}; + + memset(hdr.fPad, kAENullType, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(LibCompiler::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + LibCompiler::EncoderPowerPC asm64; + + while (std::getline(file_ptr, line)) + { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) + { + Details::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(); + Details::print_warning("exit because of: " + what, "LibCompiler"); + } + + std::filesystem::remove(object_output); + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) + { + if (kVerbose) + { + kStdOut << "AssemblerPower: 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 << "AssemblerPower: At least one record is needed to write an object " + "file.\nAssemblerPower: Make one using `public_segment .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& record_hdr : kRecords) + { + record_hdr.fFlags |= LibCompiler::kKindRelocationAtRuntime; + record_hdr.fOffset = record_count; + ++record_count; + + file_ptr_out << record_hdr; + + if (kVerbose) + kStdOut << "AssemblerPower: Wrote record " << record_hdr.fName << "...\n"; + } + + // increment once again, so that we won't lie about the kUndefinedSymbols. + ++record_count; + + for (auto& sym : kUndefinedSymbols) + { + LibCompiler::AERecordHeader undefined_sym{0}; + + if (kVerbose) + kStdOut << "AssemblerPower: Wrote symbol " << sym << " to file...\n"; + + undefined_sym.fKind = kAENullType; + undefined_sym.fSize = sym.size(); + undefined_sym.fOffset = record_count; + + ++record_count; + + memset(undefined_sym.fPad, kAENullType, kAEPad); + memcpy(undefined_sym.fName, sym.c_str(), sym.size()); + + file_ptr_out << undefined_sym; + + ++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 << "AssemblerPower: 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 << "AssemblerPower: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) + kStdOut << "AssemblerPower: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) + kStdOut << "AssemblerPower: Exit failed.\n"; + + return TOOLCHAINKIT_EXEC_ERROR; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string& line) +{ + // extern_segment is the opposite of public_segment, it signals to the li + // that we need this symbol. + if (LibCompiler::find_word(line, "extern_segment")) + { + if (kOutputAsBinary) + { + Details::print_error("Invalid extern_segment directive in flat binary mode.", + "LibCompiler"); + throw std::runtime_error("invalid_extern_segment_bin"); + } + + auto name = line.substr(line.find("extern_segment") + strlen("extern_segment") + 1); + + if (name.size() == 0) + { + Details::print_error("Invalid extern_segment", "LibCompiler"); + throw std::runtime_error("invalid_extern_segment"); + } + + 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 = LibCompiler::kPefCode; + } + else if (name.find(".data64") != std::string::npos) + { + // no code will be executed from here. + kCurrentRecord.fKind = LibCompiler::kPefData; + } + else if (name.find(".zero64") != std::string::npos) + { + // this is a bss section. + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that li can find it. + + if (name == kPefStart) + { + kCurrentRecord.fKind = LibCompiler::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, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // public_segment is a special keyword used by AssemblerPower to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64., + // .zero64 + else if (LibCompiler::find_word(line, "public_segment")) + { + if (kOutputAsBinary) + { + Details::print_error("Invalid public_segment directive in flat binary mode.", + "LibCompiler"); + throw std::runtime_error("invalid_public_segment_bin"); + } + + auto name = line.substr(line.find("public_segment") + strlen("public_segment")); + + 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 = LibCompiler::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 = LibCompiler::kPefData; + } + else if (name.find(".zero64") != std::string::npos) + { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = LibCompiler::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that li can find it. + + if (name == kPefStart) + { + kCurrentRecord.fKind = LibCompiler::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, kAENullType, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace Details::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_power64(const std::string& str) + { + return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); + } +} // namespace Details::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string LibCompiler::EncoderPowerPC::CheckLine(std::string& line, + const std::string& file) +{ + std::string err_str; + + if (line.empty() || LibCompiler::find_word(line, "extern_segment") || + LibCompiler::find_word(line, "public_segment") || + line.find('#') != std::string::npos || LibCompiler::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 + { + /// does the line contains valid input? + if (!Details::algorithm::is_valid_power64(line)) + { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + } + } + + return err_str; + } + + if (!Details::algorithm::is_valid_power64(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", "li"}; + + // these don't. + std::vector<std::string> filter_inst = {"blr", "bl", "sc"}; + + for (auto& opcodePPC : kOpcodesPowerPC) + { + if (LibCompiler::find_word(line, opcodePPC.name)) + { + 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(), opcodePPC.name); + it == filter_inst.cend()) + { + if (LibCompiler::find_word(line, opcodePPC.name)) + { + if (!isspace( + line[line.find(opcodePPC.name) + strlen(opcodePPC.name)])) + { + err_str += "\nMissing space between "; + err_str += opcodePPC.name; + err_str += " and operands.\nhere -> "; + err_str += line; + } + } + } + + return err_str; + } + } + + err_str += "Unrecognized instruction: " + line; + + return err_str; +} + +bool LibCompiler::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) + { + Details::print_error("invalid hex number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + LibCompiler::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char& i : num.number) + { + kBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "AssemblerPower: 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) + { + Details::print_error("invalid binary number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + LibCompiler::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) + { + kStdOut << "AssemblerPower: 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) + { + Details::print_error("invalid octal number: " + jump_label, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + LibCompiler::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) + { + kStdOut << "AssemblerPower: 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; + } + } + + LibCompiler::NumberCast64 num( + strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char& i : num.number) + { + kBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "AssemblerPower: found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool LibCompiler::EncoderPowerPC::WriteLine(std::string& line, + const std::string& file) +{ + if (LibCompiler::find_word(line, "public_segment")) + return true; + if (!Details::algorithm::is_valid_power64(line)) + return true; + + for (auto& opcodePPC : kOpcodesPowerPC) + { + // strict check here + if (LibCompiler::find_word(line, opcodePPC.name)) + { + std::string name(opcodePPC.name); + std::string jump_label, cpy_jump_label; + std::vector<size_t> found_registers_index; + + // check funct7 type. + switch (opcodePPC.ops->type) + { + default: { + NumberCast32 num(opcodePPC.opcode); + + for (auto ch : num.number) + { + kBytes.emplace_back(ch); + } + break; + } + case BADDR: + case PCREL: { + auto num = GetNumber32(line, name); + + kBytes.emplace_back(num.number[0]); + kBytes.emplace_back(num.number[1]); + kBytes.emplace_back(num.number[2]); + kBytes.emplace_back(0x48); + + break; + } + /// General purpose, float, vector operations. Everything that involve + /// registers. + case G0REG: + case FREG: + case VREG: + case GREG: { + // \brief how many registers we found. + std::size_t found_some_count = 0UL; + std::size_t register_count = 0UL; + std::string opcodeName = opcodePPC.name; + std::size_t register_sum = 0; + + NumberCast64 num(opcodePPC.opcode); + + 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 (isdigit(line[line_index + 3]) && + isdigit(line[line_index + 2])) + { + reg_str += line[line_index + 3]; + Details::print_error( + "invalid register index, r" + reg_str + + "\nnote: The POWER accepts registers from r0 to r32.", + 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) + { + Details::print_error("invalid register index, r" + reg_str, + file); + throw std::runtime_error("invalid_register_index"); + } + + if (opcodeName == "li") + { + char numIndex = 0; + + for (size_t i = 0; i != reg_index; i++) + { + numIndex += 0x20; + } + + auto num = GetNumber32(line, reg_str); + + kBytes.push_back(num.number[0]); + kBytes.push_back(num.number[1]); + kBytes.push_back(numIndex); + kBytes.push_back(0x38); + + // check if bigger than two. + for (size_t i = 2; i < 4; i++) + { + if (num.number[i] > 0) + { + Details::print_warning("number overflow on li operation.", + file); + break; + } + } + + break; + } + + if ((opcodeName[0] == 's' && opcodeName[1] == 't')) + { + if (register_sum == 0) + { + for (size_t indexReg = 0UL; indexReg < reg_index; + ++indexReg) + { + register_sum += 0x20; + } + } + else + { + register_sum += reg_index; + } + } + + if (opcodeName == "mr") + { + switch (register_count) + { + case 0: { + kBytes.push_back(0x78); + + char numIndex = 0x3; + + for (size_t i = 0; i != reg_index; i++) + { + numIndex += 0x8; + } + + kBytes.push_back(numIndex); + + break; + } + case 1: { + char numIndex = 0x1; + + for (size_t i = 0; i != reg_index; i++) + { + numIndex += 0x20; + } + + for (size_t i = 0; i != reg_index; i++) + { + kBytes[kBytes.size() - 1] += 0x8; + } + + kBytes[kBytes.size() - 1] -= 0x8; + + kBytes.push_back(numIndex); + + if (reg_index >= 10 && reg_index < 20) + kBytes.push_back(0x7d); + else if (reg_index >= 20 && reg_index < 30) + kBytes.push_back(0x7e); + else if (reg_index >= 30) + kBytes.push_back(0x7f); + else + kBytes.push_back(0x7c); + + break; + } + default: + break; + } + + ++register_count; + ++found_some_count; + } + + if (opcodeName == "addi") + { + if (found_some_count == 2 || found_some_count == 0) + kBytes.emplace_back(reg_index); + else if (found_some_count == 1) + kBytes.emplace_back(0x00); + + ++found_some_count; + + if (found_some_count > 3) + { + Details::print_error("Too much registers. -> " + line, file); + throw std::runtime_error("too_much_regs"); + } + } + + if (opcodeName.find("cmp") != std::string::npos) + { + ++found_some_count; + + if (found_some_count > 3) + { + Details::print_error("Too much registers. -> " + line, file); + throw std::runtime_error("too_much_regs"); + } + } + + if (opcodeName.find("mf") != std::string::npos || + opcodeName.find("mt") != std::string::npos) + { + char numIndex = 0; + + for (size_t i = 0; i != reg_index; i++) + { + numIndex += 0x20; + } + + num.number[2] += numIndex; + + ++found_some_count; + + if (found_some_count > 1) + { + Details::print_error("Too much registers. -> " + line, file); + throw std::runtime_error("too_much_regs"); + } + + if (kVerbose) + { + kStdOut << "AssemblerPower: Found register: " << register_syntax + << "\n"; + kStdOut << "AssemblerPower: Amount of registers in instruction: " + << found_some_count << "\n"; + } + + if (reg_index >= 10 && reg_index < 20) + num.number[3] = 0x7d; + else if (reg_index >= 20 && reg_index < 30) + num.number[3] = 0x7e; + else if (reg_index >= 30) + num.number[3] = 0x7f; + else + num.number[3] = 0x7c; + + for (auto ch : num.number) + { + kBytes.emplace_back(ch); + } + } + + found_registers_index.push_back(reg_index); + } + } + + if (opcodeName == "addi") + { + kBytes.emplace_back(0x38); + } + + if (opcodeName.find("cmp") != std::string::npos) + { + char rightReg = 0x0; + + for (size_t i = 0; i != found_registers_index[1]; i++) + { + rightReg += 0x08; + } + + kBytes.emplace_back(0x00); + kBytes.emplace_back(rightReg); + kBytes.emplace_back(found_registers_index[0]); + kBytes.emplace_back(0x7c); + } + + if ((opcodeName[0] == 's' && opcodeName[1] == 't')) + { + size_t offset = 0UL; + + if (line.find('+') != std::string::npos) + { + auto number = GetNumber32(line.substr(line.find("+")), "+"); + offset = number.raw; + } + + kBytes.push_back(offset); + kBytes.push_back(0x00); + kBytes.push_back(register_sum); + + kBytes.emplace_back(0x90); + } + + if (opcodeName == "mr") + { + if (register_count == 1) + { + Details::print_error("Too few registers. -> " + line, file); + throw std::runtime_error("too_few_registers"); + } + } + + // we're not in immediate addressing, reg to reg. + if (opcodePPC.ops->type != GREG) + { + // remember! register to register! + if (found_some_count == 1) + { + Details::print_error( + "Unrecognized register found.\ntip: each AssemblerPower register " + "starts with 'r'.\nline: " + + line, + file); + + throw std::runtime_error("not_a_register"); + } + } + + if (found_some_count < 1 && name[0] != 'l' && name[0] != 's') + { + Details::print_error( + "invalid combination of opcode and registers.\nline: " + line, + file); + throw std::runtime_error("invalid_comb_op_reg"); + } + + break; + } + } + + kOrigin += cPowerIPAlignment; + break; + } + } + + return true; +} + +// Last rev 13-1-24 diff --git a/dev/LibCompiler/src/AssemblyFactory.cc b/dev/LibCompiler/src/AssemblyFactory.cc new file mode 100644 index 0000000..a0d7bd8 --- /dev/null +++ b/dev/LibCompiler/src/AssemblyFactory.cc @@ -0,0 +1,59 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#include <LibCompiler/AAL/AssemblyInterface.h> +#include <LibCompiler/NFC/ErrorID.h> + +/** + * @file AssemblyFactory.cxx + * @author amlal (amlal@zka.com) + * @brief Assembler Kit + * @version 0.1 + * @date 2024-01-27 + * + * @copyright Copyright (c) 2024 Theater Quality Inc + * + */ + +#include <iostream> + +//! @file Asm.cpp +//! @brief AssemblyKit source implementation. + +namespace LibCompiler +{ + ///! @brief Compile for specific format (ELF, PEF, ZBIN) + Int32 AssemblyFactory::Compile(std::string& sourceFile, + const Int32& arch) noexcept + { + if (sourceFile.length() < 1 || !fMounted) + return TOOLCHAINKIT_UNIMPLEMENTED; + + return fMounted->CompileToFormat(sourceFile, arch); + } + + ///! @brief mount assembly backend. + void AssemblyFactory::Mount(AssemblyInterface* mountPtr) noexcept + { + if (mountPtr) + { + fMounted = mountPtr; + } + } + + ///! @brief Unmount assembler. + AssemblyInterface* AssemblyFactory::Unmount() noexcept + { + auto mount_prev = fMounted; + + if (mount_prev) + { + fMounted = nullptr; + } + + return mount_prev; + } +} // namespace LibCompiler diff --git a/dev/LibCompiler/src/CCompiler64x0.cc b/dev/LibCompiler/src/CCompiler64x0.cc new file mode 100644 index 0000000..e831401 --- /dev/null +++ b/dev/LibCompiler/src/CCompiler64x0.cc @@ -0,0 +1,1596 @@ +/* + * ======================================================== + * + * cc + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +/// BUGS: 0 +/// TODO: none + +#include <LibCompiler/AAL/CPU/64x0.h> +#include <LibCompiler/Parser.h> +#include <LibCompiler/UUID.h> +#include <cstdio> +#include <fstream> +#include <iostream> +#include <memory> +#include <random> +#include <string> +#include <utility> +#include <vector> + +/* C driver */ +/* This is part of the LibCompiler. */ +/* (c) Theater Quality Incorporated */ + +/// @author EL Mahrouss Amlal (amlel) +/// @file 64x0-cc.cxx +/// @brief 64x0 C Compiler. + +/// TODO: support structures, else if, else, . and -> + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kExitOK (0) + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" + +///////////////////////////////////// + +// INTERNAL STUFF OF THE C COMPILER + +///////////////////////////////////// + +namespace Details +{ + // \brief Register map structure, used to keep track of each variable's registers. + 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<LibCompiler::SyntaxLeafList> fSyntaxTreeList; + std::vector<CompilerRegisterMap> kStackFrame; + std::vector<CompilerStructMap> kStructMap; + LibCompiler::SyntaxLeafList* fSyntaxTree{nullptr}; + std::unique_ptr<std::ofstream> fOutputAssembly; + std::string fLastFile; + std::string fLastError; + bool fVerbose; + }; +} // namespace Details + +static Details::CompilerState kState; +static SizeType kErrorLimit = 100; +static std::string kIfFunction = ""; +static Int32 kAcceptableErrors = 0; + +namespace Details +{ + /// @brief prints an error into stdout. + /// @param reason the reason of the error. + /// @param file where does it originate from? + void print_error(std::string reason, std::string file) noexcept; + + struct CompilerType final + { + std::string fName; + std::string fValue; + }; +} // namespace Details + +///////////////////////////////////////////////////////////////////////////////////////// + +// 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 LibCompiler::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 CompilerFrontend64x0 final : public LibCompiler::ICompilerFrontend +{ +public: + explicit CompilerFrontend64x0() = default; + ~CompilerFrontend64x0() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(CompilerFrontend64x0); + + std::string Check(const char* text, const char* file); + bool Compile(const std::string text, const std::string file) override; + + const char* Language() override + { + return "64k C"; + } +}; + +static CompilerFrontend64x0* kCompilerFrontend = nullptr; +static std::vector<Details::CompilerType> kCompilerVariables; +static std::vector<std::string> kCompilerFunctions; +static std::vector<Details::CompilerType> kCompilerTypes; + +namespace Details +{ + 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 Details + +///////////////////////////////////////////////////////////////////////////////////////// + +// @name Compile +// @brief Generate MASM from a C assignement. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerFrontend64x0::Compile(const std::string text, const std::string 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 = LibCompiler::SyntaxLeafList::SyntaxLeaf(); + + auto gen = uuids::uuid_random_generator{generator}; + uuids::uuid out = gen(); + + Details::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 += " extern_segment"; + 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 = "__TOOLCHAINKIT_IF_PROC_"; + kIfFunction += std::to_string(time_off._Raw); + + syntaxLeaf.fUserValue = "\tlda r12, extern_segment "; + syntaxLeaf.fUserValue += + kIfFunction + + "\n\t#r12 = Code to jump on, r11 right cond, r10 left cond.\n\tbeq " + "r10, r11, r12\ndword public_segment .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) + { + Details::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 public_segment .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("public_segment .data64") == std::string::npos && + !kInStruct) + substr += "public_segment .data64 "; + } + + ++first_encountered; + + continue; + } + + if (textBuffer[text_index_2] == '=') + { + if (!kInBraces) + { + substr.replace(substr.find("public_segment .data64"), + strlen("public_segment .data64"), "public_segment .zero64 "); + } + + 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"), "extern_segment "); + + if (substr.find("public_segment .data64") != std::string::npos) + substr.erase(substr.find("public_segment .data64"), strlen("public_segment .data64")); + } + + auto var_to_find = + std::find_if(kCompilerVariables.cbegin(), kCompilerVariables.cend(), + [&](Details::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 += "public_segment .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 += "sub "; + 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 = LibCompiler::SyntaxLeafList::SyntaxLeaf(); + syntaxLeaf.fUserValue = "\n"; + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + return true; +} + +static bool kShouldHaveBraces = false; +static std::string kFnName; + +std::string CompilerFrontend64x0::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 extern_segments a variable. + // so that's why it's not declare upper. + if (LibCompiler::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 (LibCompiler::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 && !LibCompiler::find_word(ln, "|") && + !LibCompiler::find_word(ln, "||") && !LibCompiler::find_word(ln, "&") && + !LibCompiler::find_word(ln, "&&") && !LibCompiler::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 AssemblyCCInterface final ASSEMBLY_INTERFACE +{ +public: + explicit AssemblyCCInterface() = default; + ~AssemblyCCInterface() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(AssemblyCCInterface); + + [[maybe_unused]] static Int32 Arch() noexcept + { + return LibCompiler::AssemblyFactory::kArch64x0; + } + + Int32 CompileToFormat(std::string& src, Int32 arch) override + { + if (arch != AssemblyCCInterface::Arch()) + return 1; + + if (kCompilerFrontend == 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 = LibCompiler::current_date(); + + (*kState.fOutputAssembly) << "# Path: " << src_file << "\n"; + (*kState.fOutputAssembly) + << "# Language: 64x0 Assembly (Generated from ANSI C)\n"; + (*kState.fOutputAssembly) << "# Date: " << fmt << "\n\n"; + + LibCompiler::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 = kCompilerFrontend->Check(line_src.c_str(), src.data()); + err.empty()) + { + kCompilerFrontend->Compile(line_src, src.data()); + } + else + { + Details::print_error(err, src.data()); + } + } + + if (kAcceptableErrors > 0) + return 1; + + std::vector<std::string> keywords = {"ldw", "stw", "lda", "sta", + "add", "sub", "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 (LibCompiler::find_word(leaf.fUserValue, access_ident)) + { + for (auto& struc : kState.kStructMap) + { + /// TODO: + } + } + } + + for (auto& keyword : keywords) + { + if (LibCompiler::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 (LibCompiler::find_word(leaf.fUserValue, needle)) + { + if (leaf.fUserValue.find("extern_segment " + needle) != + std::string::npos) + { + std::string range = "extern_segment " + needle; + leaf.fUserValue.replace( + leaf.fUserValue.find("extern_segment " + 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 != "sub") + { + 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 kExitOK; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +#include <Version.h> + +#define kPrintF printf +#define kSplashCxx() \ + kPrintF(kWhite "ZKA C Driver, %s, (c) Theater Quality Incorporated\n", kDistVersion) + +static void cc_print_help() +{ + kSplashCxx(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define kExt ".c" + +TOOLCHAINKIT_MODULE(NewOSCompilerCLang64x0) +{ + 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 AssemblyCCInterface()); + kMachine = LibCompiler::AssemblyFactory::kArch64x0; + kCompilerFrontend = new CompilerFrontend64x0(); + + 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 kExitOK; + } + + 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 kExitOK; + } + + if (strcmp(argv[index], "--dialect") == 0) + { + if (kCompilerFrontend) + std::cout << kCompilerFrontend->Language() << "\n"; + + return kExitOK; + } + + 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]; + + Details::print_error(err, "cc"); + + continue; + } + + kFileList.emplace_back(argv[index]); + + std::string srcFile = argv[index]; + + if (strstr(argv[index], kExt) == nullptr) + { + if (kState.fVerbose) + { + Details::print_error(srcFile + " is not a valid C source.\n", "cc"); + } + + return 1; + } + + if (kFactory.Compile(srcFile, kMachine) != kExitOK) + return 1; + } + + return kExitOK; +} + +// Last rev 8-1-24 diff --git a/dev/LibCompiler/src/CCompilerPower64.cc b/dev/LibCompiler/src/CCompilerPower64.cc new file mode 100644 index 0000000..99a3d0f --- /dev/null +++ b/dev/LibCompiler/src/CCompilerPower64.cc @@ -0,0 +1,1616 @@ +/* + * ======================================================== + * + * CCompilerPower64 + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +#include <LibCompiler/AAL/CPU/power64.h> +#include <LibCompiler/Parser.h> +#include <LibCompiler/UUID.h> +#include <fstream> +#include <iostream> +#include <memory> +#include <random> +#include <string> +#include <utility> +#include <vector> +#include <cstdio> + +#define kExitOK 0 + +/// @author EL Mahrouss Amlal (amlel) +/// @file cc.cxx +/// @brief POWER64 C Compiler. + +///////////////////// + +/// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" + +///////////////////////////////////// + +/// INTERNAL STRUCTURES OF THE C COMPILER + +///////////////////////////////////// + +namespace Details +{ + // \brief name to register struct. + struct CompilerRegisterMap final + { + std::string fName; + std::string fReg; + }; + + // \brief Map for C structs + // \author amlel + struct CompilerStructMap final + { + /// 'struct::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<LibCompiler::SyntaxLeafList> fSyntaxTreeList; + std::vector<CompilerRegisterMap> kStackFrame; + std::vector<CompilerStructMap> kStructMap; + LibCompiler::SyntaxLeafList* fSyntaxTree{nullptr}; + std::unique_ptr<std::ofstream> fOutputAssembly; + std::string fLastFile; + std::string fLastError; + bool fVerbose; + }; +} // namespace Details + +static Details::CompilerState kState; +static SizeType kErrorLimit = 100; +static std::string kIfFunction = ""; +static Int32 kAcceptableErrors = 0; + +namespace Details +{ + /// @brief prints an error into stdout. + /// @param reason the reason of the error. + /// @param file where does it originate from? + void print_error(std::string reason, std::string file) noexcept; + + struct CompilerType final + { + std::string fName; + std::string fValue; + }; +} // namespace Details + +///////////////////////////////////////////////////////////////////////////////////////// + +// 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 LibCompiler::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 CompilerFrontendPower64 final : public LibCompiler::ICompilerFrontend +{ +public: + explicit CompilerFrontendPower64() = default; + ~CompilerFrontendPower64() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(CompilerFrontendPower64); + + std::string Check(const char* text, const char* file); + bool Compile(const std::string text, const std::string file) override; + + const char* Language() override + { + return "POWER C"; + } +}; + +static CompilerFrontendPower64* kCompilerFrontend = nullptr; +static std::vector<Details::CompilerType> kCompilerVariables; +static std::vector<std::string> kCompilerFunctions; +static std::vector<Details::CompilerType> kCompilerTypes; + +namespace Details +{ + 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 Details + +///////////////////////////////////////////////////////////////////////////////////////// + +// @name Compile +// @brief Generate MASM from a C assignement. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerFrontendPower64::Compile(const std::string text, const std::string 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 = LibCompiler::SyntaxLeafList::SyntaxLeaf(); + + auto gen = uuids::uuid_random_generator{generator}; + uuids::uuid out = gen(); + + Details::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 += " extern_segment"; + value += tmp; + } + + syntaxLeaf.fUserValue = "\tmr r31, "; + + // make it pretty. + while (value.find('\t') != std::string::npos) + value.erase(value.find('\t'), 1); + + while (value.find(' ') != std::string::npos) + value.erase(value.find(' '), 1); + + while (value.find("extern_segment") != std::string::npos) + value.erase(value.find("extern_segment"), strlen("extern_segment")); + + bool found = false; + + for (auto& reg : kState.kStackFrame) + { + if (value.find(reg.fName) != std::string::npos) + { + found = true; + syntaxLeaf.fUserValue += reg.fReg; + break; + } + } + + if (!found) + syntaxLeaf.fUserValue += "r0"; + } + + syntaxLeaf.fUserValue += "\n\tblr"; + + 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 = "__TOOLCHAINKIT_IF_PROC_"; + kIfFunction += std::to_string(time_off._Raw); + + syntaxLeaf.fUserValue = + "\tcmpw " + "r10, r11"; + + syntaxLeaf.fUserValue += "\n\tbeq extern_segment " + kIfFunction + + " \ndword public_segment .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) + { + Details::print_error( + "assignement of value inside a 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 += "\tli "; + else + substr += "\tli "; + } + else + { + substr += "\tli "; + } + } + else if (textBuffer.find('=') != std::string::npos && !kInBraces) + { + substr += "stw public_segment .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("public_segment .data64") == std::string::npos && + !kInStruct) + substr += "public_segment .data64 "; + } + + ++first_encountered; + + continue; + } + + if (textBuffer[text_index_2] == '=') + { + if (!kInBraces) + { + substr.replace(substr.find("public_segment .data64"), + strlen("public_segment .data64"), "public_segment .zero64 "); + } + + 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"), "extern_segment "); + + if (substr.find("public_segment .data64") != std::string::npos) + substr.erase(substr.find("public_segment .data64"), strlen("public_segment .data64")); + } + + auto var_to_find = + std::find_if(kCompilerVariables.cbegin(), kCompilerVariables.cend(), + [&](Details::CompilerType type) { + return type.fName.find(substr) != std::string::npos; + }); + + kCompilerVariables.push_back({.fName = substr}); + + if (textBuffer[text_index] == ';') + break; + + std::string reg = kAsmRegisterPrefix; + + ++kRegisterCounter; + reg += std::to_string(kRegisterCounter); + + auto newSubstr = substr.substr(substr.find(" ")); + + std::string symbol; + + for (size_t start = 0; start < newSubstr.size(); ++start) + { + if (newSubstr[start] == ',') + break; + + if (newSubstr[start] == ' ') + continue; + + symbol += (newSubstr[start]); + } + + kState.kStackFrame.push_back({.fName = symbol, .fReg = reg}); + + syntaxLeaf.fUserValue += + "\n\tli " + reg + substr.substr(substr.find(',')); + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + } + + // 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\tli r31, "; + } + } + + 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\tblr\n"; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + fnFound = true; + } + else + { + syntaxLeaf.fUserValue.clear(); + + syntaxLeaf.fUserValue += "public_segment .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 = LibCompiler::SyntaxLeafList::SyntaxLeaf(); + syntaxLeaf.fUserValue = "\n"; + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + return true; +} + +static bool kShouldHaveBraces = false; +static std::string kFnName; + +std::string CompilerFrontendPower64::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 extern_segments a variable. + // so that's why it's not declare upper. + if (LibCompiler::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 (LibCompiler::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 && !LibCompiler::find_word(ln, "|") && + !LibCompiler::find_word(ln, "||") && !LibCompiler::find_word(ln, "&") && + !LibCompiler::find_word(ln, "&&") && !LibCompiler::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 ASSEMBLY_INTERFACE +{ +public: + explicit AssemblyMountpointCLang() = default; + ~AssemblyMountpointCLang() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(AssemblyMountpointCLang); + + [[maybe_unused]] static Int32 Arch() noexcept + { + return LibCompiler::AssemblyFactory::kArchPowerPC; + } + + Int32 CompileToFormat(std::string& src, Int32 arch) override + { + if (arch != AssemblyMountpointCLang::Arch()) + return 1; + + if (kCompilerFrontend == 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 = LibCompiler::current_date(); + + (*kState.fOutputAssembly) << "# Path: " << src_file << "\n"; + (*kState.fOutputAssembly) + << "# Language: POWER Assembly (Generated from C)\n"; + (*kState.fOutputAssembly) << "# Date: " << fmt << "\n\n"; + + LibCompiler::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 = kCompilerFrontend->Check(line_src.c_str(), src.data()); + err.empty()) + { + kCompilerFrontend->Compile(line_src, src.data()); + } + else + { + Details::print_error(err, src.data()); + } + } + + if (kAcceptableErrors > 0) + return 1; + + std::vector<std::string> keywords = {"ld", "stw", "add", "sub", "or"}; + + /// + /// Replace, optimize, fix assembly output. + /// + + for (auto& leaf : kState.fSyntaxTree->fLeafList) + { + std::vector<std::string> access_keywords = {"->", "."}; + + for (auto& access_ident : access_keywords) + { + if (LibCompiler::find_word(leaf.fUserValue, access_ident)) + { + for (auto& struc : kState.kStructMap) + { + /// TODO: + } + } + } + + for (auto& keyword : keywords) + { + if (LibCompiler::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 (LibCompiler::find_word(leaf.fUserValue, needle)) + { + if (leaf.fUserValue.find("extern_segment ") != std::string::npos) + { + std::string range = "extern_segment "; + leaf.fUserValue.replace(leaf.fUserValue.find(range), + range.size(), ""); + } + + 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"), "mr"); + } + } + + leaf.fUserValue.replace(leaf.fUserValue.find(needle), + needle.size(), reg.fReg); + + ++cnt; + } + } + + if (cnt > 1 && keyword != "mr" && keyword != "add" && + keyword != "dec") + { + leaf.fUserValue.replace(leaf.fUserValue.find(keyword), + keyword.size(), "mr"); + } + } + } + } + + for (auto& leaf : kState.fSyntaxTree->fLeafList) + { + (*kState.fOutputAssembly) << leaf.fUserValue; + } + + kState.fSyntaxTree = nullptr; + + kState.fOutputAssembly->flush(); + kState.fOutputAssembly.reset(); + + return kExitOK; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +#include <Version.h> + +#define kPrintF printf +#define kSplashCxx() \ + kPrintF(kWhite "cc, %s, (c) Theater Quality Incorporated\n", kDistVersion) + +static void cc_print_help() +{ + kSplashCxx(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define kExt ".c" + +TOOLCHAINKIT_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 = LibCompiler::AssemblyFactory::kArchPowerPC; + kCompilerFrontend = new CompilerFrontendPower64(); + + 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 kExitOK; + } + + 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 kExitOK; + } + + if (strcmp(argv[index], "-dialect") == 0) + { + if (kCompilerFrontend) + std::cout << kCompilerFrontend->Language() << "\n"; + + return kExitOK; + } + + 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]; + + Details::print_error(err, "cc"); + + continue; + } + + kFileList.emplace_back(argv[index]); + + std::string srcFile = argv[index]; + + if (strstr(argv[index], kExt) == nullptr) + { + if (kState.fVerbose) + { + Details::print_error(srcFile + " is not a valid C source.\n", "cc"); + } + + return 1; + } + + if (kFactory.Compile(srcFile, kMachine) != kExitOK) + return 1; + } + + return kExitOK; +} + +// Last rev 8-1-24 diff --git a/dev/LibCompiler/src/CPlusPlusCompilerAMD64.cc b/dev/LibCompiler/src/CPlusPlusCompilerAMD64.cc new file mode 100644 index 0000000..0de1b06 --- /dev/null +++ b/dev/LibCompiler/src/CPlusPlusCompilerAMD64.cc @@ -0,0 +1,1091 @@ +/* + * ======================================================== + * + * c++-drv + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +/// BUGS: 1 + +#include <cstdio> +#define kPrintF printf + +#define kExitOK (EXIT_SUCCESS) +#define kExitNO (EXIT_FAILURE) + +#define kSplashCxx() \ + kPrintF(kWhite "%s\n", "TQ Media C++ Compiler Driver, (c) 2024 Theater Quality Incorporated, all rights reserved.") + +// extern_segment, @autodelete { ... }, fn foo() -> auto { ... } + +#include <LibCompiler/AAL/CPU/amd64.h> +#include <LibCompiler/Parser.h> +#include <LibCompiler/UUID.h> + +/* ZKA C++ Compiler */ +/* This is part of the LibCompiler. */ +/* (c) Theater Quality Incorporated */ + +/// @author EL Mahrouss Amlal (amlel) +/// @file CPlusPlusCompilerAMD64.cxx +/// @brief Optimized C++ Compiler Driver. +/// @todo Throw error for scoped inside scoped variables when they get referenced outside. +/// @todo Add class/struct/enum support. + +/////////////////////// + +// ANSI ESCAPE CODES // + +/////////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" + +///////////////////////////////////// + +// INTERNALS OF THE C++ COMPILER + +///////////////////////////////////// + +/// @internal +namespace Details +{ + std::filesystem::path expand_home(const std::filesystem::path& p) + { + if (!p.empty() && p.string()[0] == '~') + { + const char* home = std::getenv("HOME"); // For Unix-like systems + if (!home) + { + home = std::getenv("USERPROFILE"); // For Windows + } + if (home) + { + return std::filesystem::path(home) / p.relative_path().string().substr(1); + } + else + { + throw std::runtime_error("Home directory not found in environment variables"); + } + } + return p; + } + + struct CompilerRegisterMap final + { + std::string fName; + std::string fReg; + }; + + // \brief Offset based struct/class + struct CompilerStructMap final + { + std::string fName; + std::string fReg; + + // offset counter + std::size_t fOffsetsCnt; + + // offset array + std::vector<std::pair<Int32, std::string>> fOffsets; + }; + + struct CompilerState final + { + std::vector<CompilerRegisterMap> fStackMapVector; + std::vector<CompilerStructMap> fStructMapVector; + LibCompiler::SyntaxLeafList* fSyntaxTree{nullptr}; + std::unique_ptr<std::ofstream> fOutputAssembly; + std::string fLastFile; + std::string fLastError; + bool fVerbose; + }; +} // namespace Details + +static Details::CompilerState kState; +static SizeType kErrorLimit = 100; + +static Int32 kAcceptableErrors = 0; + +namespace Details +{ + /// @brief prints an error into stdout. + /// @param reason the reason of the error. + /// @param file where does it originate from? + void print_error(std::string reason, std::string file) noexcept; + + struct CompilerType final + { + std::string fName; + std::string fValue; + }; +} // namespace Details + +///////////////////////////////////////////////////////////////////////////////////////// + +// Target architecture. +static int kMachine = LibCompiler::AssemblyFactory::kArchAMD64; + +///////////////////////////////////////// + +// ARGUMENTS REGISTERS (R8, R15) + +///////////////////////////////////////// + +static size_t kRegisterCnt = kAsmRegisterLimit; +static size_t kStartUsable = 8; +static size_t kUsableLimit = 15; +static size_t kRegisterCounter = kStartUsable; +static std::vector<LibCompiler::CompilerKeyword> kKeywords; + +///////////////////////////////////////// + +// COMPILER PARSING UTILITIES/STATES. + +///////////////////////////////////////// + +static std::vector<std::string> kFileList; +static LibCompiler::AssemblyFactory kFactory; +static bool kInStruct = false; +static bool kOnWhileLoop = false; +static bool kOnForLoop = false; +static bool kInBraces = false; +static size_t kBracesCount = 0UL; + +/* @brief C++ compiler backend for the ZKA C++ driver */ +class CompilerFrontendCPlusPlus final : public LibCompiler::ICompilerFrontend +{ +public: + explicit CompilerFrontendCPlusPlus() = default; + ~CompilerFrontendCPlusPlus() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(CompilerFrontendCPlusPlus); + + bool Compile(const std::string text, const std::string file) override; + + const char* Language() override; +}; + +/// @internal compiler variables + +static CompilerFrontendCPlusPlus* kCompilerFrontend = nullptr; + +static std::vector<std::string> kRegisterMap; + +static std::vector<std::string> kRegisterList = { + "rbx", + "rsi", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", + "xmm12", + "xmm13", + "xmm14", + "xmm15", +}; + +/// @brief The PEF calling convention (caller must save rax, rbp) +/// @note callee must return via **rax**. +static std::vector<std::string> kRegisterConventionCallList = { + "r8", + "r9", + "r10", + "r11", + "r12", + "r13", + "r14", + "r15", +}; + +static std::size_t kFunctionEmbedLevel = 0UL; + +/// detail namespaces + +const char* CompilerFrontendCPlusPlus::Language() +{ + return "ZKA C++"; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @name Compile +/// @brief Generate assembly from a C++ source. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerFrontendCPlusPlus::Compile(const std::string text, + const std::string file) +{ + if (text.empty()) + return false; + + std::size_t index = 0UL; + std::vector<std::pair<LibCompiler::CompilerKeyword, std::size_t>> keywords_list; + + bool found = false; + static bool commentBlock = false; + + for (auto& keyword : kKeywords) + { + if (text.find(keyword.keyword_name) != std::string::npos) + { + switch (keyword.keyword_kind) + { + case LibCompiler::eKeywordKindCommentMultiLineStart: { + commentBlock = true; + return true; + } + case LibCompiler::eKeywordKindCommentMultiLineEnd: { + commentBlock = false; + break; + } + case LibCompiler::eKeywordKindCommentInline: { + break; + } + default: + break; + } + + if (text[text.find(keyword.keyword_name) - 1] == '+' && + keyword.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + continue; + + if (text[text.find(keyword.keyword_name) - 1] == '-' && + keyword.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + continue; + + if (text[text.find(keyword.keyword_name) + 1] == '=' && + keyword.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + continue; + + keywords_list.emplace_back(std::make_pair(keyword, index)); + ++index; + + found = true; + } + } + + if (!found && !commentBlock) + { + for (size_t i = 0; i < text.size(); i++) + { + if (isalnum(text[i])) + { + Details::print_error("syntax error: " + text, file); + return false; + } + } + } + + for (auto& keyword : keywords_list) + { + auto syntax_tree = LibCompiler::SyntaxLeafList::SyntaxLeaf(); + + switch (keyword.first.keyword_kind) + { + case LibCompiler::KeywordKind::eKeywordKindIf: { + auto expr = text.substr(text.find(keyword.first.keyword_name) + keyword.first.keyword_name.size() + 1, text.find(")") - 1); + + if (expr.find(">=") != std::string::npos) + { + auto left = text.substr(text.find(keyword.first.keyword_name) + keyword.first.keyword_name.size() + 2, expr.find("<=") + strlen("<=")); + auto right = text.substr(expr.find(">=") + strlen(">="), text.find(")") - 1); + + size_t i = right.size() - 1; + + try + { + while (!std::isalnum(right[i])) + { + right.erase(i, 1); + --i; + } + + right.erase(0, i); + } + catch (...) + { + right.erase(0, i); + } + + i = left.size() - 1; + try + { + while (!std::isalnum(left[i])) + { + left.erase(i, 1); + --i; + } + + left.erase(0, i); + } + catch (...) + { + left.erase(0, i); + } + + if (!isdigit(left[0]) || + !isdigit(right[0])) + { + auto indexRight = 0UL; + + auto& valueOfVar = !isdigit(left[0]) ? left : right; + + for (auto pairRight : kRegisterMap) + { + ++indexRight; + + if (pairRight != valueOfVar) + { + + auto& valueOfVarOpposite = isdigit(left[0]) ? left : right; + + syntax_tree.fUserValue += "mov " + kRegisterList[indexRight + 1] + ", " + valueOfVarOpposite + "\n"; + syntax_tree.fUserValue += "cmp " + kRegisterList[kRegisterMap.size() - 1] + "," + kRegisterList[indexRight + 1] + "\n"; + + goto done_iterarting_on_if; + } + + auto& valueOfVarOpposite = isdigit(left[0]) ? left : right; + + syntax_tree.fUserValue += "mov " + kRegisterList[indexRight + 1] + ", " + valueOfVarOpposite + "\n"; + syntax_tree.fUserValue += "cmp " + kRegisterList[kRegisterMap.size() - 1] + ", " + kRegisterList[indexRight + 1] + "\n"; + + break; + } + } + + done_iterarting_on_if: + + std::string fnName = text; + fnName.erase(fnName.find(keyword.first.keyword_name)); + + for (auto& ch : fnName) + { + if (ch == ' ') + ch = '_'; + } + + syntax_tree.fUserValue += "jge __OFFSET_ON_TRUE_NDK\nsegment .code64 __OFFSET_ON_TRUE_NDK:\n"; + } + + break; + } + case LibCompiler::KeywordKind::eKeywordKindFunctionStart: { + for (auto& ch : text) + { + if (isdigit(ch)) + { + goto dont_accept; + } + } + + goto accept; + + dont_accept: + return false; + + accept: + std::string fnName = text; + size_t indexFnName = 0; + + // this one is for the type. + for (auto& ch : text) + { + ++indexFnName; + + if (ch == '\t') + break; + + if (ch == ' ') + break; + } + + fnName = text.substr(indexFnName); + + if (text.ends_with(";")) + goto tk_write_assembly; + else if (text.size() <= indexFnName) + Details::print_error("Invalid function name: " + fnName, file); + + indexFnName = 0; + + for (auto& ch : fnName) + { + if (ch == ' ' || + ch == '\t') + { + if (fnName[indexFnName - 1] != ')') + Details::print_error("Invalid function name: " + fnName, file); + + if ((indexFnName + 1) != fnName.size()) + Details::print_error("Extra characters after function name: " + fnName, file); + } + + ++indexFnName; + } + + syntax_tree.fUserValue = "public_segment .code64 __TOOLCHAINKIT_" + fnName + "\n"; + ++kFunctionEmbedLevel; + + break; + +tk_write_assembly: + syntax_tree.fUserValue = "jmp __TOOLCHAINKIT_" + fnName + "\n"; + } + case LibCompiler::KeywordKind::eKeywordKindFunctionEnd: { + if (text.ends_with(";")) + break; + + --kFunctionEmbedLevel; + + if (kRegisterMap.size() > kRegisterList.size()) + { + --kFunctionEmbedLevel; + } + + if (kFunctionEmbedLevel < 1) + kRegisterMap.clear(); + break; + } + case LibCompiler::KeywordKind::eKeywordKindEndInstr: + case LibCompiler::KeywordKind::eKeywordKindVariableInc: + case LibCompiler::KeywordKind::eKeywordKindVariableDec: + case LibCompiler::KeywordKind::eKeywordKindVariableAssign: { + std::string valueOfVar = ""; + + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableInc) + { + valueOfVar = text.substr(text.find("+=") + 2); + } + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableDec) + { + valueOfVar = text.substr(text.find("-=") + 2); + } + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + { + valueOfVar = text.substr(text.find("=") + 1); + } + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindEndInstr) + { + break; + } + + while (valueOfVar.find(";") != std::string::npos && + keyword.first.keyword_kind != LibCompiler::KeywordKind::eKeywordKindEndInstr) + { + valueOfVar.erase(valueOfVar.find(";")); + } + + std::string varName = text; + + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableInc) + { + varName.erase(varName.find("+=")); + } + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableDec) + { + varName.erase(varName.find("-=")); + } + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableAssign) + { + varName.erase(varName.find("=")); + } + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindEndInstr) + { + varName.erase(varName.find(";")); + } + + static bool typeFound = false; + + for (auto& keyword : kKeywords) + { + if (keyword.keyword_kind == LibCompiler::eKeywordKindType) + { + if (text.find(keyword.keyword_name) != std::string::npos) + { + if (text[text.find(keyword.keyword_name)] == ' ') + { + typeFound = false; + continue; + } + + typeFound = true; + } + } + } + + std::string instr = "mov "; + + if (typeFound && keyword.first.keyword_kind != LibCompiler::KeywordKind::eKeywordKindVariableInc && + keyword.first.keyword_kind != LibCompiler::KeywordKind::eKeywordKindVariableDec) + { + if (kRegisterMap.size() > kRegisterList.size()) + { + ++kFunctionEmbedLevel; + } + + while (varName.find(" ") != std::string::npos) + { + varName.erase(varName.find(" "), 1); + } + + while (varName.find("\t") != std::string::npos) + { + varName.erase(varName.find("\t"), 1); + } + + for (size_t i = 0; !isalnum(valueOfVar[i]); i++) + { + if (i > valueOfVar.size()) + break; + + valueOfVar.erase(i, 1); + } + + constexpr auto cTrueVal = "true"; + constexpr auto cFalseVal = "false"; + + if (valueOfVar == cTrueVal) + { + valueOfVar = "1"; + } + else if (valueOfVar == cFalseVal) + { + valueOfVar = "0"; + } + + std::size_t indexRight = 0UL; + + for (auto pairRight : kRegisterMap) + { + ++indexRight; + + if (pairRight != valueOfVar) + { + if (valueOfVar[0] == '\"') + { + + syntax_tree.fUserValue = "segment .data64 __TOOLCHAINKIT_LOCAL_VAR_" + varName + ": db " + valueOfVar + ", 0\n\n"; + syntax_tree.fUserValue += instr + kRegisterList[kRegisterMap.size() - 1] + ", " + "__TOOLCHAINKIT_LOCAL_VAR_" + varName + "\n"; + } + else + { + syntax_tree.fUserValue = instr + kRegisterList[kRegisterMap.size() - 1] + ", " + valueOfVar + "\n"; + } + + goto done; + } + } + + if (((int)indexRight - 1) < 0) + { + if (valueOfVar[0] == '\"') + { + + syntax_tree.fUserValue = "segment .data64 __TOOLCHAINKIT_LOCAL_VAR_" + varName + ": db " + valueOfVar + ", 0\n"; + syntax_tree.fUserValue += instr + kRegisterList[kRegisterMap.size()] + ", " + "__TOOLCHAINKIT_LOCAL_VAR_" + varName + "\n"; + } + else + { + syntax_tree.fUserValue = instr + kRegisterList[kRegisterMap.size()] + ", " + valueOfVar + "\n"; + } + + goto done; + } + + if (valueOfVar[0] != '\"' && + valueOfVar[0] != '\'' && + !isdigit(valueOfVar[0])) + { + for (auto pair : kRegisterMap) + { + if (pair == valueOfVar) + goto done; + } + + Details::print_error("Variable not declared: " + varName, file); + return false; + } + + done: + for (auto& keyword : kKeywords) + { + if (keyword.keyword_kind == LibCompiler::eKeywordKindType && + varName.find(keyword.keyword_name) != std::string::npos) + { + varName.erase(varName.find(keyword.keyword_name), keyword.keyword_name.size()); + break; + } + } + + kRegisterMap.push_back(varName); + + break; + } + + if (kKeywords[keyword.second - 1].keyword_kind == LibCompiler::eKeywordKindType || + kKeywords[keyword.second - 1].keyword_kind == LibCompiler::eKeywordKindTypePtr) + { + syntax_tree.fUserValue = "\n"; + continue; + } + + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindEndInstr) + { + syntax_tree.fUserValue = "\n"; + continue; + } + + if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableInc) + { + instr = "add "; + } + else if (keyword.first.keyword_kind == LibCompiler::KeywordKind::eKeywordKindVariableDec) + { + instr = "sub "; + } + + std::string varErrCpy = varName; + + while (varName.find(" ") != std::string::npos) + { + varName.erase(varName.find(" "), 1); + } + + while (varName.find("\t") != std::string::npos) + { + varName.erase(varName.find("\t"), 1); + } + + std::size_t indxReg = 0UL; + + for (size_t i = 0; !isalnum(valueOfVar[i]); i++) + { + if (i > valueOfVar.size()) + break; + + valueOfVar.erase(i, 1); + } + + while (valueOfVar.find(" ") != std::string::npos) + { + valueOfVar.erase(valueOfVar.find(" "), 1); + } + + while (valueOfVar.find("\t") != std::string::npos) + { + valueOfVar.erase(valueOfVar.find("\t"), 1); + } + + constexpr auto cTrueVal = "true"; + constexpr auto cFalseVal = "false"; + + /// interpet boolean values, since we're on C++ + + if (valueOfVar == cTrueVal) + { + valueOfVar = "1"; + } + else if (valueOfVar == cFalseVal) + { + valueOfVar = "0"; + } + + for (auto pair : kRegisterMap) + { + ++indxReg; + + if (pair != varName) + continue; + + std::size_t indexRight = 0ul; + + for (auto pairRight : kRegisterMap) + { + ++indexRight; + + if (pairRight != varName) + { + syntax_tree.fUserValue = instr + kRegisterList[kRegisterMap.size()] + ", " + valueOfVar + "\n"; + continue; + } + + syntax_tree.fUserValue = instr + kRegisterList[indexRight - 1] + ", " + valueOfVar + "\n"; + break; + } + + break; + } + + if (syntax_tree.fUserValue.empty()) + { + Details::print_error("Variable not declared: " + varErrCpy, file); + } + + break; + } + case LibCompiler::KeywordKind::eKeywordKindReturn: { + try + { + auto pos = text.find("return") + strlen("return") + 1; + std::string subText = text.substr(pos); + subText = subText.erase(subText.find(";")); + size_t indxReg = 0UL; + + if (subText[0] != '\"' && + subText[0] != '\'') + { + if (!isdigit(subText[0])) + { + for (auto pair : kRegisterMap) + { + ++indxReg; + + if (pair != subText) + continue; + + syntax_tree.fUserValue = "mov rax," + kRegisterList[indxReg - 1] + "\r\nret\n"; + break; + } + + if (syntax_tree.fUserValue.empty()) + { + Details::print_error("Variable not declared: " + subText, file); + } + } + else + { + syntax_tree.fUserValue = "mov rax, " + subText + "\r\nret\n"; + } + } + else + { + syntax_tree.fUserValue = "__TOOLCHAINKIT_LOCAL_RETURN_STRING: db " + subText + ", 0\nmov rcx, __TOOLCHAINKIT_LOCAL_RETURN_STRING\n"; + syntax_tree.fUserValue += "mov rax, rcx\r\nret\n"; + } + + break; + } + catch (...) + { + syntax_tree.fUserValue = "ret\n"; + } + } + default: + { + break; + } + } + + syntax_tree.fUserData = keyword.first; + kState.fSyntaxTree->fLeafList.push_back(syntax_tree); + } + +ndk_compile_ok: + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/** + * @brief C++ assembler class. + */ + +///////////////////////////////////////////////////////////////////////////////////////// + +class AssemblyCPlusPlusInterface final ASSEMBLY_INTERFACE +{ +public: + explicit AssemblyCPlusPlusInterface() = default; + ~AssemblyCPlusPlusInterface() override = default; + + TOOLCHAINKIT_COPY_DEFAULT(AssemblyCPlusPlusInterface); + + [[maybe_unused]] static Int32 Arch() noexcept + { + return LibCompiler::AssemblyFactory::kArchAMD64; + } + + Int32 CompileToFormat(std::string& src, Int32 arch) override + { + if (arch != AssemblyCPlusPlusInterface::Arch()) + return 1; + + if (kCompilerFrontend == nullptr) + return 1; + + /* @brief copy contents wihtout extension */ + std::string src_file = src; + std::ifstream src_fp = std::ifstream(src_file, std::ios::in); + + const char* cExts[] = kAsmFileExts; + + std::string dest = src_file; + dest += cExts[2]; + + if (dest.empty()) + { + dest = "CXX-LibCompiler-"; + + 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); + + auto gen = uuids::uuid_random_generator(generator); + + auto id = gen(); + dest += uuids::to_string(id); + } + + kState.fOutputAssembly = std::make_unique<std::ofstream>(dest); + + auto fmt = LibCompiler::current_date(); + + (*kState.fOutputAssembly) << "; Repository Path: /" << src_file << "\n"; + + std::filesystem::path path = std::filesystem::path("./"); + + while (path != Details::expand_home(std::filesystem::path("~"))) + { + for (auto const& dir_entry : std::filesystem::recursive_directory_iterator{path}) + { + if (dir_entry.is_directory() && + dir_entry.path().string().find(".git") != std::string::npos) + goto break_loop; + } + + path = path.parent_path(); + break_loop: + (*kState.fOutputAssembly) << "; Repository Style: Git\n"; + break; + } + + (*kState.fOutputAssembly) + << "; Assembler Dialect: AMD64 LibCompiler Assembler. (Generated from C++)\n"; + (*kState.fOutputAssembly) << "; Date: " << fmt << "\n"; + (*kState.fOutputAssembly) << "#bits 64\n#org 0x1000000" + << "\n"; + + kState.fSyntaxTree = new LibCompiler::SyntaxLeafList(); + + // =================================== + // Parse source file. + // =================================== + + std::string line_source; + + while (std::getline(src_fp, line_source)) + { + kCompilerFrontend->Compile(line_source, src); + } + + for (auto& ast_generated : kState.fSyntaxTree->fLeafList) + { + (*kState.fOutputAssembly) << ast_generated.fUserValue; + } + + kState.fOutputAssembly->flush(); + kState.fOutputAssembly->close(); + + delete kState.fSyntaxTree; + kState.fSyntaxTree = nullptr; + + if (kAcceptableErrors > 0) + return 1; + + return kExitOK; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +static void cxx_print_help() +{ + kSplashCxx(); + kPrintF("%s", "No help available, see:\n"); + kPrintF("%s", "www.zws.zka.com/help/c++lang\n"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define kExtListCxx \ + { \ + ".cpp", ".cxx", ".cc", ".c++", ".cp" \ + } + +TOOLCHAINKIT_MODULE(CompilerCPlusPlusX8664) +{ + bool skip = false; + + kKeywords.push_back({.keyword_name = "if", .keyword_kind = LibCompiler::eKeywordKindIf}); + kKeywords.push_back({.keyword_name = "else", .keyword_kind = LibCompiler::eKeywordKindElse}); + kKeywords.push_back({.keyword_name = "else if", .keyword_kind = LibCompiler::eKeywordKindElseIf}); + + kKeywords.push_back({.keyword_name = "class", .keyword_kind = LibCompiler::eKeywordKindClass}); + kKeywords.push_back({.keyword_name = "struct", .keyword_kind = LibCompiler::eKeywordKindClass}); + kKeywords.push_back({.keyword_name = "namespace", .keyword_kind = LibCompiler::eKeywordKindNamespace}); + kKeywords.push_back({.keyword_name = "typedef", .keyword_kind = LibCompiler::eKeywordKindTypedef}); + kKeywords.push_back({.keyword_name = "using", .keyword_kind = LibCompiler::eKeywordKindTypedef}); + kKeywords.push_back({.keyword_name = "{", .keyword_kind = LibCompiler::eKeywordKindBodyStart}); + kKeywords.push_back({.keyword_name = "}", .keyword_kind = LibCompiler::eKeywordKindBodyEnd}); + kKeywords.push_back({.keyword_name = "auto", .keyword_kind = LibCompiler::eKeywordKindVariable}); + kKeywords.push_back({.keyword_name = "int", .keyword_kind = LibCompiler::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "bool", .keyword_kind = LibCompiler::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "unsigned", .keyword_kind = LibCompiler::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "short", .keyword_kind = LibCompiler::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "char", .keyword_kind = LibCompiler::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "long", .keyword_kind = LibCompiler::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "float", .keyword_kind = LibCompiler::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "double", .keyword_kind = LibCompiler::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "void", .keyword_kind = LibCompiler::eKeywordKindType}); + + kKeywords.push_back({.keyword_name = "auto*", .keyword_kind = LibCompiler::eKeywordKindVariablePtr}); + kKeywords.push_back({.keyword_name = "int*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "bool*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "unsigned*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "short*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "char*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "long*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "float*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "double*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "void*", .keyword_kind = LibCompiler::eKeywordKindTypePtr}); + + kKeywords.push_back({.keyword_name = "(", .keyword_kind = LibCompiler::eKeywordKindFunctionStart}); + kKeywords.push_back({.keyword_name = ")", .keyword_kind = LibCompiler::eKeywordKindFunctionEnd}); + kKeywords.push_back({.keyword_name = "=", .keyword_kind = LibCompiler::eKeywordKindVariableAssign}); + kKeywords.push_back({.keyword_name = "+=", .keyword_kind = LibCompiler::eKeywordKindVariableInc}); + kKeywords.push_back({.keyword_name = "-=", .keyword_kind = LibCompiler::eKeywordKindVariableDec}); + kKeywords.push_back({.keyword_name = "const", .keyword_kind = LibCompiler::eKeywordKindConstant}); + kKeywords.push_back({.keyword_name = "*", .keyword_kind = LibCompiler::eKeywordKindPtr}); + kKeywords.push_back({.keyword_name = "->", .keyword_kind = LibCompiler::eKeywordKindPtrAccess}); + kKeywords.push_back({.keyword_name = ".", .keyword_kind = LibCompiler::eKeywordKindAccess}); + kKeywords.push_back({.keyword_name = ",", .keyword_kind = LibCompiler::eKeywordKindArgSeparator}); + kKeywords.push_back({.keyword_name = ";", .keyword_kind = LibCompiler::eKeywordKindEndInstr}); + kKeywords.push_back({.keyword_name = ":", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "public:", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "private:", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "protected:", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "final", .keyword_kind = LibCompiler::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "return", .keyword_kind = LibCompiler::eKeywordKindReturn}); + kKeywords.push_back({.keyword_name = "--*", .keyword_kind = LibCompiler::eKeywordKindCommentMultiLineStart}); + kKeywords.push_back({.keyword_name = "*/", .keyword_kind = LibCompiler::eKeywordKindCommentMultiLineStart}); + kKeywords.push_back({.keyword_name = "--/", .keyword_kind = LibCompiler::eKeywordKindCommentInline}); + kKeywords.push_back({.keyword_name = "==", .keyword_kind = LibCompiler::eKeywordKindEq}); + kKeywords.push_back({.keyword_name = "!=", .keyword_kind = LibCompiler::eKeywordKindNotEq}); + kKeywords.push_back({.keyword_name = ">=", .keyword_kind = LibCompiler::eKeywordKindGreaterEq}); + kKeywords.push_back({.keyword_name = "<=", .keyword_kind = LibCompiler::eKeywordKindLessEq}); + + kFactory.Mount(new AssemblyCPlusPlusInterface()); + kCompilerFrontend = new CompilerFrontendCPlusPlus(); + + for (auto index = 1UL; index < argc; ++index) + { + if (argv[index][0] == '-') + { + if (skip) + { + skip = false; + continue; + } + + if (strcmp(argv[index], "--cl:version") == 0) + { + kSplashCxx(); + return kExitOK; + } + + if (strcmp(argv[index], "--cl:verbose") == 0) + { + kState.fVerbose = true; + + continue; + } + + if (strcmp(argv[index], "--cl:h") == 0) + { + cxx_print_help(); + + return kExitOK; + } + + if (strcmp(argv[index], "--cl:c++-dialect") == 0) + { + if (kCompilerFrontend) + std::cout << kCompilerFrontend->Language() << "\n"; + + return kExitOK; + } + + if (strcmp(argv[index], "--cl:max-err") == 0) + { + try + { + kErrorLimit = std::strtol(argv[index + 1], nullptr, 10); + } + // catch anything here + catch (...) + { + kErrorLimit = 0; + } + + skip = true; + + continue; + } + + std::string err = "Unknown option: "; + err += argv[index]; + + Details::print_error(err, "c++-drv"); + + continue; + } + + kFileList.emplace_back(argv[index]); + + std::string argv_i = argv[index]; + + std::vector exts = kExtListCxx; + bool found = false; + + for (std::string ext : exts) + { + if (argv_i.find(ext) != std::string::npos) + { + found = true; + break; + } + } + + if (!found) + { + if (kState.fVerbose) + { + Details::print_error(argv_i + " is not a valid C++ source.\n", "c++-drv"); + } + + return 1; + } + + std::cout << "CPlusPlusCompilerAMD64: Building: " << argv[index] << std::endl; + + if (kFactory.Compile(argv_i, kMachine) != kExitOK) + return 1; + } + + return kExitOK; +} + +// Last rev 8-1-24 +// diff --git a/dev/LibCompiler/src/CPlusPlusCompilerPreProcessor.cc b/dev/LibCompiler/src/CPlusPlusCompilerPreProcessor.cc new file mode 100644 index 0000000..819cfc7 --- /dev/null +++ b/dev/LibCompiler/src/CPlusPlusCompilerPreProcessor.cc @@ -0,0 +1,1070 @@ +/* + * ======================================================== + * + * bpp + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +/// BUGS: 0 + +#include <LibCompiler/Parser.h> +#include <LibCompiler/NFC/ErrorID.h> +#include <algorithm> +#include <filesystem> +#include <fstream> +#include <iostream> +#include <stdexcept> +#include <vector> + +#define kMacroPrefix '#' + +/// @author EL Mahrouss Amlal (amlel) +/// @file bpp.cxx +/// @brief Preprocessor. + +typedef Int32 (*bpp_parser_fn_t)(std::string& line, std::ifstream& hdr_file, std::ofstream& pp_out); + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Preprocessor internal types. + +///////////////////////////////////////////////////////////////////////////////////////// + +namespace Details +{ + enum + { + kEqual, + kGreaterEqThan, + kLesserEqThan, + kGreaterThan, + kLesserThan, + kNotEqual, + }; + + struct bpp_macro_condition final + { + int32_t fType; + std::string fTypeName; + }; + + struct bpp_macro final + { + std::vector<std::string> fArgs; + std::string fName; + std::string fValue; + + void Print() + { + std::cout << "name: " << fName << "\n"; + std::cout << "value: " << fValue << "\n"; + + for (auto& arg : fArgs) + { + std::cout << "arg: " << arg << "\n"; + } + } + }; + + class bpp_pragma final + { + public: + explicit bpp_pragma() = default; + ~bpp_pragma() = default; + + TOOLCHAINKIT_COPY_DEFAULT(bpp_pragma); + + std::string fMacroName; + bpp_parser_fn_t fParse; + }; +} // namespace Details + +static std::vector<std::string> kFiles; +static std::vector<Details::bpp_macro> kMacros; +static std::vector<std::string> kIncludes; + +static std::string kWorkingDir; + +static std::vector<std::string> kKeywords = { + "include", "if", "pragma", "def", "elif", + "ifdef", "ifndef", "else", "warning", "error"}; + +#define kKeywordCxxCnt kKeywords.size() + +///////////////////////////////////////////////////////////////////////////////////////// + +// @name bpp_parse_if_condition +// @brief parse #if condition + +///////////////////////////////////////////////////////////////////////////////////////// + +int32_t bpp_parse_if_condition(Details::bpp_macro_condition& cond, + Details::bpp_macro& macro, + bool& inactive_code, + bool& defined, + std::string& macro_str) +{ + if (cond.fType == Details::kEqual) + { + auto substr_macro = + macro_str.substr(macro_str.find(macro.fName) + macro.fName.size()); + + if (substr_macro.find(macro.fValue) != std::string::npos) + { + if (macro.fValue == "0") + { + defined = false; + inactive_code = true; + + return 1; + } + + defined = true; + inactive_code = false; + + return 1; + } + } + else if (cond.fType == Details::kNotEqual) + { + auto substr_macro = + macro_str.substr(macro_str.find(macro.fName) + macro.fName.size()); + + if (substr_macro.find(macro.fName) != std::string::npos) + { + if (substr_macro.find(macro.fValue) != std::string::npos) + { + defined = false; + inactive_code = true; + + return 1; + } + + defined = true; + inactive_code = false; + + return 1; + } + + return 0; + } + + auto substr_macro = + macro_str.substr(macro_str.find(macro.fName) + macro.fName.size()); + + std::string number; + + for (auto& macro_num : kMacros) + { + if (substr_macro.find(macro_num.fName) != std::string::npos) + { + for (size_t i = 0; i < macro_num.fName.size(); ++i) + { + if (isdigit(macro_num.fValue[i])) + { + number += macro_num.fValue[i]; + } + else + { + number.clear(); + break; + } + } + + break; + } + } + + size_t y = 2; + + /* last try */ + for (; y < macro_str.size(); y++) + { + if (isdigit(macro_str[y])) + { + for (size_t x = y; x < macro_str.size(); x++) + { + if (macro_str[x] == ' ') + break; + + number += macro_str[x]; + } + + break; + } + } + + size_t rhs = atol(macro.fValue.c_str()); + size_t lhs = atol(number.c_str()); + + if (lhs == 0) + { + number.clear(); + ++y; + + for (; y < macro_str.size(); y++) + { + if (isdigit(macro_str[y])) + { + for (size_t x = y; x < macro_str.size(); x++) + { + if (macro_str[x] == ' ') + break; + + number += macro_str[x]; + } + + break; + } + } + + lhs = atol(number.c_str()); + } + + if (cond.fType == Details::kGreaterThan) + { + if (lhs < rhs) + { + defined = true; + inactive_code = false; + + return 1; + } + + return 0; + } + + if (cond.fType == Details::kGreaterEqThan) + { + if (lhs <= rhs) + { + defined = true; + inactive_code = false; + + return 1; + } + + return 0; + } + + if (cond.fType == Details::kLesserEqThan) + { + if (lhs >= rhs) + { + defined = true; + inactive_code = false; + + return 1; + } + + return 0; + } + + if (cond.fType == Details::kLesserThan) + { + if (lhs > rhs) + { + defined = true; + inactive_code = false; + + return 1; + } + + return 0; + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief stores every included file here. + +///////////////////////////////////////////////////////////////////////////////////////// + +std::vector<std::string> kAllIncludes; + +///////////////////////////////////////////////////////////////////////////////////////// + +// @name bpp_parse_file +// @brief parse file to preprocess it. + +///////////////////////////////////////////////////////////////////////////////////////// + +void bpp_parse_file(std::ifstream& hdr_file, std::ofstream& pp_out) +{ + std::string hdr_line; + std::string line_after_include; + + bool inactive_code = false; + bool defined = false; + + try + { + while (std::getline(hdr_file, hdr_line)) + { + if (inactive_code) + { + if (hdr_line.find("#endif") == std::string::npos) + { + continue; + } + else if (hdr_line[0] == kMacroPrefix && + hdr_line.find("#endif") != std::string::npos) + { + + inactive_code = false; + } + + if (hdr_line.find("*/") != std::string::npos) + { + continue; + } + } + + if (hdr_line.find("--/") != std::string::npos) + { + hdr_line.erase(hdr_line.find("--/")); + } + + if (hdr_line.find("--*") != std::string::npos) + { + inactive_code = true; + // get rid of comment. + hdr_line.erase(hdr_line.find("--*")); + } + + /// BPP 'brief' documentation. + if (hdr_line.find("@brief") != std::string::npos) + { + hdr_line.erase(hdr_line.find("@brief")); + + // TODO: Write an <file_name>.html or append to it. + } + + if (hdr_line[0] == kMacroPrefix && + hdr_line.find("endif") != std::string::npos) + { + if (!defined && inactive_code) + { + inactive_code = false; + defined = false; + + continue; + } + + continue; + } + + if (!defined && inactive_code) + { + continue; + } + + if (defined && inactive_code) + { + continue; + } + + for (auto macro : kMacros) + { + if (LibCompiler::find_word(hdr_line, macro.fName)) + { + if (hdr_line.substr(hdr_line.find(macro.fName)).find(macro.fName + '(') != LibCompiler::String::npos) + { + if (!macro.fArgs.empty()) + { + LibCompiler::String symbol_val = macro.fValue; + std::vector<LibCompiler::String> args; + + size_t x_arg_indx = 0; + + LibCompiler::String line_after_define = hdr_line; + LibCompiler::String str_arg; + + if (line_after_define.find("(") != LibCompiler::String::npos) + { + line_after_define.erase(0, line_after_define.find("(") + 1); + + for (auto& subc : line_after_define) + { + if (subc == ' ' || subc == '\t') + continue; + + if (subc == ',' || subc == ')') + { + if (str_arg.empty()) + continue; + + args.push_back(str_arg); + + str_arg.clear(); + + continue; + } + + str_arg.push_back(subc); + } + } + + for (auto arg : macro.fArgs) + { + if (symbol_val.find(macro.fArgs[x_arg_indx]) != LibCompiler::String::npos) + { + symbol_val.replace(symbol_val.find(macro.fArgs[x_arg_indx]), macro.fArgs[x_arg_indx].size(), + args[x_arg_indx]); + ++x_arg_indx; + } + else + { + throw std::runtime_error("internal: Internal C++ error. (Please report that bug.)"); + } + } + + auto len = macro.fName.size(); + len += symbol_val.size(); + len += 2; // ( and ) + + hdr_line.erase(hdr_line.find(")"), 1); + + hdr_line.replace(hdr_line.find(hdr_line.substr(hdr_line.find(macro.fName + '('))), len, + symbol_val); + } + else + { + auto value = macro.fValue; + + hdr_line.replace(hdr_line.find(macro.fName), macro.fName.size(), + value); + } + } + } + } + + if (hdr_line[0] == kMacroPrefix && + hdr_line.find("define ") != std::string::npos) + { + auto line_after_define = + hdr_line.substr(hdr_line.find("define ") + strlen("define ")); + + std::string macro_value; + std::string macro_key; + + std::size_t pos = 0UL; + + std::vector<std::string> args; + bool on_args = false; + + for (auto& ch : line_after_define) + { + ++pos; + + if (ch == '(') + { + on_args = true; + continue; + } + + if (ch == ')') + { + on_args = false; + continue; + } + + if (ch == '\\') + continue; + + if (on_args) + continue; + + if (ch == ' ') + { + for (size_t i = pos; i < line_after_define.size(); i++) + { + macro_value += line_after_define[i]; + } + + break; + } + + macro_key += ch; + } + + std::string str; + + if (line_after_define.find("(") != LibCompiler::String::npos) + { + line_after_define.erase(0, line_after_define.find("(") + 1); + + for (auto& subc : line_after_define) + { + if (subc == ',' || subc == ')') + { + if (str.empty()) + continue; + + args.push_back(str); + + str.clear(); + + continue; + } + + str.push_back(subc); + } + } + + Details::bpp_macro macro; + + macro.fArgs = args; + macro.fName = macro_key; + macro.fValue = macro_value; + + kMacros.emplace_back(macro); + + continue; + } + + if (hdr_line[0] != kMacroPrefix) + { + if (inactive_code) + { + continue; + } + + pp_out << hdr_line << std::endl; + + continue; + } + + if (hdr_line[0] == kMacroPrefix && + hdr_line.find("ifndef") != std::string::npos) + { + auto line_after_ifndef = + hdr_line.substr(hdr_line.find("ifndef") + strlen("ifndef") + 1); + std::string macro; + + for (auto& ch : line_after_ifndef) + { + if (ch == ' ') + { + break; + } + + macro += ch; + } + + if (macro == "0") + { + defined = true; + inactive_code = false; + continue; + } + + if (macro == "1") + { + defined = false; + inactive_code = true; + + continue; + } + + bool found = false; + + defined = true; + inactive_code = false; + + for (auto& macro_ref : kMacros) + { + if (hdr_line.find(macro_ref.fName) != std::string::npos) + { + found = true; + break; + } + } + + if (found) + { + defined = false; + inactive_code = true; + + continue; + } + } + else if (hdr_line[0] == kMacroPrefix && + hdr_line.find("else") != std::string::npos) + { + if (!defined && inactive_code) + { + inactive_code = false; + defined = true; + + continue; + } + else + { + defined = false; + inactive_code = true; + + continue; + } + } + else if (hdr_line[0] == kMacroPrefix && + hdr_line.find("ifdef") != std::string::npos) + { + auto line_after_ifdef = + hdr_line.substr(hdr_line.find("ifdef") + strlen("ifdef") + 1); + std::string macro; + + for (auto& ch : line_after_ifdef) + { + if (ch == ' ') + { + break; + } + + macro += ch; + } + + if (macro == "0") + { + defined = false; + inactive_code = true; + + continue; + } + + if (macro == "1") + { + defined = true; + inactive_code = false; + + continue; + } + + defined = false; + inactive_code = true; + + for (auto& macro_ref : kMacros) + { + if (hdr_line.find(macro_ref.fName) != std::string::npos) + { + defined = true; + inactive_code = false; + + break; + } + } + } + else if (hdr_line[0] == kMacroPrefix && + hdr_line.find("if") != std::string::npos) + { + inactive_code = true; + + std::vector<Details::bpp_macro_condition> bpp_macro_condition_list = { + { + .fType = Details::kEqual, + .fTypeName = "==", + }, + { + .fType = Details::kNotEqual, + .fTypeName = "!=", + }, + { + .fType = Details::kLesserThan, + .fTypeName = "<", + }, + { + .fType = Details::kGreaterThan, + .fTypeName = ">", + }, + { + .fType = Details::kLesserEqThan, + .fTypeName = "<=", + }, + { + .fType = Details::kGreaterEqThan, + .fTypeName = ">=", + }, + }; + + int32_t good_to_go = 0; + + for (auto& macro_condition : bpp_macro_condition_list) + { + if (hdr_line.find(macro_condition.fTypeName) != std::string::npos) + { + for (auto& found_macro : kMacros) + { + if (hdr_line.find(found_macro.fName) != std::string::npos) + { + good_to_go = + bpp_parse_if_condition(macro_condition, found_macro, + inactive_code, defined, hdr_line); + + break; + } + } + } + } + + if (good_to_go) + continue; + + auto line_after_if = + hdr_line.substr(hdr_line.find("if") + strlen("if") + 1); + std::string macro; + + for (auto& ch : line_after_if) + { + if (ch == ' ') + { + break; + } + + macro += ch; + } + + if (macro == "0") + { + defined = false; + inactive_code = true; + continue; + } + + if (macro == "1") + { + defined = true; + inactive_code = false; + + continue; + } + + // last try, is it defined to be one? + for (auto& macro_ref : kMacros) + { + if (macro_ref.fName.find(macro) != std::string::npos && + macro_ref.fValue == "1") + { + inactive_code = false; + defined = true; + + break; + } + } + } + else if (hdr_line[0] == kMacroPrefix && + hdr_line.find("warning") != std::string::npos) + { + auto line_after_warning = + hdr_line.substr(hdr_line.find("warning") + strlen("warning") + 1); + std::string message; + + for (auto& ch : line_after_warning) + { + if (ch == '\r' || ch == '\n') + { + break; + } + + message += ch; + } + + std::cout << "warn: " << message << std::endl; + } + else if (hdr_line[0] == kMacroPrefix && + hdr_line.find("error") != std::string::npos) + { + auto line_after_warning = + hdr_line.substr(hdr_line.find("error") + strlen("error") + 1); + std::string message; + + for (auto& ch : line_after_warning) + { + if (ch == '\r' || ch == '\n') + { + break; + } + + message += ch; + } + + throw std::runtime_error("error: " + message); + } + else if (hdr_line[0] == kMacroPrefix && + hdr_line.find("include ") != std::string::npos) + { + line_after_include = + hdr_line.substr(hdr_line.find("include ") + strlen("include ")); + + kIncludeFile: + auto it = std::find(kAllIncludes.cbegin(), kAllIncludes.cend(), + line_after_include); + + if (it != kAllIncludes.cend()) + { + continue; + } + + std::string path; + + kAllIncludes.push_back(line_after_include); + + bool enable = false; + bool not_local = false; + + for (auto& ch : line_after_include) + { + if (ch == ' ') + continue; + + if (ch == '<') + { + not_local = true; + enable = true; + + continue; + } + + if (ch == '\"') + { + not_local = false; + enable = true; + continue; + } + + if (enable) + { + path += ch; + } + } + + if (not_local) + { + bool open = false; + + if (path.ends_with('>')) + { + path.erase(path.find('>')); + } + + if (path.ends_with('"')) + { + path.erase(path.find('"')); + } + + for (auto& include : kIncludes) + { + std::string header_path = include; + header_path.push_back('-'); + header_path += path; + + std::ifstream header(header_path); + + if (!header.is_open()) + continue; + + open = true; + + bpp_parse_file(header, pp_out); + + break; + } + + if (!open) + { + throw std::runtime_error("bpp: no such include file: " + path); + } + } + else + { + std::ifstream header(path); + + if (!header.is_open()) + throw std::runtime_error("bpp: no such include file: " + path); + + bpp_parse_file(header, pp_out); + } + } + else + { + std::cerr << ("bpp: unknown pre-processor directive, " + hdr_line) + << "\n"; + continue; + } + } + } + catch (std::out_of_range& oor) + { + return; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief main entrypoint of app. + +///////////////////////////////////////////////////////////////////////////////////////// + +TOOLCHAINKIT_MODULE(CPlusPlusPreprocessorMain) +{ + try + { + bool skip = false; + bool double_skip = false; + + Details::bpp_macro macro_1; + + macro_1.fName = "__true"; + macro_1.fValue = "1"; + + kMacros.push_back(macro_1); + + Details::bpp_macro macro_0; + + macro_0.fName = "__false"; + macro_0.fValue = "0"; + + kMacros.push_back(macro_0); + + Details::bpp_macro macro_zka; + + macro_zka.fName = "__TOOLCHAINKIT__"; + macro_zka.fValue = "1"; + + kMacros.push_back(macro_zka); + + Details::bpp_macro macro_size_t; + macro_size_t.fName = "__SIZE_TYPE__"; + macro_size_t.fValue = "unsigned long long int"; + + kMacros.push_back(macro_size_t); + + macro_size_t.fName = "__UINT32_TYPE__"; + macro_size_t.fValue = "unsigned int"; + + kMacros.push_back(macro_size_t); + + macro_size_t.fName = "__UINTPTR_TYPE__"; + macro_size_t.fValue = "unsigned int"; + + kMacros.push_back(macro_size_t); + + for (auto index = 1UL; index < argc; ++index) + { + if (skip) + { + skip = false; + continue; + } + + if (double_skip) + { + ++index; + double_skip = false; + continue; + } + + if (argv[index][0] == '-') + { + if (strcmp(argv[index], "--bpp:ver") == 0) + { + printf("%s\n", "bpp v1.11, (c) Theater Quality Incorporated"); + return 0; + } + + if (strcmp(argv[index], "--bpp:?") == 0) + { + printf("%s\n", "ZKA Preprocessor Driver v1.11, (c) Theater Quality Incorporated"); + printf("%s\n", "--bpp:working-dir <path>: set directory to working path."); + printf("%s\n", "--bpp:include-dir <path>: add directory to include path."); + printf("%s\n", "--bpp:def <name> <value>: define a macro."); + printf("%s\n", "--bpp:ver: print the version."); + printf("%s\n", "--bpp:?: show help (this current command)."); + + return 0; + } + + if (strcmp(argv[index], "--bpp:include-dir") == 0) + { + std::string inc = argv[index + 1]; + + skip = true; + + kIncludes.push_back(inc); + } + + if (strcmp(argv[index], "--bpp:working-dir") == 0) + { + std::string inc = argv[index + 1]; + skip = true; + kWorkingDir = inc; + } + + if (strcmp(argv[index], "--bpp:def") == 0 && argv[index + 1] != nullptr && + argv[index + 2] != nullptr) + { + std::string macro_key = argv[index + 1]; + + std::string macro_value; + bool is_string = false; + + for (int argv_find_len = 0; argv_find_len < strlen(argv[index]); + ++argv_find_len) + { + if (!isdigit(argv[index][argv_find_len])) + { + is_string = true; + macro_value += "\""; + + break; + } + } + + macro_value += argv[index + 2]; + + if (is_string) + macro_value += "\""; + + Details::bpp_macro macro; + macro.fName = macro_key; + macro.fValue = macro_value; + + kMacros.push_back(macro); + + double_skip = true; + } + + continue; + } + + kFiles.emplace_back(argv[index]); + } + + if (kFiles.empty()) + return TOOLCHAINKIT_EXEC_ERROR; + + for (auto& file : kFiles) + { + if (!std::filesystem::exists(file)) + continue; + + std::ifstream file_descriptor(file); + std::ofstream file_descriptor_pp(file + ".pp"); + + bpp_parse_file(file_descriptor, file_descriptor_pp); + } + + return 0; + } + catch (const std::runtime_error& e) + { + std::cout << e.what() << '\n'; + } + + return 1; +} + +// Last rev 8-1-24 diff --git a/dev/LibCompiler/src/Detail/AsmUtils.h b/dev/LibCompiler/src/Detail/AsmUtils.h new file mode 100644 index 0000000..0825fa5 --- /dev/null +++ b/dev/LibCompiler/src/Detail/AsmUtils.h @@ -0,0 +1,116 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include <LibCompiler/AAL/AssemblyInterface.h> +#include <LibCompiler/Parser.h> + +using namespace LibCompiler; + +namespace Details +{ + extern void print_error(std::string reason, std::string file) noexcept; + extern void print_warning(std::string reason, std::string file) noexcept; +} // namespace Details + +/// @brief Get Number from lineBuffer. +/// @param lineBuffer the lineBuffer to fetch from. +/// @param numberKey where to seek that number. +/// @return +static NumberCast32 GetNumber32(std::string lineBuffer, std::string numberKey) +{ + auto pos = lineBuffer.find(numberKey) + numberKey.size(); + + while (lineBuffer[pos] == ' ') + { + ++pos; + } + + switch (lineBuffer[pos + 1]) + { + case 'x': { + if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 16); !res) + { + if (errno != 0) + { + Details::print_error("invalid hex number: " + lineBuffer, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 16)); + + if (kVerbose) + { + kStdOut << "asm: found a base 16 number here: " << lineBuffer.substr(pos) + << "\n"; + } + + return numOffset; + } + case 'b': { + if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 2); !res) + { + if (errno != 0) + { + Details::print_error("invalid binary number:" + lineBuffer, "LibCompiler"); + throw std::runtime_error("invalid_bin"); + } + } + + NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 2)); + + if (kVerbose) + { + kStdOut << "asm: found a base 2 number here:" << lineBuffer.substr(pos) + << "\n"; + } + + return numOffset; + } + case 'o': { + if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 7); !res) + { + if (errno != 0) + { + Details::print_error("invalid octal number: " + lineBuffer, "LibCompiler"); + throw std::runtime_error("invalid_octal"); + } + } + + NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 7)); + + if (kVerbose) + { + kStdOut << "asm: found a base 8 number here:" << lineBuffer.substr(pos) + << "\n"; + } + + return numOffset; + } + default: { + if (auto res = strtol(lineBuffer.substr(pos).c_str(), nullptr, 10); !res) + { + if (errno != 0) + { + Details::print_error("invalid hex number: " + lineBuffer, "LibCompiler"); + throw std::runtime_error("invalid_hex"); + } + } + + NumberCast32 numOffset(strtol(lineBuffer.substr(pos).c_str(), nullptr, 10)); + + if (kVerbose) + { + kStdOut << "asm: found a base 10 number here:" << lineBuffer.substr(pos) + << "\n"; + } + + return numOffset; + } + } +} diff --git a/dev/LibCompiler/src/Detail/ClUtils.h b/dev/LibCompiler/src/Detail/ClUtils.h new file mode 100644 index 0000000..4596148 --- /dev/null +++ b/dev/LibCompiler/src/Detail/ClUtils.h @@ -0,0 +1,14 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + +------------------------------------------- */ + +#pragma once + +#include <LibCompiler/AAL/AssemblyInterface.h> +#include <LibCompiler/Parser.h> + +#define kZero64Section ".zero64" +#define kCode64Section ".code64" +#define kData64Section ".data64" diff --git a/dev/LibCompiler/src/Detail/ReadMe.md b/dev/LibCompiler/src/Detail/ReadMe.md new file mode 100644 index 0000000..e9d0a2c --- /dev/null +++ b/dev/LibCompiler/src/Detail/ReadMe.md @@ -0,0 +1,3 @@ +# Compiler utilities. + +A list of headers used to make compiler/assemblers. diff --git a/dev/LibCompiler/src/DynamicLinker64PEF.cc b/dev/LibCompiler/src/DynamicLinker64PEF.cc new file mode 100644 index 0000000..67b6105 --- /dev/null +++ b/dev/LibCompiler/src/DynamicLinker64PEF.cc @@ -0,0 +1,774 @@ +/* ------------------------------------------- + + Copyright (C) 2024 Theater Quality Inc, all rights reserved + + @file DynamicLinker64PEF.cc + @brief: C++ 64-Bit PEF Linker. + +------------------------------------------- */ + +/// @author EL Mahrouss Amlal (amlel) +/// @brief TQ Media 64-bit PEF Linker. +/// Last Rev: Sat Feb 24 CET 2024 +/// @note Do not look up for anything with .code64/.data64/.zero64! +/// It will be loaded when the program loader will start the image. + +//! Toolchain Kit. +#include <LibCompiler/Defines.h> + +#include <LibCompiler/NFC/ErrorID.h> + +//! Assembler Kit +#include <LibCompiler/AAL/AssemblyInterface.h> + +//! Preferred Executable Format +#include <LibCompiler/NFC/PEF.h> +#include <LibCompiler/UUID.h> + +//! Release macros. +#include <LibCompiler/Version.h> + +//! Advanced Executable Object Format. +#include <LibCompiler/NFC/AE.h> +#include <cstdint> + +#define kLinkerVersionStr "TQ Media 64-Bit Linker %s, (c) Theater Quality Incorporated 2024, all rights reserved.\n" + +#define MemoryCopy(DST, SRC, SZ) memcpy(DST, SRC, SZ) +#define StringCompare(DST, SRC) strcmp(DST, SRC) + +#define kPefNoCpu 0U +#define kPefNoSubCpu 0U + +#define kWhite "\e[0;97m" + +#define kStdOut (std::cout << kWhite << "ld64: ") + +#define kLinkerDefaultOrigin kPefBaseOrigin +#define kLinkerId (0x5046FF) +#define kLinkerAbiContainer "Container:ABI:" + +/// @brief PEF stack size symbol. +#define kLinkerStackSizeSymbol "SizeOfReserveStack" + +namespace Details +{ +struct DynamicLinkerBlob final +{ + std::vector<CharType> fPefBlob; // PEF code/bss/data blob. + std::uintptr_t fAEOffset; // the offset of the PEF container header.. +}; +} + +enum +{ + kABITypeStart = 0x1010, /* Invalid ABI start of ABI list. */ + kABITypeZKA = 0x5046, /* PF (ZKA PEF ABI) */ + kABITypeInvalid = 0xFFFF, +}; + +static LibCompiler::String kOutput = ""; +static Int32 kAbi = kABITypeZKA; +static Int32 kSubArch = kPefNoSubCpu; +static Int32 kArch = LibCompiler::kPefArchInvalid; +static Bool kFatBinaryEnable = false; +static Bool kStartFound = false; +static Bool kDuplicateSymbols = false; +static Bool kVerbose = false; + +/* ld64 is to be found, mld is to be found at runtime. */ +static const char* kLdDefineSymbol = ":UndefinedSymbol:"; +static const char* kLdDynamicSym = ":RuntimeSymbol:"; + +/* object code and list. */ +static std::vector<LibCompiler::String> kObjectList; +static std::vector<Details::DynamicLinkerBlob> kObjectBytes; + +static uintptr_t kMIBCount = 8; +static uintptr_t kByteCount = 1024; + +#define kPrintF printf +#define kLinkerSplash() kPrintF(kWhite kLinkerVersionStr, kDistVersion) + +/// @brief ZKA 64-bit Linker. +/// @note This linker is made for PEF executable, thus ZKA based OSes. +TOOLCHAINKIT_MODULE(DynamicLinker64PEF) +{ + bool is_executable = true; + + /** + * @brief parse flags and trigger options. + */ + for (size_t linker_arg = 1; linker_arg < argc; ++linker_arg) + { + if (StringCompare(argv[linker_arg], "--ld64:help") == 0) + { + kLinkerSplash(); + + kStdOut << "--ld64:ver: Show linker version.\n"; + kStdOut << "--ld64:?: Show linker help.\n"; + kStdOut << "--ld64:verbose: Enable linker trace.\n"; + kStdOut << "--ld64:dylib: Output as a Dylib PEF.\n"; + kStdOut << "--ld64:fat: Output as a FAT PEF.\n"; + kStdOut << "--ld64:32k: Output as a 32x0 PEF.\n"; + kStdOut << "--ld64:64k: Output as a 64x0 PEF.\n"; + kStdOut << "--ld64:amd64: Output as a AMD64 PEF.\n"; + kStdOut << "--ld64:rv64: Output as a RISC-V PEF.\n"; + kStdOut << "--ld64:power64: Output as a POWER PEF.\n"; + kStdOut << "--ld64:arm64: Output as a ARM64 PEF.\n"; + kStdOut << "--ld64:output: Select the output file name.\n"; + + return EXIT_SUCCESS; + } + else if (StringCompare(argv[linker_arg], "--ld64:version") == 0) + { + kLinkerSplash(); + return EXIT_SUCCESS; + } + else if (StringCompare(argv[linker_arg], "--ld64:fat-binary") == 0) + { + kFatBinaryEnable = true; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:64k") == 0) + { + kArch = LibCompiler::kPefArch64000; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:amd64") == 0) + { + kArch = LibCompiler::kPefArchAMD64; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:32k") == 0) + { + kArch = LibCompiler::kPefArch32000; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:power64") == 0) + { + kArch = LibCompiler::kPefArchPowerPC; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:riscv64") == 0) + { + kArch = LibCompiler::kPefArchRISCV; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:arm64") == 0) + { + kArch = LibCompiler::kPefArchARM64; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:verbose") == 0) + { + kVerbose = true; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:dylib") == 0) + { + if (kOutput.empty()) + { + continue; + } + + if (kOutput.find(kPefExt) != LibCompiler::String::npos) + kOutput.erase(kOutput.find(kPefExt), strlen(kPefExt)); + + kOutput += kPefDylibExt; + + is_executable = false; + + continue; + } + else if (StringCompare(argv[linker_arg], "--ld64:output") == 0) + { + kOutput = argv[linker_arg + 1]; + ++linker_arg; + + continue; + } + else + { + if (argv[linker_arg][0] == '-') + { + kStdOut << "unknown flag: " << argv[linker_arg] << "\n"; + return EXIT_FAILURE; + } + + kObjectList.emplace_back(argv[linker_arg]); + + continue; + } + } + + if (kOutput.empty()) + { + kStdOut << "no output filename set." << std::endl; + return TOOLCHAINKIT_EXEC_ERROR; + } + + // sanity check. + if (kObjectList.empty()) + { + kStdOut << "no input files." << std::endl; + return TOOLCHAINKIT_EXEC_ERROR; + } + else + { + namespace fs = std::filesystem; + + // check for existing files, if they don't throw an error. + for (auto& obj : kObjectList) + { + if (!fs::exists(obj)) + { + // if filesystem doesn't find file + // -> throw error. + kStdOut << "no such file: " << obj << std::endl; + return TOOLCHAINKIT_EXEC_ERROR; + } + } + } + + // PEF expects a valid target architecture when outputing a binary. + if (kArch == 0) + { + kStdOut << "no target architecture set, can't continue." << std::endl; + return TOOLCHAINKIT_EXEC_ERROR; + } + + LibCompiler::PEFContainer pef_container{}; + + int32_t archs = kArch; + + pef_container.Count = 0UL; + pef_container.Kind = is_executable ? LibCompiler::kPefKindExec : LibCompiler::kPefKindDylib; + pef_container.SubCpu = kSubArch; + pef_container.Linker = kLinkerId; // Theater Quality Incorporated Linker + pef_container.Abi = kAbi; // Multi-Processor UX ABI + pef_container.Magic[0] = kPefMagic[kFatBinaryEnable ? 2 : 0]; + pef_container.Magic[1] = kPefMagic[1]; + pef_container.Magic[2] = kPefMagic[kFatBinaryEnable ? 0 : 2]; + pef_container.Magic[3] = kPefMagic[3]; + pef_container.Version = kPefVersion; + + // specify the start address, can be 0x10000 + pef_container.Start = kLinkerDefaultOrigin; + pef_container.HdrSz = sizeof(LibCompiler::PEFContainer); + + std::ofstream output_fc(kOutput, std::ofstream::binary); + + if (output_fc.bad()) + { + if (kVerbose) + { + kStdOut << "error: " << strerror(errno) << "\n"; + } + + return TOOLCHAINKIT_FILE_NOT_FOUND; + } + + //! Read AE to convert as PEF. + + std::vector<LibCompiler::PEFCommandHeader> command_headers; + LibCompiler::Utils::AEReadableProtocol reader_protocol{}; + + for (const auto& objectFile : kObjectList) + { + if (!std::filesystem::exists(objectFile)) + continue; + + LibCompiler::AEHeader hdr{}; + + reader_protocol.FP = std::ifstream(objectFile, std::ifstream::binary); + reader_protocol.FP >> hdr; + + auto ae_header = hdr; + + if (ae_header.fMagic[0] == kAEMag0 && ae_header.fMagic[1] == kAEMag1 && + ae_header.fSize == sizeof(LibCompiler::AEHeader)) + { + if (ae_header.fArch != kArch) + { + if (kVerbose) + kStdOut << "info: is this a FAT binary? : "; + + if (!kFatBinaryEnable) + { + if (kVerbose) + kStdOut << "No.\n"; + + kStdOut << "error: object " << objectFile + << " is a different kind of architecture and output isn't " + "treated as a FAT binary." + << std::endl; + + return TOOLCHAINKIT_FAT_ERROR; + } + else + { + if (kVerbose) + { + kStdOut << "Architecture matches what we expect.\n"; + } + } + } + + // append arch type to archs varaible. + archs |= ae_header.fArch; + std::size_t cnt = ae_header.fCount; + + if (kVerbose) + kStdOut << "object header found, record count: " << cnt << "\n"; + + pef_container.Count = cnt; + + char_type* raw_ae_records = + new char_type[cnt * sizeof(LibCompiler::AERecordHeader)]; + + memset(raw_ae_records, 0, cnt * sizeof(LibCompiler::AERecordHeader)); + + auto* ae_records = reader_protocol.Read(raw_ae_records, cnt); + + for (size_t ae_record_index = 0; ae_record_index < cnt; + ++ae_record_index) + { + LibCompiler::PEFCommandHeader command_header{0}; + std::size_t offset_of_obj = ae_records[ae_record_index].fOffset; + + MemoryCopy(command_header.Name, ae_records[ae_record_index].fName, + kPefNameLen); + + LibCompiler::String cmd_hdr_name(command_header.Name); + + // check this header if it's any valid. + if (cmd_hdr_name.find(kPefCode64) == + LibCompiler::String::npos && + cmd_hdr_name.find(kPefData64) == + LibCompiler::String::npos && + cmd_hdr_name.find(kPefZero64) == + LibCompiler::String::npos) + { + if (cmd_hdr_name.find(kPefStart) == + LibCompiler::String::npos && + *command_header.Name == 0) + { + if (cmd_hdr_name.find(kLdDefineSymbol) != + LibCompiler::String::npos) + { + goto ld_mark_header; + } + else + { + continue; + } + } + } + + if (cmd_hdr_name.find(kPefStart) != + LibCompiler::String::npos && + cmd_hdr_name.find(kPefCode64) != + LibCompiler::String::npos) + { + kStartFound = true; + } + + ld_mark_header: + command_header.Offset = offset_of_obj; + command_header.Kind = ae_records[ae_record_index].fKind; + command_header.Size = ae_records[ae_record_index].fSize; + command_header.Cpu = ae_header.fArch; + command_header.SubCpu = ae_header.fSubArch; + + if (kVerbose) + { + kStdOut << "Record: " + << ae_records[ae_record_index].fName << " is marked.\n"; + + kStdOut << "Record offset: " << command_header.Offset << "\n"; + } + + command_headers.emplace_back(command_header); + } + + delete[] raw_ae_records; + + std::vector<char> bytes; + bytes.resize(ae_header.fCodeSize); + + // TODO: Port this to NeFS. + + reader_protocol.FP.seekg(std::streamsize(ae_header.fStartCode)); + reader_protocol.FP.read(bytes.data(), std::streamsize(ae_header.fCodeSize)); + + for (auto& byte : bytes) + { + kObjectBytes.push_back({ .fPefBlob = bytes, .fAEOffset = ae_header.fStartCode }); + } + + reader_protocol.FP.close(); + + continue; + } + + kStdOut << "Not an object container: " << objectFile << std::endl; + // don't continue, it is a fatal error. + return TOOLCHAINKIT_EXEC_ERROR; + } + + pef_container.Cpu = archs; + + output_fc << pef_container; + + if (kVerbose) + { + kStdOut << "Wrote container header.\n"; + } + + output_fc.seekp(std::streamsize(pef_container.HdrSz)); + + std::vector<LibCompiler::String> not_found; + std::vector<LibCompiler::String> symbols; + + // step 2: check for errors (multiple symbols, undefined ones) + + for (auto& command_hdr : command_headers) + { + // check if this symbol needs to be resolved. + if (LibCompiler::String(command_hdr.Name).find(kLdDefineSymbol) != + LibCompiler::String::npos && + LibCompiler::String(command_hdr.Name).find(kLdDynamicSym) == LibCompiler::String::npos) + { + if (kVerbose) + kStdOut << "Found undefined symbol: " << command_hdr.Name << "\n"; + + if (auto it = std::find(not_found.begin(), not_found.end(), + LibCompiler::String(command_hdr.Name)); + it == not_found.end()) + { + not_found.emplace_back(command_hdr.Name); + } + } + + symbols.emplace_back(command_hdr.Name); + } + + // Now try to solve these symbols. + + for (size_t not_found_idx = 0; not_found_idx < command_headers.size(); + ++not_found_idx) + { + if (const auto it = std::find(not_found.begin(), not_found.end(), + LibCompiler::String(command_headers[not_found_idx].Name)); + it != not_found.end()) + { + LibCompiler::String symbol_imp = *it; + + if (symbol_imp.find(kLdDefineSymbol) == LibCompiler::String::npos) + continue; + + // erase the lookup prefix. + symbol_imp.erase( + 0, symbol_imp.find(kLdDefineSymbol) + strlen(kLdDefineSymbol)); + + // demangle everything. + while (symbol_imp.find('$') != LibCompiler::String::npos) + symbol_imp.erase(symbol_imp.find('$'), 1); + + // the reason we do is because, this may not match the symbol, and we need + // to look for other matching symbols. + for (auto& command_hdr : command_headers) + { + if (LibCompiler::String(command_hdr.Name).find(symbol_imp) != + LibCompiler::String::npos && + LibCompiler::String(command_hdr.Name).find(kLdDefineSymbol) == + LibCompiler::String::npos) + { + LibCompiler::String undefined_symbol = command_hdr.Name; + auto result_of_sym = + undefined_symbol.substr(undefined_symbol.find(symbol_imp)); + + for (int i = 0; result_of_sym[i] != 0; ++i) + { + if (result_of_sym[i] != symbol_imp[i]) + goto ld_continue_search; + } + + not_found.erase(it); + + if (kVerbose) + kStdOut << "found symbol: " << command_hdr.Name << "\n"; + + break; + } + } + + ld_continue_search: + continue; + } + } + + // step 3: check for errors (recheck if we have those symbols.) + + if (!kStartFound && is_executable) + { + if (kVerbose) + kStdOut + << "undefined entrypoint: " << kPefStart << ", you may have forget to ld64 " + "against your compiler's runtime library.\n"; + + kStdOut << "undefined entrypoint " << kPefStart + << " for executable: " << kOutput << "\n"; + } + + // step 4: write all PEF commands. + + LibCompiler::PEFCommandHeader date_cmd_hdr{}; + + time_t timestamp = time(nullptr); + + LibCompiler::String timeStampStr = "Container:BuildEpoch:"; + timeStampStr += std::to_string(timestamp); + + strncpy(date_cmd_hdr.Name, timeStampStr.c_str(), timeStampStr.size()); + + date_cmd_hdr.Flags = 0; + date_cmd_hdr.Kind = LibCompiler::kPefZero; + date_cmd_hdr.Offset = output_fc.tellp(); + date_cmd_hdr.Size = timeStampStr.size(); + + command_headers.push_back(date_cmd_hdr); + + LibCompiler::PEFCommandHeader abi_cmd_hdr{}; + + LibCompiler::String abi = kLinkerAbiContainer; + + switch (kArch) + { + case LibCompiler::kPefArchAMD64: { + abi += "MSFT"; + break; + } + case LibCompiler::kPefArchPowerPC: { + abi += "SYSV"; + break; + } + case LibCompiler::kPefArch32000: + case LibCompiler::kPefArch64000: { + abi += " ZWS"; + break; + } + default: { + abi += " IDK"; + break; + } + } + + MemoryCopy(abi_cmd_hdr.Name, abi.c_str(), abi.size()); + + abi_cmd_hdr.Size = abi.size(); + abi_cmd_hdr.Offset = output_fc.tellp(); + abi_cmd_hdr.Flags = 0; + abi_cmd_hdr.Kind = LibCompiler::kPefLinkerID; + + command_headers.push_back(abi_cmd_hdr); + + LibCompiler::PEFCommandHeader stack_cmd_hdr{0}; + + stack_cmd_hdr.Cpu = kArch; + stack_cmd_hdr.Flags = 0; + stack_cmd_hdr.Size = sizeof(uintptr_t); + stack_cmd_hdr.Offset = 0; + + MemoryCopy(stack_cmd_hdr.Name, kLinkerStackSizeSymbol, strlen(kLinkerStackSizeSymbol)); + + command_headers.push_back(stack_cmd_hdr); + + LibCompiler::PEFCommandHeader uuid_cmd_hdr{}; + + std::random_device rd; + + auto seedData = std::array<int, std::mt19937::state_size>{}; + std::generate(std::begin(seedData), std::end(seedData), std::ref(rd)); + std::seed_seq seq(std::begin(seedData), std::end(seedData)); + std::mt19937 generator(seq); + + auto gen = uuids::uuid_random_generator{generator}; + uuids::uuid id = gen(); + auto uuidStr = uuids::to_string(id); + + MemoryCopy(uuid_cmd_hdr.Name, "Container:GUID:4:", strlen("Container:GUID:4:")); + MemoryCopy(uuid_cmd_hdr.Name + strlen("Container:GUID:4:"), uuidStr.c_str(), + uuidStr.size()); + + uuid_cmd_hdr.Size = strlen(uuid_cmd_hdr.Name); + uuid_cmd_hdr.Offset = output_fc.tellp(); + uuid_cmd_hdr.Flags = LibCompiler::kPefLinkerID; + uuid_cmd_hdr.Kind = LibCompiler::kPefZero; + + command_headers.push_back(uuid_cmd_hdr); + + // prepare a symbol vector. + std::vector<LibCompiler::String> undef_symbols; + std::vector<LibCompiler::String> dupl_symbols; + std::vector<LibCompiler::String> resolve_symbols; + + constexpr Int32 cPaddingOffset = 16; + + size_t previous_offset = (command_headers.size() * sizeof(LibCompiler::PEFCommandHeader)) + cPaddingOffset; + + // Finally write down the command headers. + // And check for any duplications + for (size_t commandHeaderIndex = 0UL; + commandHeaderIndex < command_headers.size(); ++commandHeaderIndex) + { + if (LibCompiler::String(command_headers[commandHeaderIndex].Name) + .find(kLdDefineSymbol) != LibCompiler::String::npos && + LibCompiler::String(command_headers[commandHeaderIndex].Name) + .find(kLdDynamicSym) == LibCompiler::String::npos) + { + // ignore :UndefinedSymbol: headers, they do not contain code. + continue; + } + + LibCompiler::String symbol_name = command_headers[commandHeaderIndex].Name; + + if (!symbol_name.empty()) + { + undef_symbols.emplace_back(symbol_name); + } + + command_headers[commandHeaderIndex].Offset += previous_offset; + previous_offset += command_headers[commandHeaderIndex].Size; + + LibCompiler::String name = command_headers[commandHeaderIndex].Name; + + /// so this is valid when we get to the entrypoint. + /// it is always a code64 container. And should equal to kPefStart as well. + /// this chunk of code updates the pef_container.Start with the updated offset. + if (name.find(kPefStart) != LibCompiler::String::npos && + name.find(kPefCode64) != LibCompiler::String::npos) + { + pef_container.Start = command_headers[commandHeaderIndex].Offset; + auto tellCurPos = output_fc.tellp(); + + output_fc.seekp(0); + output_fc << pef_container; + + output_fc.seekp(tellCurPos); + } + + if (kVerbose) + { + kStdOut << "Command header name: " << name << "\n"; + kStdOut << "Real address of command header content: " << command_headers[commandHeaderIndex].Offset << "\n"; + } + + output_fc << command_headers[commandHeaderIndex]; + + for (size_t sub_command_header_index = 0UL; + sub_command_header_index < command_headers.size(); + ++sub_command_header_index) + { + if (sub_command_header_index == commandHeaderIndex) + continue; + + if (LibCompiler::String(command_headers[sub_command_header_index].Name) + .find(kLdDefineSymbol) != LibCompiler::String::npos && + LibCompiler::String(command_headers[sub_command_header_index].Name) + .find(kLdDynamicSym) == LibCompiler::String::npos) + { + if (kVerbose) + { + kStdOut << "ignore :UndefinedSymbol: command header...\n"; + } + + // ignore :UndefinedSymbol: headers, they do not contain code. + continue; + } + + auto& command_hdr = command_headers[sub_command_header_index]; + + if (command_hdr.Name == + LibCompiler::String(command_headers[commandHeaderIndex].Name)) + { + if (std::find(dupl_symbols.cbegin(), dupl_symbols.cend(), + command_hdr.Name) == dupl_symbols.cend()) + { + dupl_symbols.emplace_back(command_hdr.Name); + } + + if (kVerbose) + kStdOut << "found duplicate symbol: " << command_hdr.Name + << "\n"; + + kDuplicateSymbols = true; + } + } + } + + if (!dupl_symbols.empty()) + { + for (auto& symbol : dupl_symbols) + { + kStdOut << "Multiple symbols of " << symbol << ".\n"; + } + + return TOOLCHAINKIT_EXEC_ERROR; + } + + // step 2.5: write program bytes. + + for (auto& struct_of_blob : kObjectBytes) + { + output_fc.write(struct_of_blob.fPefBlob.data(), struct_of_blob.fPefBlob.size()); + } + + if (kVerbose) + kStdOut << "wrote contents of: " << kOutput << "\n"; + + // step 3: check if we have those symbols + + std::vector<LibCompiler::String> unreferenced_symbols; + + for (auto& command_hdr : command_headers) + { + if (auto it = std::find(not_found.begin(), not_found.end(), + LibCompiler::String(command_hdr.Name)); + it != not_found.end()) + { + unreferenced_symbols.emplace_back(command_hdr.Name); + } + } + + if (!unreferenced_symbols.empty()) + { + for (auto& unreferenced_symbol : unreferenced_symbols) + { + kStdOut << "undefined symbol " << unreferenced_symbol << "\n"; + } + } + + if (!kStartFound || kDuplicateSymbols && std::filesystem::exists(kOutput) || + !unreferenced_symbols.empty()) + { + if (kVerbose) + kStdOut << "file: " << kOutput + << ", is corrupt, removing file...\n"; + + return TOOLCHAINKIT_EXEC_ERROR; + } + + return EXIT_SUCCESS; +} + +// Last rev 13-1-24 diff --git a/dev/LibCompiler/src/String.cc b/dev/LibCompiler/src/String.cc new file mode 100644 index 0000000..0804c2b --- /dev/null +++ b/dev/LibCompiler/src/String.cc @@ -0,0 +1,256 @@ +/* + * ======================================================== + * + * LibCompiler + * Copyright (C) 2024 Theater Quality Inc, all rights reserved. + * + * ======================================================== + */ + +/** + * @file String.cxx + * @author Amlal (amlal@el-mahrouss-logic.com) + * @brief C++ string manipulation API. + * @version 0.2 + * @date 2024-01-23 + * + * @copyright Copyright (c) Theater Quality Incorporated + * + */ + +#include <LibCompiler/NFC/String.h> + +namespace LibCompiler +{ + CharType* StringView::Data() + { + return m_Data; + } + + const CharType* StringView::CData() const + { + return m_Data; + } + + SizeType StringView::Length() const + { + return strlen(m_Data); + } + + bool StringView::operator==(const StringView& rhs) const + { + if (rhs.Length() != Length()) + return false; + + for (SizeType index = 0; index < Length(); ++index) + { + if (rhs.m_Data[index] != m_Data[index]) + return false; + } + + return true; + } + + bool StringView::operator==(const CharType* rhs) const + { + if (string_length(rhs) != Length()) + return false; + + for (SizeType index = 0; index < string_length(rhs); ++index) + { + if (rhs[index] != m_Data[index]) + return false; + } + + return true; + } + + bool StringView::operator!=(const StringView& rhs) const + { + if (rhs.Length() != Length()) + return false; + + for (SizeType index = 0; index < rhs.Length(); ++index) + { + if (rhs.m_Data[index] == m_Data[index]) + return false; + } + + return true; + } + + bool StringView::operator!=(const CharType* rhs) const + { + if (string_length(rhs) != Length()) + return false; + + for (SizeType index = 0; index < string_length(rhs); ++index) + { + if (rhs[index] == m_Data[index]) + return false; + } + + return true; + } + + StringView StringBuilder::Construct(const CharType* data) + { + if (!data || *data == 0) + return StringView(0); + + StringView view(strlen(data)); + view += data; + + return view; + } + + const char* StringBuilder::FromInt(const char* fmt, int i) + { + if (!fmt) + return ("-1"); + + auto ret_len = 8 + string_length(fmt); + char* ret = new char[ret_len]; + + if (!ret) + return ("-1"); + + memset(ret, 0, ret_len); + + CharType result[sizeof(int64_t)]; + + if (!to_str(result, sizeof(int64_t), i)) + { + delete[] ret; + return ("-1"); + } + + const auto fmt_len = string_length(fmt); + const auto res_len = string_length(result); + + for (SizeType idx = 0; idx < fmt_len; ++idx) + { + if (fmt[idx] == '%') + { + SizeType result_cnt = idx; + + for (auto y_idx = 0; y_idx < res_len; ++y_idx) + { + ret[y_idx] = result[result_cnt]; + ++result_cnt; + } + + break; + } + + ret[idx] = fmt[idx]; + } + + return ret; /* Copy that ret into a buffer, Alloca allocates to the stack */ + } + + const char* StringBuilder::FromBool(const char* fmt, bool i) + { + if (!fmt) + return ("?"); + + const char* boolean_expr = i ? "true" : "false"; + char* ret = new char[i ? 4 : 5 + string_length(fmt)]; + + if (!ret) + return ("?"); + + const auto fmt_len = string_length(fmt); + const auto res_len = string_length(boolean_expr); + + for (SizeType idx = 0; idx < fmt_len; ++idx) + { + if (fmt[idx] == '%') + { + SizeType result_cnt = idx; + + for (auto y_idx = idx; y_idx < res_len; ++y_idx) + { + ret[result_cnt] = boolean_expr[y_idx]; + ++result_cnt; + } + + break; + } + + ret[idx] = fmt[idx]; + } + + return ret; + } + + bool StringBuilder::Equals(const char* lhs, const char* rhs) + { + if (string_length(rhs) != string_length(lhs)) + return false; + + for (SizeType index = 0; index < string_length(rhs); ++index) + { + if (rhs[index] != lhs[index]) + return false; + } + + return true; + } + + const char* StringBuilder::Format(const char* fmt, const char* fmtRight) + { + if (!fmt || !fmtRight) + return ("?"); + + char* ret = new char[string_length(fmtRight) + string_length(fmtRight)]; + if (!ret) + return ("?"); + + for (SizeType idx = 0; idx < string_length(fmt); ++idx) + { + if (fmt[idx] == '%') + { + SizeType result_cnt = idx; + + for (SizeType y_idx = 0; y_idx < string_length(fmtRight); ++y_idx) + { + ret[result_cnt] = fmtRight[y_idx]; + ++result_cnt; + } + + break; + } + + ret[idx] = fmt[idx]; + } + + return ret; + } + + StringView& StringView::operator+=(const CharType* rhs) + { + if (strlen(rhs) > this->m_Sz) + { + throw std::runtime_error("out_of_bounds: StringView"); + } + + memcpy(this->m_Data + this->m_Cur, rhs, strlen(rhs)); + this->m_Cur += strlen(rhs); + + return *this; + } + + StringView& StringView::operator+=(const StringView& rhs) + { + if (rhs.m_Cur > this->m_Sz) + { + throw std::runtime_error("out_of_bounds: StringView"); + } + + memcpy(this->m_Data + this->m_Cur, rhs.CData(), strlen(rhs.CData())); + this->m_Cur += strlen(rhs.CData()); + + return *this; + } +} // namespace LibCompiler |
