From 583748c67453a62382e0c0e77aa8ca6535900ae0 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Tue, 14 May 2024 10:02:08 +0200 Subject: MHR-21: Add function signature to output (C++) Signed-off-by: Amlal El Mahrouss --- Examples/ExampleCDialect.S | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) (limited to 'Examples/ExampleCDialect.S') diff --git a/Examples/ExampleCDialect.S b/Examples/ExampleCDialect.S index d937aed..9d90d46 100644 --- a/Examples/ExampleCDialect.S +++ b/Examples/ExampleCDialect.S @@ -1,28 +1,14 @@ -# Path: Examples/ExampleCDialect.c -# Language: POWER Assembly (Generated from C) -# Build Date: 2024-4-28 +; Path: Examples/ExampleCDialect.cc +; Language: MPCC assembly. (Generated from C++) +; Date: 2024-5-14 +#bits 64 +#org 0x1000000 +export .code64 __MPCC_int___ImageStart +mov r8, 0 - - - -dword export .code64 __ImageStart - - - li r3,0x1000 - - cmpw r10, r11 - beq import __MPCC_IF_PROC_6099380296 -dword export .code64 __MPCC_IF_PROC_6099380296 - - - - mr r31, r3 - blr - - - mr r31, r0 - blr - +mov r9, 36 +mov rax, r8 +ret -- cgit v1.2.3 From d764e44d4b43392a86a1c7c0ab1ad455fafc3294 Mon Sep 17 00:00:00 2001 From: Amlal Date: Wed, 17 Jul 2024 18:36:07 +0200 Subject: [IMP] Much better source tree. Signed-off-by: Amlal --- .vscode/c_cpp_properties.json | 60 +- Comm/AsmKit/AsmKit.hpp | 217 --- Comm/AsmKit/CPU/32x0.hpp | 95 -- Comm/AsmKit/CPU/64x0.hpp | 108 -- Comm/AsmKit/CPU/amd64.hpp | 57 - Comm/AsmKit/CPU/arm64.hpp | 26 - Comm/AsmKit/CPU/ppc.hpp | 1929 --------------------------- Comm/CompilerKit.hpp | 33 - Comm/Defines.hpp | 147 -- Comm/ParserKit.hpp | 173 --- Comm/Public/SDK/CRT/__mpcc_alloca.hxx | 15 - Comm/Public/SDK/CRT/__mpcc_defines.hxx | 83 -- Comm/Public/SDK/CRT/__mpcc_exception.hxx | 27 - Comm/Public/SDK/CRT/__mpcc_hint.hxx | 20 - Comm/Public/SDK/CRT/__mpcc_malloc.hxx | 30 - Comm/Public/SDK/CRT/__mpcc_power.inc | 35 - Comm/StdKit/AE.hpp | 143 -- Comm/StdKit/ELF.hpp | 389 ------ Comm/StdKit/ErrorID.hpp | 22 - Comm/StdKit/ErrorOr.hpp | 61 - Comm/StdKit/PEF.hpp | 133 -- Comm/StdKit/Ref.hpp | 91 -- Comm/StdKit/String.hpp | 90 -- Comm/StdKit/XCOFF.hxx | 41 - Comm/UUID.hpp | 983 -------------- Comm/Version.hxx | 3 - Examples/ExampleCDialect.S | 2 +- Headers/AsmKit/AsmKit.hpp | 217 +++ Headers/AsmKit/CPU/32x0.hpp | 95 ++ Headers/AsmKit/CPU/64x0.hpp | 108 ++ Headers/AsmKit/CPU/amd64.hpp | 57 + Headers/AsmKit/CPU/arm64.hpp | 26 + Headers/AsmKit/CPU/ppc.hpp | 1929 +++++++++++++++++++++++++++ Headers/Defines.hpp | 147 ++ Headers/Macros.hpp | 33 + Headers/ParserKit.hpp | 173 +++ Headers/Public/SDK/CRT/__mpcc_alloca.hxx | 15 + Headers/Public/SDK/CRT/__mpcc_defines.hxx | 83 ++ Headers/Public/SDK/CRT/__mpcc_exception.hxx | 27 + Headers/Public/SDK/CRT/__mpcc_hint.hxx | 20 + Headers/Public/SDK/CRT/__mpcc_malloc.hxx | 30 + Headers/Public/SDK/CRT/__mpcc_power.inc | 35 + Headers/StdKit/AE.hpp | 143 ++ Headers/StdKit/ELF.hpp | 389 ++++++ Headers/StdKit/ErrorID.hpp | 22 + Headers/StdKit/ErrorOr.hpp | 61 + Headers/StdKit/PEF.hpp | 134 ++ Headers/StdKit/Ref.hpp | 91 ++ Headers/StdKit/String.hpp | 90 ++ Headers/StdKit/XCOFF.hxx | 41 + Headers/UUID.hpp | 983 ++++++++++++++ Headers/Version.hxx | 3 + Notes/ASM specs.txt | 11 + Notes/HAVP DSP.txt | 13 + Notes/Notice.txt | 4 + Notes/RISC CPU.txt | 17 + Notes/asm-specs.txt | 11 - Notes/havp.txt | 13 - Notes/notice.txt | 4 - Notes/vnrp.txt | 17 - Sources/32asm.cc | 52 - Sources/32asm.cxx | 52 + Sources/64asm.cc | 954 ------------- Sources/64asm.cxx | 954 +++++++++++++ Sources/64x0-cc.cc | 1627 ---------------------- Sources/64x0-cc.cxx | 1627 ++++++++++++++++++++++ Sources/AssemblyFactory.cc | 59 - Sources/AssemblyFactory.cxx | 59 + Sources/Detail/asmutils.hxx | 4 +- Sources/Detail/compilerutils.hxx | 4 +- Sources/String.cc | 200 --- Sources/String.cxx | 200 +++ Sources/bpp.cc | 899 ------------- Sources/bpp.cxx | 899 +++++++++++++ Sources/coff2ae.cc | 23 - Sources/coff2ae.cxx | 23 + Sources/compile_flags.txt | 2 +- Sources/cplusplus.cc | 905 ------------- Sources/cplusplus.cxx | 905 +++++++++++++ Sources/elf2ae.cc | 22 - Sources/elf2ae.cxx | 22 + Sources/i64asm.cc | 1484 --------------------- Sources/i64asm.cxx | 1484 +++++++++++++++++++++ Sources/link.cc | 738 ---------- Sources/link.cxx | 741 ++++++++++ Sources/power-cc.cc | 1645 ----------------------- Sources/power-cc.cxx | 1645 +++++++++++++++++++++++ Sources/ppcasm.cc | 976 -------------- Sources/ppcasm.cxx | 976 ++++++++++++++ codetools.files | 78 +- codetools.includes | 2 +- posix.make | 20 +- win64.make | 20 +- 93 files changed, 14680 insertions(+), 14676 deletions(-) delete mode 100644 Comm/AsmKit/AsmKit.hpp delete mode 100644 Comm/AsmKit/CPU/32x0.hpp delete mode 100644 Comm/AsmKit/CPU/64x0.hpp delete mode 100644 Comm/AsmKit/CPU/amd64.hpp delete mode 100644 Comm/AsmKit/CPU/arm64.hpp delete mode 100644 Comm/AsmKit/CPU/ppc.hpp delete mode 100644 Comm/CompilerKit.hpp delete mode 100644 Comm/Defines.hpp delete mode 100644 Comm/ParserKit.hpp delete mode 100644 Comm/Public/SDK/CRT/__mpcc_alloca.hxx delete mode 100644 Comm/Public/SDK/CRT/__mpcc_defines.hxx delete mode 100644 Comm/Public/SDK/CRT/__mpcc_exception.hxx delete mode 100644 Comm/Public/SDK/CRT/__mpcc_hint.hxx delete mode 100644 Comm/Public/SDK/CRT/__mpcc_malloc.hxx delete mode 100644 Comm/Public/SDK/CRT/__mpcc_power.inc delete mode 100644 Comm/StdKit/AE.hpp delete mode 100644 Comm/StdKit/ELF.hpp delete mode 100644 Comm/StdKit/ErrorID.hpp delete mode 100644 Comm/StdKit/ErrorOr.hpp delete mode 100644 Comm/StdKit/PEF.hpp delete mode 100644 Comm/StdKit/Ref.hpp delete mode 100644 Comm/StdKit/String.hpp delete mode 100644 Comm/StdKit/XCOFF.hxx delete mode 100644 Comm/UUID.hpp delete mode 100644 Comm/Version.hxx create mode 100644 Headers/AsmKit/AsmKit.hpp create mode 100644 Headers/AsmKit/CPU/32x0.hpp create mode 100644 Headers/AsmKit/CPU/64x0.hpp create mode 100644 Headers/AsmKit/CPU/amd64.hpp create mode 100644 Headers/AsmKit/CPU/arm64.hpp create mode 100644 Headers/AsmKit/CPU/ppc.hpp create mode 100644 Headers/Defines.hpp create mode 100644 Headers/Macros.hpp create mode 100644 Headers/ParserKit.hpp create mode 100644 Headers/Public/SDK/CRT/__mpcc_alloca.hxx create mode 100644 Headers/Public/SDK/CRT/__mpcc_defines.hxx create mode 100644 Headers/Public/SDK/CRT/__mpcc_exception.hxx create mode 100644 Headers/Public/SDK/CRT/__mpcc_hint.hxx create mode 100644 Headers/Public/SDK/CRT/__mpcc_malloc.hxx create mode 100644 Headers/Public/SDK/CRT/__mpcc_power.inc create mode 100644 Headers/StdKit/AE.hpp create mode 100644 Headers/StdKit/ELF.hpp create mode 100644 Headers/StdKit/ErrorID.hpp create mode 100644 Headers/StdKit/ErrorOr.hpp create mode 100644 Headers/StdKit/PEF.hpp create mode 100644 Headers/StdKit/Ref.hpp create mode 100644 Headers/StdKit/String.hpp create mode 100644 Headers/StdKit/XCOFF.hxx create mode 100644 Headers/UUID.hpp create mode 100644 Headers/Version.hxx create mode 100644 Notes/ASM specs.txt create mode 100644 Notes/HAVP DSP.txt create mode 100644 Notes/Notice.txt create mode 100644 Notes/RISC CPU.txt delete mode 100644 Notes/asm-specs.txt delete mode 100644 Notes/havp.txt delete mode 100644 Notes/notice.txt delete mode 100644 Notes/vnrp.txt delete mode 100644 Sources/32asm.cc create mode 100644 Sources/32asm.cxx delete mode 100644 Sources/64asm.cc create mode 100644 Sources/64asm.cxx delete mode 100644 Sources/64x0-cc.cc create mode 100644 Sources/64x0-cc.cxx delete mode 100644 Sources/AssemblyFactory.cc create mode 100644 Sources/AssemblyFactory.cxx delete mode 100644 Sources/String.cc create mode 100644 Sources/String.cxx delete mode 100644 Sources/bpp.cc create mode 100644 Sources/bpp.cxx delete mode 100644 Sources/coff2ae.cc create mode 100644 Sources/coff2ae.cxx delete mode 100644 Sources/cplusplus.cc create mode 100644 Sources/cplusplus.cxx delete mode 100644 Sources/elf2ae.cc create mode 100644 Sources/elf2ae.cxx delete mode 100644 Sources/i64asm.cc create mode 100644 Sources/i64asm.cxx delete mode 100644 Sources/link.cc create mode 100644 Sources/link.cxx delete mode 100644 Sources/power-cc.cc create mode 100644 Sources/power-cc.cxx delete mode 100644 Sources/ppcasm.cc create mode 100644 Sources/ppcasm.cxx (limited to 'Examples/ExampleCDialect.S') diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 696e195..c520968 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,31 +1,31 @@ { - "configurations": [ - { - "name": "Macintosh (CLang)", - "includePath": [ - "${workspaceFolder}/Comm/**", - "${workspaceFolder}/Sources/Detail/**", - "${workspaceFolder}/**" - ], - "defines": [], - "compilerPath": "/usr/bin/clang", - "cStandard": "c17", - "cppStandard": "c++20", - "intelliSenseMode": "macos-clang-arm64" - }, - { - "name": "Windows (Cygwin)", - "includePath": [ - "${workspaceFolder}/Comm/**", - "${workspaceFolder}/Sources/Detail/**", - "${workspaceFolder}/**" - ], - "defines": [], - "compilerPath": "C:/cygwin64/bin/g++.exe", - "cStandard": "c17", - "cppStandard": "c++20", - "intelliSenseMode": "windows-gcc-x64" - } - ], - "version": 4 -} \ No newline at end of file + "configurations": [ + { + "name": "Macintosh (CLang)", + "includePath": [ + "${workspaceFolder}/Headers/**", + "${workspaceFolder}/Sources/Detail/**", + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/clang", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "macos-clang-arm64" + }, + { + "name": "Windows (Cygwin)", + "includePath": [ + "${workspaceFolder}/Headers/**", + "${workspaceFolder}/Sources/Detail/**", + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "C:/cygwin64/bin/g++.exe", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "windows-gcc-x64" + } + ], + "version": 4 +} diff --git a/Comm/AsmKit/AsmKit.hpp b/Comm/AsmKit/AsmKit.hpp deleted file mode 100644 index 38b418c..0000000 --- a/Comm/AsmKit/AsmKit.hpp +++ /dev/null @@ -1,217 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -#include -#include -#include - -namespace CompilerKit -{ - // - // @brief Frontend to Assembly mountpoint. - // - class AssemblyInterface - { - public: - explicit AssemblyInterface() = default; - virtual ~AssemblyInterface() = default; - - MPCC_COPY_DEFAULT(AssemblyInterface); - - //@ brief compile to object file. - // 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; - - MPCC_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; - - MPCC_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; - - MPCC_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; - - MPCC_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; - - MPCC_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; - - MPCC_COPY_DEFAULT(EncoderPowerPC); - - virtual std::string CheckLine(std::string& line, - const std::string& file) override; - virtual bool WriteLine(std::string& line, const std::string& file) override; - virtual bool WriteNumber(const std::size_t& pos, - std::string& from_what) override; - }; - -#endif // __ASM_NEED_32x0__ -} // namespace CompilerKit diff --git a/Comm/AsmKit/CPU/32x0.hpp b/Comm/AsmKit/CPU/32x0.hpp deleted file mode 100644 index 7b93a94..0000000 --- a/Comm/AsmKit/CPU/32x0.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -#include - -// @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]; - char fOpcode; - char fSize; - char fFunct3; - char fFunct7; -}; - -#define kAsmDWordStr ".dword" /* 64 bit */ -#define kAsmWordStr ".word" /* 32-bit */ -#define kAsmHWordStr ".half" /* 16-bit */ -#define kAsmByteStr ".byte" /* 8-bit */ - -inline std::vector kOpcodes32x0 = { - kAsmOpcodeDecl("nop", 0b0100011, 0b000, kAsmNoArgs) // nothing to do. - kAsmOpcodeDecl("br", 0b1110011, 0b001, kAsmJump) // jump to branch - kAsmOpcodeDecl("mr", 0b0100011, 0b101, kAsmImmediate) // move registers - kAsmOpcodeDecl("psh", 0b0111011, 0b000, kAsmImmediate) // push to sp - kAsmOpcodeDecl("pop", 0b0111011, 0b001, kAsmImmediate) // pop from sp. - kAsmOpcodeDecl("cls", 0b0111011, 0b010, - kAsmImmediate) // setup stack and call, store address to CR. - kAsmOpcodeDecl("rts", 0b0111011, 0b110, - kAsmImmediate) // pull stack and return form CR. - kAsmOpcodeDecl("int", 0b0111111, 0b000, kAsmSyscall) // raise interrupt -}; - -// \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/Comm/AsmKit/CPU/64x0.hpp b/Comm/AsmKit/CPU/64x0.hpp deleted file mode 100644 index b8bafb9..0000000 --- a/Comm/AsmKit/CPU/64x0.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -#include -#include - -// @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 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("decc", 0b0101011, 0b111, kAsmImmediate) - kAsmOpcodeDecl("int", 0b1110011, 0b00, kAsmSyscall) - kAsmOpcodeDecl("pha", 0b1110011, 0b00, kAsmNoArgs) - kAsmOpcodeDecl("pla", 0b1110011, 0b01, kAsmNoArgs)}; - -// \brief 64x0 register prefix -// example: r32, r0 -// r32 -> sp -// r0 -> hw zero - -#define kAsmFloatZeroRegister 0 -#define kAsmZeroRegister 0 - -#define kAsmRegisterPrefix "r" -#define kAsmRegisterLimit 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/Comm/AsmKit/CPU/amd64.hpp b/Comm/AsmKit/CPU/amd64.hpp deleted file mode 100644 index 06dc1da..0000000 --- a/Comm/AsmKit/CPU/amd64.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -#include - -// @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 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/Comm/AsmKit/CPU/arm64.hpp b/Comm/AsmKit/CPU/arm64.hpp deleted file mode 100644 index 8dd5204..0000000 --- a/Comm/AsmKit/CPU/arm64.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/* ------------------------------------------- - -Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -#include - -/// @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/Comm/AsmKit/CPU/ppc.hpp b/Comm/AsmKit/CPU/ppc.hpp deleted file mode 100644 index e3ea6c5..0000000 --- a/Comm/AsmKit/CPU/ppc.hpp +++ /dev/null @@ -1,1929 +0,0 @@ -/* ------------------------------------------- - - Some modifications are copyrighted under: - ZKA Technologies - - Original author: - Apple Inc - -------------------------------------------- */ - -#pragma once - -#include - -/// @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< - -#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 -#include -#include - -#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 MPCC_COPY_DELETE(KLASS) \ - KLASS& operator=(const KLASS&) = delete; \ - KLASS(const KLASS&) = delete; - -#define MPCC_COPY_DEFAULT(KLASS) \ - KLASS& operator=(const KLASS&) = default; \ - KLASS(const KLASS&) = default; - -#define MPCC_MOVE_DELETE(KLASS) \ - KLASS& operator=(KLASS&&) = delete; \ - KLASS(KLASS&&) = delete; - -#define MPCC_MOVE_DEFAULT(KLASS) \ - KLASS& operator=(KLASS&&) = default; \ - KLASS(KLASS&&) = default; - -#include -#include -#include -#include - -namespace CompilerKit -{ - inline constexpr int BASE_YEAR = 1900; - - inline std::string current_date() noexcept - { - auto time_data = time(nullptr); - auto time_struct = gmtime(&time_data); - - std::string fmt = std::to_string(BASE_YEAR + 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; - } -} // namespace CompilerKit - -#define PACKED __attribute__((packed)) - -typedef char char_type; - -#define kObjectFileExt ".obj" -#define kBinaryFileExt ".bin" - -#define kAsmFileExts \ - { \ - ".64x", ".32x", ".masm", ".s", ".S", ".asm" \ - } - -#ifdef __NDK_MODULE__ -#define NDK_MODULE(name) int name(int argc, char** argv) -#else -#define NDK_MODULE(name) int main(int argc, char** argv) -#endif /* ifdef __NDK_MODULE__ */ - -#pragma scalar_storage_order big-endian - -#endif /* ifndef __MPCC_DEFINES_HPP__ */ diff --git a/Comm/ParserKit.hpp b/Comm/ParserKit.hpp deleted file mode 100644 index 4c3f483..0000000 --- a/Comm/ParserKit.hpp +++ /dev/null @@ -1,173 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -#include -#include - -namespace ParserKit -{ - using namespace CompilerKit; - - /// @brief Compiler backend, implements a frontend, such as C, C++... - /// See Toolchain, for some examples. - class CompilerBackend - { - public: - explicit CompilerBackend() = default; - virtual ~CompilerBackend() = default; - - MPCC_COPY_DEFAULT(CompilerBackend); - - // 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 char* file) = 0; - - //! @brief What language are we dealing with? - virtual const char* Language() - { - return "Invalid Language"; - } - }; - - 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 __PK_USE_STRUCT_INSTEAD__ - CompilerKeyword fUserData; -#else - std::string fUserData; -#endif - - std::string fUserValue; - struct SyntaxLeaf* fNext; - }; - - std::vector fLeafList; - SizeType fNumLeafs; - - size_t SizeOf() - { - return fNumLeafs; - } - std::vector& 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 ParserKit diff --git a/Comm/Public/SDK/CRT/__mpcc_alloca.hxx b/Comm/Public/SDK/CRT/__mpcc_alloca.hxx deleted file mode 100644 index 02b3123..0000000 --- a/Comm/Public/SDK/CRT/__mpcc_alloca.hxx +++ /dev/null @@ -1,15 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -typedef void* ptr_type; -typedef __SIZE_TYPE__ size_type; - -inline void* __mpcc_alloca_gcc(size_type sz) -{ - return __builtin_alloca(sz); -} diff --git a/Comm/Public/SDK/CRT/__mpcc_defines.hxx b/Comm/Public/SDK/CRT/__mpcc_defines.hxx deleted file mode 100644 index 19ed8a4..0000000 --- a/Comm/Public/SDK/CRT/__mpcc_defines.hxx +++ /dev/null @@ -1,83 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#ifndef __MPCC_DEF__ -#define __MPCC_DEF__ - -#ifndef __GNUC__ - -typedef __SIZE_TYPE__ size_t; - -#ifdef __LP64__ -typedef long int ssize_t; -#else -typedef int ssize_t; -#endif // __LP64__ - -typedef size_t ptrdiff_t; -typedef size_t uintptr_t; -typedef void* voidptr_t; -typedef void* any_t; -typedef char* caddr_t; - -#ifndef NULL -#define NULL ((voidptr_t)0) -#endif // !null - -#ifdef __GNUC__ -#include -#define __mpcc_alloca(sz) __mpcc_alloca_gcc(sz) -#define __packed__ __attribute__((packed)) -#elif defined(__MPCC__) -#define __packed__ __mpcc_packed__ -#define __alloca(sz) __mpcc_alloca(sz) -#endif - -#define __deref(ptr) (*(ptr)) - -#ifdef __cplusplus -#define __init_decl() \ - extern "C" \ - { - - -#define __fini_decl() \ - }; \ - - -#else -#define __init_decl() -#define __fini_decl() -#endif - -typedef long long off_t; -typedef unsigned long long uoff_t; - -typedef union float_cast { - struct - { - unsigned int mantissa : 23; - unsigned int exponent : 8; - unsigned int sign : 1; - }; - - float f; -} __packed__ float_cast_t; - -typedef union double_cast { - struct - { - unsigned long long int mantissa : 52; - unsigned int exponent : 11; - unsigned int sign : 1; - }; - - double f; -} __packed__ double_cast_t; - -#endif // ifndef __GNUC__ - -#endif /* __MPCC_DEF__ */ diff --git a/Comm/Public/SDK/CRT/__mpcc_exception.hxx b/Comm/Public/SDK/CRT/__mpcc_exception.hxx deleted file mode 100644 index 4a9f84a..0000000 --- a/Comm/Public/SDK/CRT/__mpcc_exception.hxx +++ /dev/null @@ -1,27 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -/// This file is an implementation of __throw* family of functions. - -#include -#include -#include - -namespace std -{ - inline void __throw_general(void) - { - throw std::runtime_error("MPCC C++ Runtime error."); - } - - inline void __throw_domain_error(const char* error) - { - std::cout << "MPCC C++: Domain error: " << error << "\r"; - __throw_general(); - } -} // namespace std diff --git a/Comm/Public/SDK/CRT/__mpcc_hint.hxx b/Comm/Public/SDK/CRT/__mpcc_hint.hxx deleted file mode 100644 index 02dbc94..0000000 --- a/Comm/Public/SDK/CRT/__mpcc_hint.hxx +++ /dev/null @@ -1,20 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -#pragma compiler(hint_manifest) - -#define _Input -#define _Output - -#define _Optional - -#define _StrictCheckInput -#define _StrictCheckOutput - -#define _InOut -#define _StrictInOut diff --git a/Comm/Public/SDK/CRT/__mpcc_malloc.hxx b/Comm/Public/SDK/CRT/__mpcc_malloc.hxx deleted file mode 100644 index eeaa67b..0000000 --- a/Comm/Public/SDK/CRT/__mpcc_malloc.hxx +++ /dev/null @@ -1,30 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -#include - -namespace stdx -{ - /// @brief allocate a new class. - /// @tparam KindClass the class type to allocate. - template - inline void* allocate(Args&&... args) - { - return new KindClass(std::forward(args)...); - } - - /// @brief free a class. - /// @tparam KindClass the class type to allocate. - template - inline void release(KindClass ptr) - { - if (!ptr) - return; - delete ptr; - } -} // namespace stdx diff --git a/Comm/Public/SDK/CRT/__mpcc_power.inc b/Comm/Public/SDK/CRT/__mpcc_power.inc deleted file mode 100644 index 9e4928c..0000000 --- a/Comm/Public/SDK/CRT/__mpcc_power.inc +++ /dev/null @@ -1,35 +0,0 @@ -# Path: SDK/__mpcc_power.inc -# Language: MPCC POWER Assembly support for GNU. -# Build Date: 2024-6-4 - -%ifdef __CODETOOLS__ - -%def lda li -%def sta stw -%def ldw li - -%def r0 0 -%def r1 1 -%def r2 2 -%def r3 3 -%def r4 4 -%def r5 5 -%def r6 6 -%def r7 7 -%def r8 8 -%def r9 9 -%def r10 10 -%def r11 11 -%def r12 12 -%def r13 13 -%def r14 14 -%def r15 15 -%def r16 16 -%def r17 17 -%def r18 18 -%def r19 19 -%def r20 20 - -%endif - -%def nop mr 0, 0 diff --git a/Comm/StdKit/AE.hpp b/Comm/StdKit/AE.hpp deleted file mode 100644 index ee39e56..0000000 --- a/Comm/StdKit/AE.hpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * ======================================================== - * - * MPCC - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -#pragma once - -#include - -#define kAEMag0 'A' -#define kAEMag1 'E' - -#define kAESymbolLen 64 -#define kAEPad 8 -#define kAEMagLen 2 -#define kAEInvalidOpcode 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 CompilerKit -{ - // @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 CompilerKit - -// provide operator<< for AE - -std::ofstream& operator<<(std::ofstream& fp, CompilerKit::AEHeader& container) -{ - fp.write((char*)&container, sizeof(CompilerKit::AEHeader)); - - return fp; -} - -std::ofstream& operator<<(std::ofstream& fp, - CompilerKit::AERecordHeader& container) -{ - fp.write((char*)&container, sizeof(CompilerKit::AERecordHeader)); - - return fp; -} - -std::ifstream& operator>>(std::ifstream& fp, CompilerKit::AEHeader& container) -{ - fp.read((char*)&container, sizeof(CompilerKit::AEHeader)); - return fp; -} - -std::ifstream& operator>>(std::ifstream& fp, - CompilerKit::AERecordHeader& container) -{ - fp.read((char*)&container, sizeof(CompilerKit::AERecordHeader)); - return fp; -} - -namespace CompilerKit::Utils -{ - /** - * @brief AE Reader protocol - * - */ - class AEReadableProtocol final - { - public: - std::ifstream FP; - - public: - explicit AEReadableProtocol() = default; - ~AEReadableProtocol() = default; - - MPCC_COPY_DELETE(AEReadableProtocol); - - /** - * @brief Read AE record - * - * @param raw the containing buffer - * @param sz it's size (without sizeof(AERecordHeader) added to it.) - * @return AERecordHeaderPtr - */ - AERecordHeaderPtr Read(char* raw, std::size_t sz) - { - if (!raw) - return nullptr; - - return this->_Read(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 - TypeClass* _Read(char* raw, std::size_t sz) - { - FP.read(raw, std::streamsize(sz)); - return reinterpret_cast(raw); - } - }; -} // namespace CompilerKit::Utils diff --git a/Comm/StdKit/ELF.hpp b/Comm/StdKit/ELF.hpp deleted file mode 100644 index 4f0d0ae..0000000 --- a/Comm/StdKit/ELF.hpp +++ /dev/null @@ -1,389 +0,0 @@ -#pragma once - -#include -#include - -struct file; - -#ifndef elf_read_implies_exec -/* Executables for which elf_read_implies_exec() returns TRUE will - have the READ_IMPLIES_EXEC personality flag set automatically. - Override in asm/elf.h as needed. */ -#define elf_read_implies_exec(ex, have_pt_gnu_stack) 0 -#endif - -/* 32-bit ELF base types. */ -typedef uint32_t Elf32_Addr; -typedef uint16_t Elf32_Half; -typedef uint32_t Elf32_Off; -typedef int32_t Elf32_Sword; -typedef uint32_t Elf32_Word; - -/* 64-bit ELF base types. */ -typedef uintptr_t Elf64_Addr; -typedef uint16_t Elf64_Half; -typedef int16_t Elf64_SHalf; -typedef uint64_t Elf64_Off; -typedef int32_t Elf64_Sword; -typedef uint32_t Elf64_Word; -typedef uint64_t Elf64_Xword; -typedef int64_t Elf64_Sxword; - -/* These constants are for the segment types stored in the image headers */ -#define PT_NULL 0 -#define PT_LOAD 1 -#define PT_DYNAMIC 2 -#define PT_INTERP 3 -#define PT_NOTE 4 -#define PT_SHLIB 5 -#define PT_PHDR 6 -#define PT_TLS 7 /* Thread local storage segment */ -#define PT_LOOS 0x60000000 /* OS-specific */ -#define PT_HIOS 0x6fffffff /* OS-specific */ -#define PT_LOPROC 0x70000000 -#define PT_HIPROC 0x7fffffff -#define PT_GNU_EH_FRAME 0x6474e550 - -#define PT_GNU_STACK (PT_LOOS + 0x474e551) - -/* These constants define the different elf file types */ -#define ET_NONE 0 -#define ET_REL 1 -#define ET_EXEC 2 -#define ET_DYN 3 -#define ET_CORE 4 -#define ET_LOPROC 0xff00 -#define ET_HIPROC 0xffff - -/* This is the info that is needed to parse the dynamic section of the file */ -#define DT_NULL 0 -#define DT_NEEDED 1 -#define DT_PLTRELSZ 2 -#define DT_PLTGOT 3 -#define DT_HASH 4 -#define DT_STRTAB 5 -#define DT_SYMTAB 6 -#define DT_RELA 7 -#define DT_RELASZ 8 -#define DT_RELAENT 9 -#define DT_STRSZ 10 -#define DT_SYMENT 11 -#define DT_INIT 12 -#define DT_FINI 13 -#define DT_SONAME 14 -#define DT_RPATH 15 -#define DT_SYMBOLIC 16 -#define DT_REL 17 -#define DT_RELSZ 18 -#define DT_RELENT 19 -#define DT_PLTREL 20 -#define DT_DEBUG 21 -#define DT_TEXTREL 22 -#define DT_JMPREL 23 -#define DT_ENCODING 32 -#define OLD_DT_LOOS 0x60000000 -#define DT_LOOS 0x6000000d -#define DT_HIOS 0x6ffff000 -#define DT_VALRNGLO 0x6ffffd00 -#define DT_VALRNGHI 0x6ffffdff -#define DT_ADDRRNGLO 0x6ffffe00 -#define DT_ADDRRNGHI 0x6ffffeff -#define DT_VERSYM 0x6ffffff0 -#define DT_RELACOUNT 0x6ffffff9 -#define DT_RELCOUNT 0x6ffffffa -#define DT_FLAGS_1 0x6ffffffb -#define DT_VERDEF 0x6ffffffc -#define DT_VERDEFNUM 0x6ffffffd -#define DT_VERNEED 0x6ffffffe -#define DT_VERNEEDNUM 0x6fffffff -#define OLD_DT_HIOS 0x6fffffff -#define DT_LOPROC 0x70000000 -#define DT_HIPROC 0x7fffffff - -/* This info is needed when parsing the symbol table */ -#define STB_LOCAL 0 -#define STB_GLOBAL 1 -#define STB_WEAK 2 - -#define STT_NOTYPE 0 -#define STT_OBJECT 1 -#define STT_FUNC 2 -#define STT_SECTION 3 -#define STT_FILE 4 -#define STT_COMMON 5 -#define STT_TLS 6 - -#define ELF_ST_BIND(x) ((x) >> 4) -#define ELF_ST_TYPE(x) (((unsigned int)x) & 0xf) -#define ELF32_ST_BIND(x) ELF_ST_BIND(x) -#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) -#define ELF64_ST_BIND(x) ELF_ST_BIND(x) -#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) - -typedef struct dynamic -{ - Elf32_Sword d_tag; - union { - Elf32_Sword d_val; - Elf32_Addr d_ptr; - } d_un; -} Elf32_Dyn; - -typedef struct -{ - Elf64_Sxword d_tag; /* entry tag value */ - union { - Elf64_Xword d_val; - Elf64_Addr d_ptr; - } d_un; -} Elf64_Dyn; - -/* The following are used with relocations */ -#define ELF32_R_SYM(x) ((x) >> 8) -#define ELF32_R_TYPE(x) ((x)&0xff) - -#define ELF64_R_SYM(i) ((i) >> 32) -#define ELF64_R_TYPE(i) ((i)&0xffffffff) - -typedef struct elf32_rel -{ - Elf32_Addr r_offset; - Elf32_Word r_info; -} Elf32_Rel; - -typedef struct elf64_rel -{ - Elf64_Addr r_offset; /* Location at which to apply the action */ - Elf64_Xword r_info; /* index and type of relocation */ -} Elf64_Rel; - -typedef struct elf32_rela -{ - Elf32_Addr r_offset; - Elf32_Word r_info; - Elf32_Sword r_addend; -} Elf32_Rela; - -typedef struct elf64_rela -{ - Elf64_Addr r_offset; /* Location at which to apply the action */ - Elf64_Xword r_info; /* index and type of relocation */ - Elf64_Sxword r_addend; /* Constant addend used to compute value */ -} Elf64_Rela; - -typedef struct elf32_sym -{ - Elf32_Word st_name; - Elf32_Addr st_value; - Elf32_Word st_size; - unsigned char st_info; - unsigned char st_other; - Elf32_Half st_shndx; -} Elf32_Sym; - -typedef struct elf64_sym -{ - Elf64_Word st_name; /* Symbol name, index in string tbl */ - unsigned char st_info; /* Type and binding attributes */ - unsigned char st_other; /* No defined meaning, 0 */ - Elf64_Half st_shndx; /* Associated section index */ - Elf64_Addr st_value; /* Value of the symbol */ - Elf64_Xword st_size; /* Associated symbol size */ -} Elf64_Sym; - -#define EI_NIDENT 16 - -typedef struct elf32_hdr -{ - unsigned char e_ident[EI_NIDENT]; - Elf32_Half e_type; - Elf32_Half e_machine; - Elf32_Word e_version; - Elf32_Addr e_entry; /* Entry point */ - Elf32_Off e_phoff; - Elf32_Off e_shoff; - Elf32_Word e_flags; - Elf32_Half e_ehsize; - Elf32_Half e_phentsize; - Elf32_Half e_phnum; - Elf32_Half e_shentsize; - Elf32_Half e_shnum; - Elf32_Half e_shstrndx; -} Elf32_Ehdr; - -typedef struct elf64_hdr -{ - unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */ - Elf64_Half e_type; - Elf64_Half e_machine; - Elf64_Word e_version; - Elf64_Addr e_entry; /* Entry point virtual address */ - Elf64_Off e_phoff; /* Program header table file offset */ - Elf64_Off e_shoff; /* Section header table file offset */ - Elf64_Word e_flags; - Elf64_Half e_ehsize; - Elf64_Half e_phentsize; - Elf64_Half e_phnum; - Elf64_Half e_shentsize; - Elf64_Half e_shnum; - Elf64_Half e_shstrndx; -} Elf64_Ehdr; - -/* These constants define the permissions on sections in the program - header, p_flags. */ -#define PF_R 0x4 -#define PF_W 0x2 -#define PF_X 0x1 - -typedef struct elf32_phdr -{ - Elf32_Word p_type; - Elf32_Off p_offset; - Elf32_Addr p_vaddr; - Elf32_Addr p_paddr; - Elf32_Word p_filesz; - Elf32_Word p_memsz; - Elf32_Word p_flags; - Elf32_Word p_align; -} Elf32_Phdr; - -typedef struct elf64_phdr -{ - Elf64_Word p_type; - Elf64_Word p_flags; - Elf64_Off p_offset; /* Segment file offset */ - Elf64_Addr p_vaddr; /* Segment virtual address */ - Elf64_Addr p_paddr; /* Segment physical address */ - Elf64_Xword p_filesz; /* Segment size in file */ - Elf64_Xword p_memsz; /* Segment size in memory */ - Elf64_Xword p_align; /* Segment alignment, file & memory */ -} Elf64_Phdr; - -/* sh_type */ -#define SHT_NULL 0 -#define SHT_PROGBITS 1 -#define SHT_SYMTAB 2 -#define SHT_STRTAB 3 -#define SHT_RELA 4 -#define SHT_HASH 5 -#define SHT_DYNAMIC 6 -#define SHT_NOTE 7 -#define SHT_NOBITS 8 -#define SHT_REL 9 -#define SHT_SHLIB 10 -#define SHT_DYNSYM 11 -#define SHT_NUM 12 -#define SHT_LOPROC 0x70000000 -#define SHT_HIPROC 0x7fffffff -#define SHT_LOUSER 0x80000000 -#define SHT_HIUSER 0xffffffff - -/* sh_flags */ -#define SHF_WRITE 0x1 -#define SHF_ALLOC 0x2 -#define SHF_EXECINSTR 0x4 -#define SHF_MASKPROC 0xf0000000 - -/* special section indexes */ -#define SHN_UNDEF 0 -#define SHN_LORESERVE 0xff00 -#define SHN_LOPROC 0xff00 -#define SHN_HIPROC 0xff1f -#define SHN_ABS 0xfff1 -#define SHN_COMMON 0xfff2 -#define SHN_HIRESERVE 0xffff - -typedef struct -{ - Elf32_Word sh_name; - Elf32_Word sh_type; - Elf32_Word sh_flags; - Elf32_Addr sh_addr; - Elf32_Off sh_offset; - Elf32_Word sh_size; - Elf32_Word sh_link; - Elf32_Word sh_info; - Elf32_Word sh_addralign; - Elf32_Word sh_entsize; -} Elf32_Shdr; - -typedef struct elf64_shdr -{ - Elf64_Word sh_name; /* Section name, index in string tbl */ - Elf64_Word sh_type; /* Type of section */ - Elf64_Xword sh_flags; /* Miscellaneous section attributes */ - Elf64_Addr sh_addr; /* Section virtual addr at execution */ - Elf64_Off sh_offset; /* Section file offset */ - Elf64_Xword sh_size; /* Size of section in bytes */ - Elf64_Word sh_link; /* Index of another section */ - Elf64_Word sh_info; /* Additional section information */ - Elf64_Xword sh_addralign; /* Section alignment */ - Elf64_Xword sh_entsize; /* Entry size if section holds table */ -} Elf64_Shdr; - -#define EI_MAG0 0 /* e_ident[] indexes */ -#define EI_MAG1 1 -#define EI_MAG2 2 -#define EI_MAG3 3 -#define EI_CLASS 4 -#define EI_DATA 5 -#define EI_VERSION 6 -#define EI_OSABI 7 -#define EI_PAD 8 - -#define ELFMAG0 0x7f /* EI_MAG */ -#define ELFMAG1 'E' -#define ELFMAG2 'L' -#define ELFMAG3 'F' -#define ELFMAG "\177ELF" -#define SELFMAG 4 - -#define ELFCLASSNONE 0 /* EI_CLASS */ -#define ELFCLASS32 1 -#define ELFCLASS64 2 -#define ELFCLASSNUM 3 - -#define ELFDATANONE 0 /* e_ident[EI_DATA] */ -#define ELFDATA2LSB 1 -#define ELFDATA2MSB 2 - -#define EV_NONE 0 /* e_version, EI_VERSION */ -#define EV_CURRENT 1 -#define EV_NUM 2 - -#define ELFOSABI_NONE 0 -#define ELFOSABI_LINUX 3 - -#ifndef ELF_OSABI -#define ELF_OSABI ELFOSABI_NONE -#endif - -/* Notes used in ET_CORE */ -#define NT_PRSTATUS 1 -#define NT_PRFPREG 2 -#define NT_PRPSINFO 3 -#define NT_TASKSTRUCT 4 -#define NT_AUXV 6 -#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ -#define NT_PPC_VMX 0x100 /* POWER Altivec/VMX registers */ -#define NT_PPC_SPE 0x101 /* POWER SPE/EVR registers */ -#define NT_PPC_VSX 0x102 /* POWER VSX registers */ -#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ -#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ -#define NT_PRXSTATUS 0x300 /* s390 upper register halves */ - -/* Note header in a PT_NOTE section */ -typedef struct elf32_note -{ - Elf32_Word n_namesz; /* Name size */ - Elf32_Word n_descsz; /* Content size */ - Elf32_Word n_type; /* Content type */ -} Elf32_Nhdr; - -/* Note header in a PT_NOTE section */ -typedef struct elf64_note -{ - Elf64_Word n_namesz; /* Name size */ - Elf64_Word n_descsz; /* Content size */ - Elf64_Word n_type; /* Content type */ -} Elf64_Nhdr; diff --git a/Comm/StdKit/ErrorID.hpp b/Comm/StdKit/ErrorID.hpp deleted file mode 100644 index abaac85..0000000 --- a/Comm/StdKit/ErrorID.hpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * ======================================================== - * - * CompilerKit - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -#pragma once - -#include -#include - -#define MPCC_EXEC_ERROR -30 -#define MPCC_FILE_NOT_FOUND -31 -#define MPCC_DIR_NOT_FOUND -32 -#define MPCC_FILE_EXISTS -33 -#define MPCC_TOO_LONG -34 -#define MPCC_INVALID_DATA -35 -#define MPCC_UNIMPLEMENTED -36 -#define MPCC_FAT_ERROR -37 diff --git a/Comm/StdKit/ErrorOr.hpp b/Comm/StdKit/ErrorOr.hpp deleted file mode 100644 index 4097cc9..0000000 --- a/Comm/StdKit/ErrorOr.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * ======================================================== - * - * CompilerKit - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -#pragma once - -#include -#include - -namespace CompilerKit -{ - using ErrorT = UInt32; - - template - 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 Leak() - { - return mRef; - } - - operator bool() - { - return mRef; - } - - private: - Ref mRef; - Int32 mId{0}; - }; - - using ErrorOrAny = ErrorOr; - -} // namespace CompilerKit diff --git a/Comm/StdKit/PEF.hpp b/Comm/StdKit/PEF.hpp deleted file mode 100644 index 85044b2..0000000 --- a/Comm/StdKit/PEF.hpp +++ /dev/null @@ -1,133 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#pragma once - -#include - -// @file PEF.hpp -// @brief Preferred Executable Format - -#define kPefMagic "Joy!" -#define kPefMagicFat "yoJ!" - -#define kPefExt ".exec" -#define kPefDylibExt ".lib" -#define kPefLibExt ".slib" -#define kPefObjectExt ".obj" -#define kPefDebugExt ".dbg" - -#define kPefMagicLen 5 - -#define kPefVersion 2 -#define kPefNameLen 255 - -#define kPefBaseOrigin (0x1000000) - -#define kPefStart "__ImageStart" - -namespace CompilerKit -{ - enum - { - kPefArchIntel86S = 100, - kPefArchAMD64, - kPefArchRISCV, - kPefArch64000, /* 64x0 RISC architecture. */ - kPefArch32000, - kPefArchPowerPC, /* 64-bit POWER architecture. */ - kPefArchARM64, - kPefArchCount = (kPefArchPowerPC - kPefArchIntel86S) + 1, - kPefArchInvalid = 0xFF, - }; - - enum - { - kPefSubArchAMD, - kPefSubArchIntel, - kPefSubArchGeneric, - kPefSubArchIBM, - }; - - enum - { - kPefKindExec = 1, /* .exe */ - kPefKindSharedObject = 2, /* .lib */ - 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 CompilerKit - -inline std::ofstream& operator<<(std::ofstream& fp, - CompilerKit::PEFContainer& container) -{ - fp.write((char*)&container, sizeof(CompilerKit::PEFContainer)); - return fp; -} - -inline std::ofstream& operator<<(std::ofstream& fp, - CompilerKit::PEFCommandHeader& container) -{ - fp.write((char*)&container, sizeof(CompilerKit::PEFCommandHeader)); - return fp; -} - -std::ifstream& operator>>(std::ifstream& fp, - CompilerKit::PEFContainer& container) -{ - fp.read((char*)&container, sizeof(CompilerKit::PEFContainer)); - return fp; -} - -std::ifstream& operator>>(std::ifstream& fp, - CompilerKit::PEFCommandHeader& container) -{ - fp.read((char*)&container, sizeof(CompilerKit::PEFCommandHeader)); - return fp; -} diff --git a/Comm/StdKit/Ref.hpp b/Comm/StdKit/Ref.hpp deleted file mode 100644 index e655ccb..0000000 --- a/Comm/StdKit/Ref.hpp +++ /dev/null @@ -1,91 +0,0 @@ - -/* - * ======================================================== - * - * CompilerKit - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -#pragma once - -namespace CompilerKit -{ - // @author ZKA Technologies - // @brief Reference class, refers to a pointer of data in static memory. - template - class Ref final - { - public: - explicit Ref() = default; - ~Ref() = default; - - 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; - bool m_Strong{false}; - }; - - template - class NonNullRef final - { - public: - NonNullRef() = delete; - NonNullRef(nullPtr) = delete; - - explicit NonNullRef(T* ref) - : m_Ref(ref, true) - { - } - - Ref& operator->() - { - MUST_PASS(m_Ref); - return m_Ref; - } - - NonNullRef& operator=(const NonNullRef& ref) = delete; - NonNullRef(const NonNullRef& ref) = default; - - private: - Ref m_Ref{nullptr}; - }; -} // namespace CompilerKit diff --git a/Comm/StdKit/String.hpp b/Comm/StdKit/String.hpp deleted file mode 100644 index 7e62bc5..0000000 --- a/Comm/StdKit/String.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * ======================================================== - * - * CompilerKit - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -#pragma once - -#include -#include - -namespace CompilerKit -{ - /** - * @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 char[Sz]; - assert(m_Data); - } - - ~StringView() noexcept - { - if (m_Data) - { - memset(m_Data, 0, m_Sz); - delete[] m_Data; - - m_Data = nullptr; - } - } - - MPCC_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: - char* 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 CompilerKit diff --git a/Comm/StdKit/XCOFF.hxx b/Comm/StdKit/XCOFF.hxx deleted file mode 100644 index 4b25484..0000000 --- a/Comm/StdKit/XCOFF.hxx +++ /dev/null @@ -1,41 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - - File: XCOFF.hpp - Purpose: XCOFF for NewOS. - - Revision History: - - 04/07/24: Added file (amlel) - -------------------------------------------- */ - -#ifndef __XCOFF__ -#define __XCOFF__ - -#include - -#define kXCOFF64Magic 0x01F7 - -#define kXCOFFRelFlg 0x0001 -#define kXCOFFExecutable 0x0002 -#define kXCOFFLnno 0x0004 -#define kXCOFFLSyms 0x0008 - -namespace CompilerKit -{ - /// @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 CompilerKit - -#endif // ifndef __XCOFF__ diff --git a/Comm/UUID.hpp b/Comm/UUID.hpp deleted file mode 100644 index 00b153b..0000000 --- a/Comm/UUID.hpp +++ /dev/null @@ -1,983 +0,0 @@ -#ifndef STDUUID_H -#define STDUUID_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus - -#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) -#define LIBUUID_CPP20_OR_GREATER -#endif - -#endif - -#ifdef LIBUUID_CPP20_OR_GREATER -#include -#else -#include -#endif - -#ifdef _WIN32 - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#ifdef UUID_SYSTEM_GENERATOR -#include -#endif - -#ifdef UUID_TIME_GENERATOR -#include -#pragma comment(lib, "IPHLPAPI.lib") -#endif - -#elif defined(__linux__) || defined(__unix__) - -#ifdef UUID_SYSTEM_GENERATOR -#include -#endif - -#elif defined(__APPLE__) - -#ifdef UUID_SYSTEM_GENERATOR -#include -#endif - -#endif - -namespace uuids -{ -#ifdef __cpp_lib_span - template - using span = std::span; -#else - template - using span = gsl::span; -#endif - - namespace detail - { - template - [[nodiscard]] constexpr inline unsigned char hex2char(TChar const ch) noexcept - { - if (ch >= static_cast('0') && ch <= static_cast('9')) - return static_cast(ch - static_cast('0')); - if (ch >= static_cast('a') && ch <= static_cast('f')) - return static_cast(10 + ch - static_cast('a')); - if (ch >= static_cast('A') && ch <= static_cast('F')) - return static_cast(10 + ch - static_cast('A')); - return 0; - } - - template - [[nodiscard]] constexpr inline bool is_hex(TChar const ch) noexcept - { - return (ch >= static_cast('0') && ch <= static_cast('9')) || - (ch >= static_cast('a') && ch <= static_cast('f')) || - (ch >= static_cast('A') && ch <= static_cast('F')); - } - - template - [[nodiscard]] constexpr std::basic_string_view to_string_view( - TChar const* str) noexcept - { - if (str) - return str; - return {}; - } - - template - [[nodiscard]] constexpr std::basic_string_view - 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(start); - const uint8_t* finish = static_cast(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(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((bitCount >> 24) & 0xFF)); - process_byte(static_cast((bitCount >> 16) & 0xFF)); - process_byte(static_cast((bitCount >> 8) & 0xFF)); - process_byte(static_cast((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(d32[0] >> 24); - digest[di++] = static_cast(d32[0] >> 16); - digest[di++] = static_cast(d32[0] >> 8); - digest[di++] = static_cast(d32[0] >> 0); - - digest[di++] = static_cast(d32[1] >> 24); - digest[di++] = static_cast(d32[1] >> 16); - digest[di++] = static_cast(d32[1] >> 8); - digest[di++] = static_cast(d32[1] >> 0); - - digest[di++] = static_cast(d32[2] >> 24); - digest[di++] = static_cast(d32[2] >> 16); - digest[di++] = static_cast(d32[2] >> 8); - digest[di++] = static_cast(d32[2] >> 0); - - digest[di++] = static_cast(d32[3] >> 24); - digest[di++] = static_cast(d32[3] >> 16); - digest[di++] = static_cast(d32[3] >> 8); - digest[di++] = static_cast(d32[3] >> 0); - - digest[di++] = static_cast(d32[4] >> 24); - digest[di++] = static_cast(d32[4] >> 16); - digest[di++] = static_cast(d32[4] >> 8); - digest[di++] = static_cast(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(m_block[i * 4 + 0] << 24); - w[i] |= static_cast(m_block[i * 4 + 1] << 16); - w[i] |= static_cast(m_block[i * 4 + 2] << 8); - w[i] |= static_cast(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 - inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000"; - - template <> - inline constexpr wchar_t empty_guid[37] = - L"00000000-0000-0000-0000-000000000000"; - - template - inline constexpr CharT guid_encoder[17] = "0123456789abcdef"; - - template <> - inline constexpr wchar_t guid_encoder[17] = L"0123456789abcdef"; - } // namespace detail - - // -------------------------------------------------------------------------------------------------------------------------- - // 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 Allocator = std::allocator> - std::basic_string 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 const& arr) noexcept - : data{arr} - { - } - - explicit uuid(span bytes) - { - std::copy(std::cbegin(bytes), std::cend(bytes), std::begin(data)); - } - - template - 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 as_bytes() const - { - return span( - reinterpret_cast(data.data()), 16); - } - - template - [[nodiscard]] constexpr static bool is_valid_uuid( - StringType const& in_str) noexcept - { - auto str = detail::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 || !detail::is_hex(str[i])) - { - return false; - } - - if (firstDigit) - { - firstDigit = false; - } - else - { - index++; - firstDigit = true; - } - } - - if (index < 16) - { - return false; - } - - return true; - } - - template - [[nodiscard]] constexpr static std::optional from_string( - StringType const& in_str) noexcept - { - auto str = detail::to_string_view(in_str); - bool firstDigit = true; - size_t hasBraces = 0; - size_t index = 0; - - std::array 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 || !detail::is_hex(str[i])) - { - return {}; - } - - if (firstDigit) - { - data[index] = static_cast(detail::hex2char(str[i]) << 4); - firstDigit = false; - } - else - { - data[index] = - static_cast(data[index] | detail::hex2char(str[i])); - index++; - firstDigit = true; - } - } - - if (index < 16) - { - return {}; - } - - return uuid{data}; - } - - private: - std::array data{{0}}; - - friend bool operator==(uuid const& lhs, uuid const& rhs) noexcept; - friend bool operator<(uuid const& lhs, uuid const& rhs) noexcept; - - template - friend std::basic_ostream& operator<<( - std::basic_ostream& s, uuid const& id); - - template - friend std::basic_string to_string(uuid const& id); - - friend std::hash; - }; - - // -------------------------------------------------------------------------------------------------------------------------- - // 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 - [[nodiscard]] inline std::basic_string to_string( - uuid const& id) - { - std::basic_string uustr{detail::empty_guid}; - - for (size_t i = 0, index = 0; i < 36; ++i) - { - if (i == 8 || i == 13 || i == 18 || i == 23) - { - continue; - } - uustr[i] = detail::guid_encoder[id.data[index] >> 4 & 0x0f]; - uustr[++i] = detail::guid_encoder[id.data[index] & 0x0f]; - index++; - } - - return uustr; - } - - template - std::basic_ostream& operator<<( - std::basic_ostream& 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 bytes = { - {static_cast((newId.Data1 >> 24) & 0xFF), - static_cast((newId.Data1 >> 16) & 0xFF), - static_cast((newId.Data1 >> 8) & 0xFF), - static_cast((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 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 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 - 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(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 distribution; - std::shared_ptr generator; - }; - - using uuid_random_generator = basic_uuid_random_generator; - - class uuid_name_generator - { - public: - explicit uuid_name_generator(uuid const& namespace_uuid) noexcept - : nsuuid(namespace_uuid) - { - } - - template - [[nodiscard]] uuid operator()(StringType const& name) - { - reset(); - process_characters(detail::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 - void process_characters(std::basic_string_view const str) - { - for (uint32_t c : str) - { - hasher.process_byte(static_cast(c & 0xFF)); - if constexpr (!std::is_same_v) - { - hasher.process_byte(static_cast((c >> 8) & 0xFF)); - hasher.process_byte(static_cast((c >> 16) & 0xFF)); - hasher.process_byte(static_cast((c >> 24) & 0xFF)); - } - } - } - - [[nodiscard]] uuid make_uuid() - { - detail::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; - detail::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; - - std::optional 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 buf(len); - auto pips = reinterpret_cast(&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(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 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 data; - - auto tm = get_time_intervals(); - - auto clock_seq = get_clock_sequence(); - - auto ptm = reinterpret_cast(&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 - { - 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 hasher; - return static_cast(hasher(uuids::to_string(uuid))); -#else - uint64_t l = static_cast(uuid.data[0]) << 56 | - static_cast(uuid.data[1]) << 48 | - static_cast(uuid.data[2]) << 40 | - static_cast(uuid.data[3]) << 32 | - static_cast(uuid.data[4]) << 24 | - static_cast(uuid.data[5]) << 16 | - static_cast(uuid.data[6]) << 8 | - static_cast(uuid.data[7]); - uint64_t h = static_cast(uuid.data[8]) << 56 | - static_cast(uuid.data[9]) << 48 | - static_cast(uuid.data[10]) << 40 | - static_cast(uuid.data[11]) << 32 | - static_cast(uuid.data[12]) << 24 | - static_cast(uuid.data[13]) << 16 | - static_cast(uuid.data[14]) << 8 | - static_cast(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/Comm/Version.hxx b/Comm/Version.hxx deleted file mode 100644 index ab02cf6..0000000 --- a/Comm/Version.hxx +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#define kDistVersion "v1.21" diff --git a/Examples/ExampleCDialect.S b/Examples/ExampleCDialect.S index 9d90d46..1eb2f82 100644 --- a/Examples/ExampleCDialect.S +++ b/Examples/ExampleCDialect.S @@ -1,4 +1,4 @@ -; Path: Examples/ExampleCDialect.cc +; Path: Examples/ExampleCDialect.cxx ; Language: MPCC assembly. (Generated from C++) ; Date: 2024-5-14 diff --git a/Headers/AsmKit/AsmKit.hpp b/Headers/AsmKit/AsmKit.hpp new file mode 100644 index 0000000..b492ce8 --- /dev/null +++ b/Headers/AsmKit/AsmKit.hpp @@ -0,0 +1,217 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +#include +#include +#include + +namespace CompilerKit +{ + // + // @brief Frontend to Assembly mountpoint. + // + class AssemblyInterface + { + public: + explicit AssemblyInterface() = default; + virtual ~AssemblyInterface() = default; + + MPCC_COPY_DEFAULT(AssemblyInterface); + + //@ brief compile to object file. + // 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; + + MPCC_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; + + MPCC_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; + + MPCC_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; + + MPCC_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; + + MPCC_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; + + MPCC_COPY_DEFAULT(EncoderPowerPC); + + virtual std::string CheckLine(std::string& line, + const std::string& file) override; + virtual bool WriteLine(std::string& line, const std::string& file) override; + virtual bool WriteNumber(const std::size_t& pos, + std::string& from_what) override; + }; + +#endif // __ASM_NEED_32x0__ +} // namespace CompilerKit diff --git a/Headers/AsmKit/CPU/32x0.hpp b/Headers/AsmKit/CPU/32x0.hpp new file mode 100644 index 0000000..cfe4a47 --- /dev/null +++ b/Headers/AsmKit/CPU/32x0.hpp @@ -0,0 +1,95 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +#include + +// @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]; + char fOpcode; + char fSize; + char fFunct3; + char fFunct7; +}; + +#define kAsmDWordStr ".dword" /* 64 bit */ +#define kAsmWordStr ".word" /* 32-bit */ +#define kAsmHWordStr ".half" /* 16-bit */ +#define kAsmByteStr ".byte" /* 8-bit */ + +inline std::vector kOpcodes32x0 = { + kAsmOpcodeDecl("nop", 0b0100011, 0b000, kAsmNoArgs) // nothing to do. + kAsmOpcodeDecl("br", 0b1110011, 0b001, kAsmJump) // jump to branch + kAsmOpcodeDecl("mr", 0b0100011, 0b101, kAsmImmediate) // move registers + kAsmOpcodeDecl("psh", 0b0111011, 0b000, kAsmImmediate) // push to sp + kAsmOpcodeDecl("pop", 0b0111011, 0b001, kAsmImmediate) // pop from sp. + kAsmOpcodeDecl("cls", 0b0111011, 0b010, + kAsmImmediate) // setup stack and call, store address to CR. + kAsmOpcodeDecl("rts", 0b0111011, 0b110, + kAsmImmediate) // pull stack and return form CR. + kAsmOpcodeDecl("int", 0b0111111, 0b000, kAsmSyscall) // raise interrupt +}; + +// \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/Headers/AsmKit/CPU/64x0.hpp b/Headers/AsmKit/CPU/64x0.hpp new file mode 100644 index 0000000..2da4f24 --- /dev/null +++ b/Headers/AsmKit/CPU/64x0.hpp @@ -0,0 +1,108 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +#include +#include + +// @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 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("decc", 0b0101011, 0b111, kAsmImmediate) + kAsmOpcodeDecl("int", 0b1110011, 0b00, kAsmSyscall) + kAsmOpcodeDecl("pha", 0b1110011, 0b00, kAsmNoArgs) + kAsmOpcodeDecl("pla", 0b1110011, 0b01, kAsmNoArgs)}; + +// \brief 64x0 register prefix +// example: r32, r0 +// r32 -> sp +// r0 -> hw zero + +#define kAsmFloatZeroRegister 0 +#define kAsmZeroRegister 0 + +#define kAsmRegisterPrefix "r" +#define kAsmRegisterLimit 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/Headers/AsmKit/CPU/amd64.hpp b/Headers/AsmKit/CPU/amd64.hpp new file mode 100644 index 0000000..bc4b956 --- /dev/null +++ b/Headers/AsmKit/CPU/amd64.hpp @@ -0,0 +1,57 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +#include + +// @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 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/Headers/AsmKit/CPU/arm64.hpp b/Headers/AsmKit/CPU/arm64.hpp new file mode 100644 index 0000000..e7d4f68 --- /dev/null +++ b/Headers/AsmKit/CPU/arm64.hpp @@ -0,0 +1,26 @@ +/* ------------------------------------------- + +Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +#include + +/// @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/Headers/AsmKit/CPU/ppc.hpp b/Headers/AsmKit/CPU/ppc.hpp new file mode 100644 index 0000000..e3ea6c5 --- /dev/null +++ b/Headers/AsmKit/CPU/ppc.hpp @@ -0,0 +1,1929 @@ +/* ------------------------------------------- + + Some modifications are copyrighted under: + ZKA Technologies + + Original author: + Apple Inc + +------------------------------------------- */ + +#pragma once + +#include + +/// @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< + +#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 +#include +#include + +#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 MPCC_COPY_DELETE(KLASS) \ + KLASS& operator=(const KLASS&) = delete; \ + KLASS(const KLASS&) = delete; + +#define MPCC_COPY_DEFAULT(KLASS) \ + KLASS& operator=(const KLASS&) = default; \ + KLASS(const KLASS&) = default; + +#define MPCC_MOVE_DELETE(KLASS) \ + KLASS& operator=(KLASS&&) = delete; \ + KLASS(KLASS&&) = delete; + +#define MPCC_MOVE_DEFAULT(KLASS) \ + KLASS& operator=(KLASS&&) = default; \ + KLASS(KLASS&&) = default; + +#include +#include +#include +#include + +namespace CompilerKit +{ + inline constexpr int BASE_YEAR = 1900; + + inline std::string current_date() noexcept + { + auto time_data = time(nullptr); + auto time_struct = gmtime(&time_data); + + std::string fmt = std::to_string(BASE_YEAR + 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; + } +} // namespace CompilerKit + +#define PACKED __attribute__((packed)) + +typedef char char_type; + +#define kObjectFileExt ".obj" +#define kBinaryFileExt ".bin" + +#define kAsmFileExts \ + { \ + ".64x", ".32x", ".masm", ".s", ".S", ".asm" \ + } + +#ifdef __NDK_MODULE__ +#define NDK_MODULE(name) int name(int argc, char** argv) +#else +#define NDK_MODULE(name) int main(int argc, char** argv) +#endif /* ifdef __NDK_MODULE__ */ + +#pragma scalar_storage_order big-endian + +#endif /* ifndef __MPCC_DEFINES_HPP__ */ diff --git a/Headers/Macros.hpp b/Headers/Macros.hpp new file mode 100644 index 0000000..c190cda --- /dev/null +++ b/Headers/Macros.hpp @@ -0,0 +1,33 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +/// @brief provide support for Macros.hpp header. + +#ifndef _CK_CL_HPP +#define _CK_CL_HPP + +#define MPCC_COPY_DELETE(KLASS) \ + KLASS& operator=(const KLASS&) = delete; \ + KLASS(const KLASS&) = delete; + +#define MPCC_COPY_DEFAULT(KLASS) \ + KLASS& operator=(const KLASS&) = default; \ + KLASS(const KLASS&) = default; + +#define MPCC_MOVE_DELETE(KLASS) \ + KLASS& operator=(KLASS&&) = delete; \ + KLASS(KLASS&&) = delete; + +#define MPCC_MOVE_DEFAULT(KLASS) \ + KLASS& operator=(KLASS&&) = default; \ + KLASS(KLASS&&) = default; + +/// @note xxxx is the error placeholder, in hexadecimal. +#define MPCC_ERROR_PREFIX_CXX "CXXxxxx" +#define MPCC_ERROR_PREFIX_CL "CLxxxx" +#define MPCC_ERROR_PREFIX_ASM "ASMxxxx" + +#endif /* ifndef _CK_CL_HPP */ diff --git a/Headers/ParserKit.hpp b/Headers/ParserKit.hpp new file mode 100644 index 0000000..186c9cf --- /dev/null +++ b/Headers/ParserKit.hpp @@ -0,0 +1,173 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +#include +#include + +namespace ParserKit +{ + using namespace CompilerKit; + + /// @brief Compiler backend, implements a frontend, such as C, C++... + /// See Toolchain, for some examples. + class CompilerBackend + { + public: + explicit CompilerBackend() = default; + virtual ~CompilerBackend() = default; + + MPCC_COPY_DEFAULT(CompilerBackend); + + // 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 char* file) = 0; + + //! @brief What language are we dealing with? + virtual const char* Language() + { + return "Invalid Language"; + } + }; + + 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 __PK_USE_STRUCT_INSTEAD__ + CompilerKeyword fUserData; +#else + std::string fUserData; +#endif + + std::string fUserValue; + struct SyntaxLeaf* fNext; + }; + + std::vector fLeafList; + SizeType fNumLeafs; + + size_t SizeOf() + { + return fNumLeafs; + } + std::vector& 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 ParserKit diff --git a/Headers/Public/SDK/CRT/__mpcc_alloca.hxx b/Headers/Public/SDK/CRT/__mpcc_alloca.hxx new file mode 100644 index 0000000..02b3123 --- /dev/null +++ b/Headers/Public/SDK/CRT/__mpcc_alloca.hxx @@ -0,0 +1,15 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +typedef void* ptr_type; +typedef __SIZE_TYPE__ size_type; + +inline void* __mpcc_alloca_gcc(size_type sz) +{ + return __builtin_alloca(sz); +} diff --git a/Headers/Public/SDK/CRT/__mpcc_defines.hxx b/Headers/Public/SDK/CRT/__mpcc_defines.hxx new file mode 100644 index 0000000..19ed8a4 --- /dev/null +++ b/Headers/Public/SDK/CRT/__mpcc_defines.hxx @@ -0,0 +1,83 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#ifndef __MPCC_DEF__ +#define __MPCC_DEF__ + +#ifndef __GNUC__ + +typedef __SIZE_TYPE__ size_t; + +#ifdef __LP64__ +typedef long int ssize_t; +#else +typedef int ssize_t; +#endif // __LP64__ + +typedef size_t ptrdiff_t; +typedef size_t uintptr_t; +typedef void* voidptr_t; +typedef void* any_t; +typedef char* caddr_t; + +#ifndef NULL +#define NULL ((voidptr_t)0) +#endif // !null + +#ifdef __GNUC__ +#include +#define __mpcc_alloca(sz) __mpcc_alloca_gcc(sz) +#define __packed__ __attribute__((packed)) +#elif defined(__MPCC__) +#define __packed__ __mpcc_packed__ +#define __alloca(sz) __mpcc_alloca(sz) +#endif + +#define __deref(ptr) (*(ptr)) + +#ifdef __cplusplus +#define __init_decl() \ + extern "C" \ + { + + +#define __fini_decl() \ + }; \ + + +#else +#define __init_decl() +#define __fini_decl() +#endif + +typedef long long off_t; +typedef unsigned long long uoff_t; + +typedef union float_cast { + struct + { + unsigned int mantissa : 23; + unsigned int exponent : 8; + unsigned int sign : 1; + }; + + float f; +} __packed__ float_cast_t; + +typedef union double_cast { + struct + { + unsigned long long int mantissa : 52; + unsigned int exponent : 11; + unsigned int sign : 1; + }; + + double f; +} __packed__ double_cast_t; + +#endif // ifndef __GNUC__ + +#endif /* __MPCC_DEF__ */ diff --git a/Headers/Public/SDK/CRT/__mpcc_exception.hxx b/Headers/Public/SDK/CRT/__mpcc_exception.hxx new file mode 100644 index 0000000..4a9f84a --- /dev/null +++ b/Headers/Public/SDK/CRT/__mpcc_exception.hxx @@ -0,0 +1,27 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +/// This file is an implementation of __throw* family of functions. + +#include +#include +#include + +namespace std +{ + inline void __throw_general(void) + { + throw std::runtime_error("MPCC C++ Runtime error."); + } + + inline void __throw_domain_error(const char* error) + { + std::cout << "MPCC C++: Domain error: " << error << "\r"; + __throw_general(); + } +} // namespace std diff --git a/Headers/Public/SDK/CRT/__mpcc_hint.hxx b/Headers/Public/SDK/CRT/__mpcc_hint.hxx new file mode 100644 index 0000000..02dbc94 --- /dev/null +++ b/Headers/Public/SDK/CRT/__mpcc_hint.hxx @@ -0,0 +1,20 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +#pragma compiler(hint_manifest) + +#define _Input +#define _Output + +#define _Optional + +#define _StrictCheckInput +#define _StrictCheckOutput + +#define _InOut +#define _StrictInOut diff --git a/Headers/Public/SDK/CRT/__mpcc_malloc.hxx b/Headers/Public/SDK/CRT/__mpcc_malloc.hxx new file mode 100644 index 0000000..eeaa67b --- /dev/null +++ b/Headers/Public/SDK/CRT/__mpcc_malloc.hxx @@ -0,0 +1,30 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +#include + +namespace stdx +{ + /// @brief allocate a new class. + /// @tparam KindClass the class type to allocate. + template + inline void* allocate(Args&&... args) + { + return new KindClass(std::forward(args)...); + } + + /// @brief free a class. + /// @tparam KindClass the class type to allocate. + template + inline void release(KindClass ptr) + { + if (!ptr) + return; + delete ptr; + } +} // namespace stdx diff --git a/Headers/Public/SDK/CRT/__mpcc_power.inc b/Headers/Public/SDK/CRT/__mpcc_power.inc new file mode 100644 index 0000000..9e4928c --- /dev/null +++ b/Headers/Public/SDK/CRT/__mpcc_power.inc @@ -0,0 +1,35 @@ +# Path: SDK/__mpcc_power.inc +# Language: MPCC POWER Assembly support for GNU. +# Build Date: 2024-6-4 + +%ifdef __CODETOOLS__ + +%def lda li +%def sta stw +%def ldw li + +%def r0 0 +%def r1 1 +%def r2 2 +%def r3 3 +%def r4 4 +%def r5 5 +%def r6 6 +%def r7 7 +%def r8 8 +%def r9 9 +%def r10 10 +%def r11 11 +%def r12 12 +%def r13 13 +%def r14 14 +%def r15 15 +%def r16 16 +%def r17 17 +%def r18 18 +%def r19 19 +%def r20 20 + +%endif + +%def nop mr 0, 0 diff --git a/Headers/StdKit/AE.hpp b/Headers/StdKit/AE.hpp new file mode 100644 index 0000000..baa4249 --- /dev/null +++ b/Headers/StdKit/AE.hpp @@ -0,0 +1,143 @@ +/* + * ======================================================== + * + * MPCC + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include + +#define kAEMag0 'A' +#define kAEMag1 'E' + +#define kAESymbolLen 64 +#define kAEPad 8 +#define kAEMagLen 2 +#define kAEInvalidOpcode 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 CompilerKit +{ + // @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 CompilerKit + +// provide operator<< for AE + +std::ofstream& operator<<(std::ofstream& fp, CompilerKit::AEHeader& container) +{ + fp.write((char*)&container, sizeof(CompilerKit::AEHeader)); + + return fp; +} + +std::ofstream& operator<<(std::ofstream& fp, + CompilerKit::AERecordHeader& container) +{ + fp.write((char*)&container, sizeof(CompilerKit::AERecordHeader)); + + return fp; +} + +std::ifstream& operator>>(std::ifstream& fp, CompilerKit::AEHeader& container) +{ + fp.read((char*)&container, sizeof(CompilerKit::AEHeader)); + return fp; +} + +std::ifstream& operator>>(std::ifstream& fp, + CompilerKit::AERecordHeader& container) +{ + fp.read((char*)&container, sizeof(CompilerKit::AERecordHeader)); + return fp; +} + +namespace CompilerKit::Utils +{ + /** + * @brief AE Reader protocol + * + */ + class AEReadableProtocol final + { + public: + std::ifstream FP; + + public: + explicit AEReadableProtocol() = default; + ~AEReadableProtocol() = default; + + MPCC_COPY_DELETE(AEReadableProtocol); + + /** + * @brief Read AE record + * + * @param raw the containing buffer + * @param sz it's size (without sizeof(AERecordHeader) added to it.) + * @return AERecordHeaderPtr + */ + AERecordHeaderPtr Read(char* raw, std::size_t sz) + { + if (!raw) + return nullptr; + + return this->_Read(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 + TypeClass* _Read(char* raw, std::size_t sz) + { + FP.read(raw, std::streamsize(sz)); + return reinterpret_cast(raw); + } + }; +} // namespace CompilerKit::Utils diff --git a/Headers/StdKit/ELF.hpp b/Headers/StdKit/ELF.hpp new file mode 100644 index 0000000..4f0d0ae --- /dev/null +++ b/Headers/StdKit/ELF.hpp @@ -0,0 +1,389 @@ +#pragma once + +#include +#include + +struct file; + +#ifndef elf_read_implies_exec +/* Executables for which elf_read_implies_exec() returns TRUE will + have the READ_IMPLIES_EXEC personality flag set automatically. + Override in asm/elf.h as needed. */ +#define elf_read_implies_exec(ex, have_pt_gnu_stack) 0 +#endif + +/* 32-bit ELF base types. */ +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; + +/* 64-bit ELF base types. */ +typedef uintptr_t Elf64_Addr; +typedef uint16_t Elf64_Half; +typedef int16_t Elf64_SHalf; +typedef uint64_t Elf64_Off; +typedef int32_t Elf64_Sword; +typedef uint32_t Elf64_Word; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* These constants are for the segment types stored in the image headers */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_TLS 7 /* Thread local storage segment */ +#define PT_LOOS 0x60000000 /* OS-specific */ +#define PT_HIOS 0x6fffffff /* OS-specific */ +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff +#define PT_GNU_EH_FRAME 0x6474e550 + +#define PT_GNU_STACK (PT_LOOS + 0x474e551) + +/* These constants define the different elf file types */ +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + +/* This is the info that is needed to parse the dynamic section of the file */ +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_ENCODING 32 +#define OLD_DT_LOOS 0x60000000 +#define DT_LOOS 0x6000000d +#define DT_HIOS 0x6ffff000 +#define DT_VALRNGLO 0x6ffffd00 +#define DT_VALRNGHI 0x6ffffdff +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_VERSYM 0x6ffffff0 +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa +#define DT_FLAGS_1 0x6ffffffb +#define DT_VERDEF 0x6ffffffc +#define DT_VERDEFNUM 0x6ffffffd +#define DT_VERNEED 0x6ffffffe +#define DT_VERNEEDNUM 0x6fffffff +#define OLD_DT_HIOS 0x6fffffff +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff + +/* This info is needed when parsing the symbol table */ +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_COMMON 5 +#define STT_TLS 6 + +#define ELF_ST_BIND(x) ((x) >> 4) +#define ELF_ST_TYPE(x) (((unsigned int)x) & 0xf) +#define ELF32_ST_BIND(x) ELF_ST_BIND(x) +#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) +#define ELF64_ST_BIND(x) ELF_ST_BIND(x) +#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) + +typedef struct dynamic +{ + Elf32_Sword d_tag; + union { + Elf32_Sword d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* entry tag value */ + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +/* The following are used with relocations */ +#define ELF32_R_SYM(x) ((x) >> 8) +#define ELF32_R_TYPE(x) ((x)&0xff) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i)&0xffffffff) + +typedef struct elf32_rel +{ + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct elf64_rel +{ + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Xword r_info; /* index and type of relocation */ +} Elf64_Rel; + +typedef struct elf32_rela +{ + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct elf64_rela +{ + Elf64_Addr r_offset; /* Location at which to apply the action */ + Elf64_Xword r_info; /* index and type of relocation */ + Elf64_Sxword r_addend; /* Constant addend used to compute value */ +} Elf64_Rela; + +typedef struct elf32_sym +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct elf64_sym +{ + Elf64_Word st_name; /* Symbol name, index in string tbl */ + unsigned char st_info; /* Type and binding attributes */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf64_Half st_shndx; /* Associated section index */ + Elf64_Addr st_value; /* Value of the symbol */ + Elf64_Xword st_size; /* Associated symbol size */ +} Elf64_Sym; + +#define EI_NIDENT 16 + +typedef struct elf32_hdr +{ + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; /* Entry point */ + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct elf64_hdr +{ + unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */ + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* These constants define the permissions on sections in the program + header, p_flags. */ +#define PF_R 0x4 +#define PF_W 0x2 +#define PF_X 0x1 + +typedef struct elf32_phdr +{ + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct elf64_phdr +{ + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment, file & memory */ +} Elf64_Phdr; + +/* sh_type */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_NUM 12 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +/* sh_flags */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + +/* special section indexes */ +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + +typedef struct +{ + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct elf64_shdr +{ + Elf64_Word sh_name; /* Section name, index in string tbl */ + Elf64_Word sh_type; /* Type of section */ + Elf64_Xword sh_flags; /* Miscellaneous section attributes */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Size of section in bytes */ + Elf64_Word sh_link; /* Index of another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +#define EI_MAG0 0 /* e_ident[] indexes */ +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_PAD 8 + +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define ELFCLASSNONE 0 /* EI_CLASS */ +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define EV_NONE 0 /* e_version, EI_VERSION */ +#define EV_CURRENT 1 +#define EV_NUM 2 + +#define ELFOSABI_NONE 0 +#define ELFOSABI_LINUX 3 + +#ifndef ELF_OSABI +#define ELF_OSABI ELFOSABI_NONE +#endif + +/* Notes used in ET_CORE */ +#define NT_PRSTATUS 1 +#define NT_PRFPREG 2 +#define NT_PRPSINFO 3 +#define NT_TASKSTRUCT 4 +#define NT_AUXV 6 +#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ +#define NT_PPC_VMX 0x100 /* POWER Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* POWER SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* POWER VSX registers */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_PRXSTATUS 0x300 /* s390 upper register halves */ + +/* Note header in a PT_NOTE section */ +typedef struct elf32_note +{ + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ +} Elf32_Nhdr; + +/* Note header in a PT_NOTE section */ +typedef struct elf64_note +{ + Elf64_Word n_namesz; /* Name size */ + Elf64_Word n_descsz; /* Content size */ + Elf64_Word n_type; /* Content type */ +} Elf64_Nhdr; diff --git a/Headers/StdKit/ErrorID.hpp b/Headers/StdKit/ErrorID.hpp new file mode 100644 index 0000000..41d6b2f --- /dev/null +++ b/Headers/StdKit/ErrorID.hpp @@ -0,0 +1,22 @@ +/* + * ======================================================== + * + * CompilerKit + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include +#include + +#define MPCC_EXEC_ERROR -30 +#define MPCC_FILE_NOT_FOUND -31 +#define MPCC_DIR_NOT_FOUND -32 +#define MPCC_FILE_EXISTS -33 +#define MPCC_TOO_LONG -34 +#define MPCC_INVALID_DATA -35 +#define MPCC_UNIMPLEMENTED -36 +#define MPCC_FAT_ERROR -37 diff --git a/Headers/StdKit/ErrorOr.hpp b/Headers/StdKit/ErrorOr.hpp new file mode 100644 index 0000000..6c8e87e --- /dev/null +++ b/Headers/StdKit/ErrorOr.hpp @@ -0,0 +1,61 @@ +/* + * ======================================================== + * + * CompilerKit + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include +#include + +namespace CompilerKit +{ + using ErrorT = UInt32; + + template + 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 Leak() + { + return mRef; + } + + operator bool() + { + return mRef; + } + + private: + Ref mRef; + Int32 mId{0}; + }; + + using ErrorOrAny = ErrorOr; + +} // namespace CompilerKit diff --git a/Headers/StdKit/PEF.hpp b/Headers/StdKit/PEF.hpp new file mode 100644 index 0000000..d8709ed --- /dev/null +++ b/Headers/StdKit/PEF.hpp @@ -0,0 +1,134 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#pragma once + +#include + +// @file PEF.hpp +// @brief Preferred Executable Format + +#define kPefMagic "Joy!" +#define kPefMagicFat "yoJ!" + +#define kPefExt ".exec" +#define kPefDylibExt ".lib" +#define kPefLibExt ".slib" +#define kPefObjectExt ".obj" +#define kPefDebugExt ".dbg" + +#define kPefMagicLen 5 + +#define kPefVersion 2 +#define kPefNameLen 255 + +#define kPefBaseOrigin (0x1000000) + +#define kPefStart "__ImageStart" + +namespace CompilerKit +{ + 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, + kPefSubArchGeneric, + kPefSubArchIBM, + }; + + enum + { + kPefKindExec = 1, /* .exe */ + kPefKindSharedObject = 2, /* .lib */ + 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 CompilerKit + +inline std::ofstream& operator<<(std::ofstream& fp, + CompilerKit::PEFContainer& container) +{ + fp.write((char*)&container, sizeof(CompilerKit::PEFContainer)); + return fp; +} + +inline std::ofstream& operator<<(std::ofstream& fp, + CompilerKit::PEFCommandHeader& container) +{ + fp.write((char*)&container, sizeof(CompilerKit::PEFCommandHeader)); + return fp; +} + +std::ifstream& operator>>(std::ifstream& fp, + CompilerKit::PEFContainer& container) +{ + fp.read((char*)&container, sizeof(CompilerKit::PEFContainer)); + return fp; +} + +std::ifstream& operator>>(std::ifstream& fp, + CompilerKit::PEFCommandHeader& container) +{ + fp.read((char*)&container, sizeof(CompilerKit::PEFCommandHeader)); + return fp; +} diff --git a/Headers/StdKit/Ref.hpp b/Headers/StdKit/Ref.hpp new file mode 100644 index 0000000..e655ccb --- /dev/null +++ b/Headers/StdKit/Ref.hpp @@ -0,0 +1,91 @@ + +/* + * ======================================================== + * + * CompilerKit + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +namespace CompilerKit +{ + // @author ZKA Technologies + // @brief Reference class, refers to a pointer of data in static memory. + template + class Ref final + { + public: + explicit Ref() = default; + ~Ref() = default; + + 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; + bool m_Strong{false}; + }; + + template + class NonNullRef final + { + public: + NonNullRef() = delete; + NonNullRef(nullPtr) = delete; + + explicit NonNullRef(T* ref) + : m_Ref(ref, true) + { + } + + Ref& operator->() + { + MUST_PASS(m_Ref); + return m_Ref; + } + + NonNullRef& operator=(const NonNullRef& ref) = delete; + NonNullRef(const NonNullRef& ref) = default; + + private: + Ref m_Ref{nullptr}; + }; +} // namespace CompilerKit diff --git a/Headers/StdKit/String.hpp b/Headers/StdKit/String.hpp new file mode 100644 index 0000000..59a7997 --- /dev/null +++ b/Headers/StdKit/String.hpp @@ -0,0 +1,90 @@ +/* + * ======================================================== + * + * CompilerKit + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include +#include + +namespace CompilerKit +{ + /** + * @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 char[Sz]; + assert(m_Data); + } + + ~StringView() noexcept + { + if (m_Data) + { + memset(m_Data, 0, m_Sz); + delete[] m_Data; + + m_Data = nullptr; + } + } + + MPCC_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: + char* 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 CompilerKit diff --git a/Headers/StdKit/XCOFF.hxx b/Headers/StdKit/XCOFF.hxx new file mode 100644 index 0000000..dbc258d --- /dev/null +++ b/Headers/StdKit/XCOFF.hxx @@ -0,0 +1,41 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + + File: XCOFF.hpp + Purpose: XCOFF for NewOS. + + Revision History: + + 04/07/24: Added file (amlel) + +------------------------------------------- */ + +#ifndef __XCOFF__ +#define __XCOFF__ + +#include + +#define kXCOFF64Magic 0x01F7 + +#define kXCOFFRelFlg 0x0001 +#define kXCOFFExecutable 0x0002 +#define kXCOFFLnno 0x0004 +#define kXCOFFLSyms 0x0008 + +namespace CompilerKit +{ + /// @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 CompilerKit + +#endif // ifndef __XCOFF__ diff --git a/Headers/UUID.hpp b/Headers/UUID.hpp new file mode 100644 index 0000000..00b153b --- /dev/null +++ b/Headers/UUID.hpp @@ -0,0 +1,983 @@ +#ifndef STDUUID_H +#define STDUUID_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus + +#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define LIBUUID_CPP20_OR_GREATER +#endif + +#endif + +#ifdef LIBUUID_CPP20_OR_GREATER +#include +#else +#include +#endif + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifdef UUID_SYSTEM_GENERATOR +#include +#endif + +#ifdef UUID_TIME_GENERATOR +#include +#pragma comment(lib, "IPHLPAPI.lib") +#endif + +#elif defined(__linux__) || defined(__unix__) + +#ifdef UUID_SYSTEM_GENERATOR +#include +#endif + +#elif defined(__APPLE__) + +#ifdef UUID_SYSTEM_GENERATOR +#include +#endif + +#endif + +namespace uuids +{ +#ifdef __cpp_lib_span + template + using span = std::span; +#else + template + using span = gsl::span; +#endif + + namespace detail + { + template + [[nodiscard]] constexpr inline unsigned char hex2char(TChar const ch) noexcept + { + if (ch >= static_cast('0') && ch <= static_cast('9')) + return static_cast(ch - static_cast('0')); + if (ch >= static_cast('a') && ch <= static_cast('f')) + return static_cast(10 + ch - static_cast('a')); + if (ch >= static_cast('A') && ch <= static_cast('F')) + return static_cast(10 + ch - static_cast('A')); + return 0; + } + + template + [[nodiscard]] constexpr inline bool is_hex(TChar const ch) noexcept + { + return (ch >= static_cast('0') && ch <= static_cast('9')) || + (ch >= static_cast('a') && ch <= static_cast('f')) || + (ch >= static_cast('A') && ch <= static_cast('F')); + } + + template + [[nodiscard]] constexpr std::basic_string_view to_string_view( + TChar const* str) noexcept + { + if (str) + return str; + return {}; + } + + template + [[nodiscard]] constexpr std::basic_string_view + 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(start); + const uint8_t* finish = static_cast(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(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((bitCount >> 24) & 0xFF)); + process_byte(static_cast((bitCount >> 16) & 0xFF)); + process_byte(static_cast((bitCount >> 8) & 0xFF)); + process_byte(static_cast((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(d32[0] >> 24); + digest[di++] = static_cast(d32[0] >> 16); + digest[di++] = static_cast(d32[0] >> 8); + digest[di++] = static_cast(d32[0] >> 0); + + digest[di++] = static_cast(d32[1] >> 24); + digest[di++] = static_cast(d32[1] >> 16); + digest[di++] = static_cast(d32[1] >> 8); + digest[di++] = static_cast(d32[1] >> 0); + + digest[di++] = static_cast(d32[2] >> 24); + digest[di++] = static_cast(d32[2] >> 16); + digest[di++] = static_cast(d32[2] >> 8); + digest[di++] = static_cast(d32[2] >> 0); + + digest[di++] = static_cast(d32[3] >> 24); + digest[di++] = static_cast(d32[3] >> 16); + digest[di++] = static_cast(d32[3] >> 8); + digest[di++] = static_cast(d32[3] >> 0); + + digest[di++] = static_cast(d32[4] >> 24); + digest[di++] = static_cast(d32[4] >> 16); + digest[di++] = static_cast(d32[4] >> 8); + digest[di++] = static_cast(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(m_block[i * 4 + 0] << 24); + w[i] |= static_cast(m_block[i * 4 + 1] << 16); + w[i] |= static_cast(m_block[i * 4 + 2] << 8); + w[i] |= static_cast(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 + inline constexpr CharT empty_guid[37] = "00000000-0000-0000-0000-000000000000"; + + template <> + inline constexpr wchar_t empty_guid[37] = + L"00000000-0000-0000-0000-000000000000"; + + template + inline constexpr CharT guid_encoder[17] = "0123456789abcdef"; + + template <> + inline constexpr wchar_t guid_encoder[17] = L"0123456789abcdef"; + } // namespace detail + + // -------------------------------------------------------------------------------------------------------------------------- + // 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 Allocator = std::allocator> + std::basic_string 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 const& arr) noexcept + : data{arr} + { + } + + explicit uuid(span bytes) + { + std::copy(std::cbegin(bytes), std::cend(bytes), std::begin(data)); + } + + template + 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 as_bytes() const + { + return span( + reinterpret_cast(data.data()), 16); + } + + template + [[nodiscard]] constexpr static bool is_valid_uuid( + StringType const& in_str) noexcept + { + auto str = detail::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 || !detail::is_hex(str[i])) + { + return false; + } + + if (firstDigit) + { + firstDigit = false; + } + else + { + index++; + firstDigit = true; + } + } + + if (index < 16) + { + return false; + } + + return true; + } + + template + [[nodiscard]] constexpr static std::optional from_string( + StringType const& in_str) noexcept + { + auto str = detail::to_string_view(in_str); + bool firstDigit = true; + size_t hasBraces = 0; + size_t index = 0; + + std::array 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 || !detail::is_hex(str[i])) + { + return {}; + } + + if (firstDigit) + { + data[index] = static_cast(detail::hex2char(str[i]) << 4); + firstDigit = false; + } + else + { + data[index] = + static_cast(data[index] | detail::hex2char(str[i])); + index++; + firstDigit = true; + } + } + + if (index < 16) + { + return {}; + } + + return uuid{data}; + } + + private: + std::array data{{0}}; + + friend bool operator==(uuid const& lhs, uuid const& rhs) noexcept; + friend bool operator<(uuid const& lhs, uuid const& rhs) noexcept; + + template + friend std::basic_ostream& operator<<( + std::basic_ostream& s, uuid const& id); + + template + friend std::basic_string to_string(uuid const& id); + + friend std::hash; + }; + + // -------------------------------------------------------------------------------------------------------------------------- + // 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 + [[nodiscard]] inline std::basic_string to_string( + uuid const& id) + { + std::basic_string uustr{detail::empty_guid}; + + for (size_t i = 0, index = 0; i < 36; ++i) + { + if (i == 8 || i == 13 || i == 18 || i == 23) + { + continue; + } + uustr[i] = detail::guid_encoder[id.data[index] >> 4 & 0x0f]; + uustr[++i] = detail::guid_encoder[id.data[index] & 0x0f]; + index++; + } + + return uustr; + } + + template + std::basic_ostream& operator<<( + std::basic_ostream& 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 bytes = { + {static_cast((newId.Data1 >> 24) & 0xFF), + static_cast((newId.Data1 >> 16) & 0xFF), + static_cast((newId.Data1 >> 8) & 0xFF), + static_cast((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 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 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 + 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(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 distribution; + std::shared_ptr generator; + }; + + using uuid_random_generator = basic_uuid_random_generator; + + class uuid_name_generator + { + public: + explicit uuid_name_generator(uuid const& namespace_uuid) noexcept + : nsuuid(namespace_uuid) + { + } + + template + [[nodiscard]] uuid operator()(StringType const& name) + { + reset(); + process_characters(detail::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 + void process_characters(std::basic_string_view const str) + { + for (uint32_t c : str) + { + hasher.process_byte(static_cast(c & 0xFF)); + if constexpr (!std::is_same_v) + { + hasher.process_byte(static_cast((c >> 8) & 0xFF)); + hasher.process_byte(static_cast((c >> 16) & 0xFF)); + hasher.process_byte(static_cast((c >> 24) & 0xFF)); + } + } + } + + [[nodiscard]] uuid make_uuid() + { + detail::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; + detail::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; + + std::optional 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 buf(len); + auto pips = reinterpret_cast(&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(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 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 data; + + auto tm = get_time_intervals(); + + auto clock_seq = get_clock_sequence(); + + auto ptm = reinterpret_cast(&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 + { + 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 hasher; + return static_cast(hasher(uuids::to_string(uuid))); +#else + uint64_t l = static_cast(uuid.data[0]) << 56 | + static_cast(uuid.data[1]) << 48 | + static_cast(uuid.data[2]) << 40 | + static_cast(uuid.data[3]) << 32 | + static_cast(uuid.data[4]) << 24 | + static_cast(uuid.data[5]) << 16 | + static_cast(uuid.data[6]) << 8 | + static_cast(uuid.data[7]); + uint64_t h = static_cast(uuid.data[8]) << 56 | + static_cast(uuid.data[9]) << 48 | + static_cast(uuid.data[10]) << 40 | + static_cast(uuid.data[11]) << 32 | + static_cast(uuid.data[12]) << 24 | + static_cast(uuid.data[13]) << 16 | + static_cast(uuid.data[14]) << 8 | + static_cast(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/Headers/Version.hxx b/Headers/Version.hxx new file mode 100644 index 0000000..639c2c6 --- /dev/null +++ b/Headers/Version.hxx @@ -0,0 +1,3 @@ +#pragma once + +#define kDistVersion "v1.22" diff --git a/Notes/ASM specs.txt b/Notes/ASM specs.txt new file mode 100644 index 0000000..a0c42bf --- /dev/null +++ b/Notes/ASM specs.txt @@ -0,0 +1,11 @@ +==================== +X86 ASSEMBLER SPECS +==================== + +WHAT TO DO: + Provide a complete support of x86-64 with: + + - org directive. + - 64-bit and 32-bit registers. + - basic instructions (load, store, jump to) + - flushable into object-code and flat binary. \ No newline at end of file diff --git a/Notes/HAVP DSP.txt b/Notes/HAVP DSP.txt new file mode 100644 index 0000000..12fcec5 --- /dev/null +++ b/Notes/HAVP DSP.txt @@ -0,0 +1,13 @@ +HAVP - Harvard Audio/Video Processor + +- Encoding: IBAD + +- Data path = 24 + - 16: sound data + - 8: information data + +- Register size: 32 +- Store strategy: shift registers. +- Standard registers: [ r0, r9 ] +- Floating point registers: [ f0, f2 ] +- Builtin SRAM: 512kb diff --git a/Notes/Notice.txt b/Notes/Notice.txt new file mode 100644 index 0000000..23691da --- /dev/null +++ b/Notes/Notice.txt @@ -0,0 +1,4 @@ +The X64000 draft papers +They contain thing that might appear through the next iteration of 64k. + +Please look at the document shared for the latest revisions. \ No newline at end of file diff --git a/Notes/RISC CPU.txt b/Notes/RISC CPU.txt new file mode 100644 index 0000000..e17b494 --- /dev/null +++ b/Notes/RISC CPU.txt @@ -0,0 +1,17 @@ +VNRP - Von Neumann, RISC Processor + +- Encoding = RegToReg, Imm, Syscall, Jump, NoArgs + +- Data path = 128-bit (register data) +- Addressing = 58-bit physical address size. + +- Registers (128-bit) = r0, r19 +- Float/Vector registers (128-bit) = f0, f9 + +- Out of order (superscalar also added to the equation) = Yes +- Superscalar = Yes + +- L1 cache: 16kb (8 instr, 8 data) +- L2 cache: 1024kb (512 instr, 512 data) + +- Clock speed: 1 Ghz diff --git a/Notes/asm-specs.txt b/Notes/asm-specs.txt deleted file mode 100644 index a0c42bf..0000000 --- a/Notes/asm-specs.txt +++ /dev/null @@ -1,11 +0,0 @@ -==================== -X86 ASSEMBLER SPECS -==================== - -WHAT TO DO: - Provide a complete support of x86-64 with: - - - org directive. - - 64-bit and 32-bit registers. - - basic instructions (load, store, jump to) - - flushable into object-code and flat binary. \ No newline at end of file diff --git a/Notes/havp.txt b/Notes/havp.txt deleted file mode 100644 index 12fcec5..0000000 --- a/Notes/havp.txt +++ /dev/null @@ -1,13 +0,0 @@ -HAVP - Harvard Audio/Video Processor - -- Encoding: IBAD - -- Data path = 24 - - 16: sound data - - 8: information data - -- Register size: 32 -- Store strategy: shift registers. -- Standard registers: [ r0, r9 ] -- Floating point registers: [ f0, f2 ] -- Builtin SRAM: 512kb diff --git a/Notes/notice.txt b/Notes/notice.txt deleted file mode 100644 index 23691da..0000000 --- a/Notes/notice.txt +++ /dev/null @@ -1,4 +0,0 @@ -The X64000 draft papers -They contain thing that might appear through the next iteration of 64k. - -Please look at the document shared for the latest revisions. \ No newline at end of file diff --git a/Notes/vnrp.txt b/Notes/vnrp.txt deleted file mode 100644 index e17b494..0000000 --- a/Notes/vnrp.txt +++ /dev/null @@ -1,17 +0,0 @@ -VNRP - Von Neumann, RISC Processor - -- Encoding = RegToReg, Imm, Syscall, Jump, NoArgs - -- Data path = 128-bit (register data) -- Addressing = 58-bit physical address size. - -- Registers (128-bit) = r0, r19 -- Float/Vector registers (128-bit) = f0, f9 - -- Out of order (superscalar also added to the equation) = Yes -- Superscalar = Yes - -- L1 cache: 16kb (8 instr, 8 data) -- L2 cache: 1024kb (512 instr, 512 data) - -- Clock speed: 1 Ghz diff --git a/Sources/32asm.cc b/Sources/32asm.cc deleted file mode 100644 index d32ab0b..0000000 --- a/Sources/32asm.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -/// bugs: 0 - -///////////////////////////////////////////////////////////////////////////////////////// - -// @file 32asm.cxx -// @author ZKA Technologies -// @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 -#include -#include -#include -#include -#include -#include -#include -#include - -///////////////////// - -// 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. - -///////////////////////////////////////////////////////////////////////////////////////// - -NDK_MODULE(NewOSAssembler32000) { return 0; } diff --git a/Sources/32asm.cxx b/Sources/32asm.cxx new file mode 100644 index 0000000..8577722 --- /dev/null +++ b/Sources/32asm.cxx @@ -0,0 +1,52 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +/// bugs: 0 + +///////////////////////////////////////////////////////////////////////////////////////// + +// @file 32asm.cxx +// @author ZKA Technologies +// @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 +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////// + +// 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. + +///////////////////////////////////////////////////////////////////////////////////////// + +NDK_MODULE(NewOSAssembler32000) { return 0; } diff --git a/Sources/64asm.cc b/Sources/64asm.cc deleted file mode 100644 index 0a89e6a..0000000 --- a/Sources/64asm.cc +++ /dev/null @@ -1,954 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -/// bugs: 0 - -///////////////////////////////////////////////////////////////////////////////////////// - -// @file 64asm.cxx -// @author Amlal EL Mahrouss -// @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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -///////////////////// - -// 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 = CompilerKit::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> kOriginLabel; - -static bool kVerbose = false; - -static std::vector kBytes; - -static CompilerKit::AERecordHeader kCurrentRecord{ - .fName = "", .fKind = CompilerKit::kPefCode, .fSize = 0, .fOffset = 0}; - -static std::vector kRecords; -static std::vector kUndefinedSymbols; - -static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; -static const std::string kRelocSymbol = ":RuntimeSymbol:"; - -// \brief forward decl. -static bool asm_read_attributes(std::string &line); - -namespace detail { -void print_error(std::string reason, const std::string &file) noexcept { - if (reason[0] == '\n') reason.erase(0, 1); - - kStdErr << kRed << "[ 64asm ] " << kWhite - << ((file == "64asm") ? "internal assembler error " - : ("in file, " + file)) - << kBlank << std::endl; - kStdErr << kRed << "[ 64asm ] " << kWhite << reason << kBlank << std::endl; - - if (kAcceptableErrors > kErrorLimit) std::exit(3); - - ++kAcceptableErrors; -} - -void print_warning(std::string reason, const std::string &file) noexcept { - if (reason[0] == '\n') reason.erase(0, 1); - - if (!file.empty()) { - kStdOut << kYellow << "[ file ] " << kWhite << file << kBlank << std::endl; - } - - kStdOut << kYellow << "[ 64asm ] " << kWhite << reason << kBlank << std::endl; -} -} // namespace detail - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief 64x0 assembler entrypoint, the program/module starts here. - -///////////////////////////////////////////////////////////////////////////////////////// - -NDK_MODULE(NewOSAssembler64000) { - for (size_t i = 1; i < argc; ++i) { - if (argv[i][0] == '-') { - if (strcmp(argv[i], "-version") == 0 || strcmp(argv[i], "-v") == 0) { - kStdOut << "64asm: 64x0 Assembler.\n64asm: v1.10\n64asm: Copyright (c) " - "ZKA Technologies.\n"; - return 0; - } else if (strcmp(argv[i], "-h") == 0) { - kStdOut << "64asm: 64x0 Assembler.\n64asm: 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 << "64asm: ignore " << argv[i] << "\n"; - continue; - } - - if (!std::filesystem::exists(argv[i])) { - kStdOut << "64asm: 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 << "64asm: error: " << strerror(errno) << "\n"; - } - } - - std::string line; - - CompilerKit::AEHeader hdr{0}; - - memset(hdr.fPad, kAEInvalidOpcode, kAEPad); - - hdr.fMagic[0] = kAEMag0; - hdr.fMagic[1] = kAEMag1; - hdr.fSize = sizeof(CompilerKit::AEHeader); - hdr.fArch = kOutputArch; - - ///////////////////////////////////////////////////////////////////////////////////////// - - // COMPILATION LOOP - - ///////////////////////////////////////////////////////////////////////////////////////// - - CompilerKit::Encoder64x0 asm64; - - while (std::getline(file_ptr, line)) { - if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) { - detail::print_error(ln, argv[i]); - continue; - } - - try { - asm_read_attributes(line); - asm64.WriteLine(line, argv[i]); - } catch (const std::exception &e) { - if (kVerbose) { - std::string what = e.what(); - detail::print_warning("exit because of: " + what, "64asm"); - } - - std::filesystem::remove(object_output); - goto asm_fail_exit; - } - } - - if (!kOutputAsBinary) { - if (kVerbose) { - kStdOut << "64asm: 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 << "64asm: At least one record is needed to write an object " - "file.\n64asm: Make one using `export .code64 foo_bar`.\n"; - - std::filesystem::remove(object_output); - return -1; - } - - kRecords[kRecords.size() - 1].fSize = kBytes.size(); - - std::size_t record_count = 0UL; - - for (auto &rec : kRecords) { - if (kVerbose) - kStdOut << "64asm: Wrote record " << rec.fName << " to file...\n"; - - rec.fFlags |= CompilerKit::kKindRelocationAtRuntime; - rec.fOffset = record_count; - ++record_count; - - file_ptr_out << rec; - } - - // increment once again, so that we won't lie about the kUndefinedSymbols. - ++record_count; - - for (auto &sym : kUndefinedSymbols) { - CompilerKit::AERecordHeader _record_hdr{0}; - - if (kVerbose) - kStdOut << "64asm: Wrote symbol " << sym << " to file...\n"; - - _record_hdr.fKind = kAEInvalidOpcode; - _record_hdr.fSize = sym.size(); - _record_hdr.fOffset = record_count; - - ++record_count; - - memset(_record_hdr.fPad, kAEInvalidOpcode, kAEPad); - memcpy(_record_hdr.fName, sym.c_str(), sym.size()); - - file_ptr_out << _record_hdr; - - ++kCounter; - } - - auto pos_end = file_ptr_out.tellp(); - - file_ptr_out.seekp(pos); - - hdr.fStartCode = pos_end; - hdr.fCodeSize = kBytes.size(); - - file_ptr_out << hdr; - - file_ptr_out.seekp(pos_end); - } else { - if (kVerbose) { - kStdOut << "64asm: Write raw binary...\n"; - } - } - - // byte from byte, we write this. - for (auto &byte : kBytes) { - file_ptr_out.write(reinterpret_cast(&byte), sizeof(byte)); - } - - if (kVerbose) kStdOut << "64asm: Wrote file with program in it.\n"; - - file_ptr_out.flush(); - file_ptr_out.close(); - - if (kVerbose) kStdOut << "64asm: Exit succeeded.\n"; - - return 0; - } - -asm_fail_exit: - - if (kVerbose) kStdOut << "64asm: Exit failed.\n"; - - return -1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief Check for attributes -// returns true if any was found. - -///////////////////////////////////////////////////////////////////////////////////////// - -static bool asm_read_attributes(std::string &line) { - // import is the opposite of export, it signals to the ld - // that we need this symbol. - if (ParserKit::find_word(line, "import")) { - if (kOutputAsBinary) { - detail::print_error("Invalid import directive in flat binary mode.", - "64asm"); - throw std::runtime_error("invalid_import_bin"); - } - - auto name = line.substr(line.find("import") + strlen("import")); - - /// sanity check to avoid stupid linker errors. - if (name.size() == 0) { - detail::print_error("Invalid import", "ppcasm"); - throw std::runtime_error("invalid_import"); - } - - std::string result = std::to_string(name.size()); - result += kUndefinedSymbol; - - // mangle this - for (char &j : name) { - if (j == ' ' || j == ',') j = '$'; - } - - result += name; - - if (name.find(".code64") != std::string::npos) { - // data is treated as code. - kCurrentRecord.fKind = CompilerKit::kPefCode; - } else if (name.find(".data64") != std::string::npos) { - // no code will be executed from here. - kCurrentRecord.fKind = CompilerKit::kPefData; - } else if (name.find(".zero64") != std::string::npos) { - // this is a bss section. - kCurrentRecord.fKind = CompilerKit::kPefZero; - } - - // this is a special case for the start stub. - // we want this so that ld can find it. - - if (name == kPefStart) { - kCurrentRecord.fKind = CompilerKit::kPefCode; - } - - // now we can tell the code size of the previous kCurrentRecord. - - if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); - - memset(kCurrentRecord.fName, 0, kAESymbolLen); - memcpy(kCurrentRecord.fName, result.c_str(), result.size()); - - ++kCounter; - - memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); - - kRecords.emplace_back(kCurrentRecord); - - return true; - } - // export is a special keyword used by 64asm to tell the AE output stage to - // mark this section as a header. it currently supports .code64, .data64., - // .zero64 - else if (ParserKit::find_word(line, "export")) { - if (kOutputAsBinary) { - detail::print_error("Invalid export directive in flat binary mode.", - "64asm"); - throw std::runtime_error("invalid_export_bin"); - } - - auto name = line.substr(line.find("export") + strlen("export")); - - std::string name_copy = name; - - for (char &j : name) { - if (j == ' ') j = '$'; - } - - if (name.find(".code64") != std::string::npos) { - // data is treated as code. - - name_copy.erase(name_copy.find(".code64"), strlen(".code64")); - kCurrentRecord.fKind = CompilerKit::kPefCode; - } else if (name.find(".data64") != std::string::npos) { - // no code will be executed from here. - - name_copy.erase(name_copy.find(".data64"), strlen(".data64")); - kCurrentRecord.fKind = CompilerKit::kPefData; - } else if (name.find(".zero64") != std::string::npos) { - // this is a bss section. - - name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); - kCurrentRecord.fKind = CompilerKit::kPefZero; - } - - // this is a special case for the start stub. - // we want this so that ld can find it. - - if (name == kPefStart) { - kCurrentRecord.fKind = CompilerKit::kPefCode; - } - - while (name_copy.find(" ") != std::string::npos) - name_copy.erase(name_copy.find(" "), 1); - - kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); - ++kOrigin; - - // now we can tell the code size of the previous kCurrentRecord. - - if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); - - memset(kCurrentRecord.fName, 0, kAESymbolLen); - memcpy(kCurrentRecord.fName, name.c_str(), name.size()); - - ++kCounter; - - memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); - - kRecords.emplace_back(kCurrentRecord); - - return true; - } - - return false; -} - -// \brief algorithms and helpers. - -namespace detail::algorithm { -// \brief authorize a brief set of characters. -static inline bool is_not_alnum_space(char c) { - return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || - (c == ',') || (c == '(') || (c == ')') || (c == '"') || - (c == '\'') || (c == '[') || (c == ']') || (c == '+') || - (c == '_') || (c == ':') || (c == '@') || (c == '.')); -} - -bool is_valid(const std::string &str) { - return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); -} -} // namespace detail::algorithm - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief Check for line (syntax check) - -///////////////////////////////////////////////////////////////////////////////////////// - -std::string CompilerKit::Encoder64x0::CheckLine(std::string &line, - const std::string &file) { - std::string err_str; - - if (line.empty() || ParserKit::find_word(line, "import") || - ParserKit::find_word(line, "export") || - line.find('#') != std::string::npos || ParserKit::find_word(line, ";")) { - if (line.find('#') != std::string::npos) { - line.erase(line.find('#')); - } else if (line.find(';') != std::string::npos) { - line.erase(line.find(';')); - } else { - // now check the line for validity - if (!detail::algorithm::is_valid(line)) { - err_str = "Line contains non alphanumeric characters.\nhere -> "; - err_str += line; - } - } - - return err_str; - } - - if (!detail::algorithm::is_valid(line)) { - err_str = "Line contains non alphanumeric characters.\nhere -> "; - err_str += line; - - return err_str; - } - - // check for a valid instruction format. - - if (line.find(',') != std::string::npos) { - if (line.find(',') + 1 == line.size()) { - err_str += "\nInstruction lacks right register, here -> "; - err_str += line.substr(line.find(',')); - - return err_str; - } else { - bool nothing_on_right = true; - - if (line.find(',') + 1 > line.size()) { - err_str += "\nInstruction not complete, here -> "; - err_str += line; - - return err_str; - } - - auto substr = line.substr(line.find(',') + 1); - - for (auto &ch : substr) { - if (ch != ' ' && ch != '\t') { - nothing_on_right = false; - } - } - - // this means we found nothing after that ',' . - if (nothing_on_right) { - err_str += "\nInstruction not complete, here -> "; - err_str += line; - - return err_str; - } - } - } - - // these do take an argument. - std::vector operands_inst = {"stw", "ldw", "lda", "sta"}; - - // these don't. - std::vector filter_inst = {"jlr", "jrl", "int"}; - - for (auto &opcode64x0 : kOpcodes64x0) { - if (line.find(opcode64x0.fName) != std::string::npos) { - if (opcode64x0.fFunct7 == kAsmNoArgs) return err_str; - - for (auto &op : operands_inst) { - // if only the instruction was found. - if (line == op) { - err_str += "\nMalformed "; - err_str += op; - err_str += " instruction, here -> "; - err_str += line; - } - } - - // if it is like that -> addr1, 0x0 - if (auto it = std::find(filter_inst.begin(), filter_inst.end(), - opcode64x0.fName); - it == filter_inst.cend()) { - if (ParserKit::find_word(line, opcode64x0.fName)) { - if (!isspace(line[line.find(opcode64x0.fName) + - strlen(opcode64x0.fName)])) { - err_str += "\nMissing space between "; - err_str += opcode64x0.fName; - err_str += " and operands.\nhere -> "; - err_str += line; - } - } - } - - return err_str; - } - } - - err_str += "Unrecognized instruction: " + line; - - return err_str; -} - -bool CompilerKit::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) { - detail::print_error("invalid hex number: " + jump_label, "64asm"); - throw std::runtime_error("invalid_hex_number"); - } - } - - CompilerKit::NumberCast64 num( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); - - for (char &i : num.number) { - kBytes.push_back(i); - } - - if (kVerbose) { - kStdOut << "64asm: found a base 16 number here: " - << jump_label.substr(pos) << "\n"; - } - - return true; - } - case 'b': { - if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); - !res) { - if (errno != 0) { - detail::print_error("invalid binary number: " + jump_label, "64asm"); - throw std::runtime_error("invalid_bin"); - } - } - - CompilerKit::NumberCast64 num( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); - - if (kVerbose) { - kStdOut << "64asm: found a base 2 number here: " - << jump_label.substr(pos) << "\n"; - } - - for (char &i : num.number) { - kBytes.push_back(i); - } - - return true; - } - case 'o': { - if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); - !res) { - if (errno != 0) { - detail::print_error("invalid octal number: " + jump_label, "64asm"); - throw std::runtime_error("invalid_octal"); - } - } - - CompilerKit::NumberCast64 num( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); - - if (kVerbose) { - kStdOut << "64asm: found a base 8 number here: " - << jump_label.substr(pos) << "\n"; - } - - for (char &i : num.number) { - kBytes.push_back(i); - } - - return true; - } - default: { - break; - } - } - - /* check for errno and stuff like that */ - if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { - if (errno != 0) { - return false; - } - } - - CompilerKit::NumberCast64 num( - strtol(jump_label.substr(pos).c_str(), nullptr, 10)); - - for (char &i : num.number) { - kBytes.push_back(i); - } - - if (kVerbose) { - kStdOut << "64asm: found a base 10 number here: " << jump_label.substr(pos) - << "\n"; - } - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief Read and write an instruction to the output array. - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CompilerKit::Encoder64x0::WriteLine(std::string &line, - const std::string &file) { - if (ParserKit::find_word(line, "export ")) return true; - - for (auto &opcode64x0 : kOpcodes64x0) { - // strict check here - if (ParserKit::find_word(line, opcode64x0.fName) && - detail::algorithm::is_valid(line)) { - std::string name(opcode64x0.fName); - std::string jump_label, cpy_jump_label; - - kBytes.emplace_back(opcode64x0.fOpcode); - kBytes.emplace_back(opcode64x0.fFunct3); - kBytes.emplace_back(opcode64x0.fFunct7); - - // check funct7 type. - switch (opcode64x0.fFunct7) { - // reg to reg means register to register transfer operation. - case kAsmRegToReg: - case kAsmImmediate: { - // \brief how many registers we found. - std::size_t found_some = 0UL; - - for (size_t line_index = 0UL; line_index < line.size(); - line_index++) { - if (line[line_index] == kAsmRegisterPrefix[0] && - isdigit(line[line_index + 1])) { - std::string register_syntax = kAsmRegisterPrefix; - register_syntax += line[line_index + 1]; - - if (isdigit(line[line_index + 2])) - register_syntax += line[line_index + 2]; - - std::string reg_str; - reg_str += line[line_index + 1]; - - if (isdigit(line[line_index + 2])) - reg_str += line[line_index + 2]; - - // it ranges from r0 to r19 - // something like r190 doesn't exist in the instruction set. - if (kOutputArch == CompilerKit::kPefArch64000) { - if (isdigit(line[line_index + 3]) && - isdigit(line[line_index + 2])) { - reg_str += line[line_index + 3]; - detail::print_error( - "invalid register index, r" + reg_str + - "\nnote: The 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) { - detail::print_error("invalid register index, r" + reg_str, - file); - throw std::runtime_error("invalid_register_index"); - } - - kBytes.emplace_back(reg_index); - ++found_some; - - if (kVerbose) { - kStdOut << "64asm: Register found: " << register_syntax << "\n"; - kStdOut << "64asm: Register amount in instruction: " - << found_some << "\n"; - } - } - } - - // we're not in immediate addressing, reg to reg. - if (opcode64x0.fFunct7 != kAsmImmediate) { - // remember! register to register! - if (found_some == 1) { - detail::print_error( - "Too few registers.\ntip: each 64asm register " - "starts with 'r'.\nline: " + - line, - file); - throw std::runtime_error("not_a_register"); - } - } - - if (found_some < 1 && name != "ldw" && name != "lda" && - name != "stw") { - detail::print_error( - "invalid combination of opcode and registers.\nline: " + line, - file); - throw std::runtime_error("invalid_comb_op_reg"); - } else if (found_some == 1 && name == "add") { - detail::print_error( - "invalid combination of opcode and registers.\nline: " + line, - file); - throw std::runtime_error("invalid_comb_op_reg"); - } else if (found_some == 1 && name == "sub") { - detail::print_error( - "invalid combination of opcode and registers.\nline: " + line, - file); - throw std::runtime_error("invalid_comb_op_reg"); - } - - if (found_some > 0 && name == "pop") { - detail::print_error( - "invalid combination for opcode 'pop'.\ntip: it expects " - "nothing.\nline: " + - line, - file); - throw std::runtime_error("invalid_comb_op_pop"); - } - } - default: - break; - } - - // try to fetch a number from the name - if (name == "stw" || name == "ldw" || name == "lda" || name == "sta") { - auto where_string = name; - - // if we load something, we'd need it's symbol/literal - if (name == "stw" || name == "sta" || name == "ldw" || name == "lda" || - name == "sta") - where_string = ","; - - jump_label = line; - - auto found_sym = false; - - while (jump_label.find(where_string) != std::string::npos) { - jump_label = jump_label.substr(jump_label.find(where_string) + - where_string.size()); - - while (jump_label.find(" ") != std::string::npos) { - jump_label.erase(jump_label.find(" "), 1); - } - - if (jump_label[0] != kAsmRegisterPrefix[0] && - !isdigit(jump_label[1])) { - if (found_sym) { - detail::print_error( - "invalid combination of opcode and operands.\nhere -> " + - jump_label, - file); - throw std::runtime_error("invalid_comb_op_ops"); - } else { - // death trap installed. - found_sym = true; - } - } - } - - cpy_jump_label = jump_label; - - // replace any spaces with $ - if (jump_label[0] == ' ') { - while (jump_label.find(' ') != std::string::npos) { - if (isalnum(jump_label[0]) || isdigit(jump_label[0])) break; - - jump_label.erase(jump_label.find(' '), 1); - } - } - - if (!this->WriteNumber(0, jump_label)) { - // sta expects this: sta 0x000000, r0 - if (name == "sta") { - detail::print_error( - "invalid combination of opcode and operands.\nHere ->" + line, - file); - throw std::runtime_error("invalid_comb_op_ops"); - } - } else { - if (name == "sta" && - cpy_jump_label.find("import ") != std::string::npos) { - detail::print_error("invalid usage import on 'sta', here: " + line, - file); - throw std::runtime_error("invalid_sta_usage"); - } - } - - goto asm_write_label; - } - - // This is the case where we jump to a label, it is also used as a goto. - if (name == "lda" || name == "sta") { - asm_write_label: - if (cpy_jump_label.find('\n') != std::string::npos) - cpy_jump_label.erase(cpy_jump_label.find('\n'), 1); - - if (cpy_jump_label.find("import") != std::string::npos) { - cpy_jump_label.erase(cpy_jump_label.find("import"), strlen("import")); - - if (name == "sta") { - detail::print_error("import is not allowed on a sta operation.", - file); - throw std::runtime_error("import_sta_op"); - } else { - goto asm_end_label_cpy; - } - } - - if (name == "lda" || name == "sta") { - for (auto &label : kOriginLabel) { - if (cpy_jump_label == label.first) { - if (kVerbose) { - kStdOut << "64asm: Replace label " << cpy_jump_label - << " to address: " << label.second << std::endl; - } - - CompilerKit::NumberCast64 num(label.second); - - for (auto &num : num.number) { - kBytes.push_back(num); - } - - goto asm_end_label_cpy; - } - } - - if (cpy_jump_label[0] == '0') { - switch (cpy_jump_label[1]) { - case 'x': - case 'o': - case 'b': - if (this->WriteNumber(0, cpy_jump_label)) - goto asm_end_label_cpy; - - break; - default: - break; - } - - if (isdigit(cpy_jump_label[0])) { - if (this->WriteNumber(0, cpy_jump_label)) goto asm_end_label_cpy; - - break; - } - } - } - - if (cpy_jump_label.size() < 1) { - detail::print_error("label is empty, can't jump on it.", file); - throw std::runtime_error("label_empty"); - } - - /// don't go any further if: - /// load word (ldw) or store word. (stw) - - if (name == "ldw" || name == "stw") break; - - auto mld_reloc_str = std::to_string(cpy_jump_label.size()); - mld_reloc_str += kUndefinedSymbol; - mld_reloc_str += cpy_jump_label; - - bool ignore_back_slash = false; - - for (auto &reloc_chr : mld_reloc_str) { - if (reloc_chr == '\\') { - ignore_back_slash = true; - continue; - } - - if (ignore_back_slash) { - ignore_back_slash = false; - continue; - } - - kBytes.push_back(reloc_chr); - } - - kBytes.push_back('\0'); - goto asm_end_label_cpy; - } - - asm_end_label_cpy: - kOrigin += c64x0IPAlignment; - - break; - } - } - - return true; -} - -// Last rev 13-1-24 diff --git a/Sources/64asm.cxx b/Sources/64asm.cxx new file mode 100644 index 0000000..777f448 --- /dev/null +++ b/Sources/64asm.cxx @@ -0,0 +1,954 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +/// bugs: 0 + +///////////////////////////////////////////////////////////////////////////////////////// + +// @file 64asm.cxx +// @author Amlal EL Mahrouss +// @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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////// + +// 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 = CompilerKit::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> kOriginLabel; + +static bool kVerbose = false; + +static std::vector kBytes; + +static CompilerKit::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = CompilerKit::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector kRecords; +static std::vector kUndefinedSymbols; + +static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; +static const std::string kRelocSymbol = ":RuntimeSymbol:"; + +// \brief forward decl. +static bool asm_read_attributes(std::string &line); + +namespace detail { +void print_error(std::string reason, const std::string &file) noexcept { + if (reason[0] == '\n') reason.erase(0, 1); + + kStdErr << kRed << "[ 64asm ] " << kWhite + << ((file == "64asm") ? "internal assembler error " + : ("in file, " + file)) + << kBlank << std::endl; + kStdErr << kRed << "[ 64asm ] " << kWhite << reason << kBlank << std::endl; + + if (kAcceptableErrors > kErrorLimit) std::exit(3); + + ++kAcceptableErrors; +} + +void print_warning(std::string reason, const std::string &file) noexcept { + if (reason[0] == '\n') reason.erase(0, 1); + + if (!file.empty()) { + kStdOut << kYellow << "[ file ] " << kWhite << file << kBlank << std::endl; + } + + kStdOut << kYellow << "[ 64asm ] " << kWhite << reason << kBlank << std::endl; +} +} // namespace detail + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief 64x0 assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +NDK_MODULE(NewOSAssembler64000) { + for (size_t i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + if (strcmp(argv[i], "-version") == 0 || strcmp(argv[i], "-v") == 0) { + kStdOut << "64asm: 64x0 Assembler.\n64asm: v1.10\n64asm: Copyright (c) " + "ZKA Technologies.\n"; + return 0; + } else if (strcmp(argv[i], "-h") == 0) { + kStdOut << "64asm: 64x0 Assembler.\n64asm: 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 << "64asm: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) { + kStdOut << "64asm: 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 << "64asm: error: " << strerror(errno) << "\n"; + } + } + + std::string line; + + CompilerKit::AEHeader hdr{0}; + + memset(hdr.fPad, kAEInvalidOpcode, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(CompilerKit::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + CompilerKit::Encoder64x0 asm64; + + while (std::getline(file_ptr, line)) { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) { + detail::print_error(ln, argv[i]); + continue; + } + + try { + asm_read_attributes(line); + asm64.WriteLine(line, argv[i]); + } catch (const std::exception &e) { + if (kVerbose) { + std::string what = e.what(); + detail::print_warning("exit because of: " + what, "64asm"); + } + + std::filesystem::remove(object_output); + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) { + if (kVerbose) { + kStdOut << "64asm: 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 << "64asm: At least one record is needed to write an object " + "file.\n64asm: Make one using `export .code64 foo_bar`.\n"; + + std::filesystem::remove(object_output); + return -1; + } + + kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + std::size_t record_count = 0UL; + + for (auto &rec : kRecords) { + if (kVerbose) + kStdOut << "64asm: Wrote record " << rec.fName << " to file...\n"; + + rec.fFlags |= CompilerKit::kKindRelocationAtRuntime; + rec.fOffset = record_count; + ++record_count; + + file_ptr_out << rec; + } + + // increment once again, so that we won't lie about the kUndefinedSymbols. + ++record_count; + + for (auto &sym : kUndefinedSymbols) { + CompilerKit::AERecordHeader _record_hdr{0}; + + if (kVerbose) + kStdOut << "64asm: Wrote symbol " << sym << " to file...\n"; + + _record_hdr.fKind = kAEInvalidOpcode; + _record_hdr.fSize = sym.size(); + _record_hdr.fOffset = record_count; + + ++record_count; + + memset(_record_hdr.fPad, kAEInvalidOpcode, kAEPad); + memcpy(_record_hdr.fName, sym.c_str(), sym.size()); + + file_ptr_out << _record_hdr; + + ++kCounter; + } + + auto pos_end = file_ptr_out.tellp(); + + file_ptr_out.seekp(pos); + + hdr.fStartCode = pos_end; + hdr.fCodeSize = kBytes.size(); + + file_ptr_out << hdr; + + file_ptr_out.seekp(pos_end); + } else { + if (kVerbose) { + kStdOut << "64asm: Write raw binary...\n"; + } + } + + // byte from byte, we write this. + for (auto &byte : kBytes) { + file_ptr_out.write(reinterpret_cast(&byte), sizeof(byte)); + } + + if (kVerbose) kStdOut << "64asm: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) kStdOut << "64asm: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) kStdOut << "64asm: Exit failed.\n"; + + return -1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string &line) { + // import is the opposite of export, it signals to the ld + // that we need this symbol. + if (ParserKit::find_word(line, "import")) { + if (kOutputAsBinary) { + detail::print_error("Invalid import directive in flat binary mode.", + "64asm"); + throw std::runtime_error("invalid_import_bin"); + } + + auto name = line.substr(line.find("import") + strlen("import")); + + /// sanity check to avoid stupid linker errors. + if (name.size() == 0) { + detail::print_error("Invalid import", "ppcasm"); + throw std::runtime_error("invalid_import"); + } + + std::string result = std::to_string(name.size()); + result += kUndefinedSymbol; + + // mangle this + for (char &j : name) { + if (j == ' ' || j == ',') j = '$'; + } + + result += name; + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + kCurrentRecord.fKind = CompilerKit::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + kCurrentRecord.fKind = CompilerKit::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + kCurrentRecord.fKind = CompilerKit::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = CompilerKit::kPefCode; + } + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, result.c_str(), result.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // export is a special keyword used by 64asm to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64., + // .zero64 + else if (ParserKit::find_word(line, "export")) { + if (kOutputAsBinary) { + detail::print_error("Invalid export directive in flat binary mode.", + "64asm"); + throw std::runtime_error("invalid_export_bin"); + } + + auto name = line.substr(line.find("export") + strlen("export")); + + std::string name_copy = name; + + for (char &j : name) { + if (j == ' ') j = '$'; + } + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + + name_copy.erase(name_copy.find(".code64"), strlen(".code64")); + kCurrentRecord.fKind = CompilerKit::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + + name_copy.erase(name_copy.find(".data64"), strlen(".data64")); + kCurrentRecord.fKind = CompilerKit::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = CompilerKit::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = CompilerKit::kPefCode; + } + + while (name_copy.find(" ") != std::string::npos) + name_copy.erase(name_copy.find(" "), 1); + + kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); + ++kOrigin; + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, name.c_str(), name.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace detail::algorithm { +// \brief authorize a brief set of characters. +static inline bool is_not_alnum_space(char c) { + return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || + (c == ',') || (c == '(') || (c == ')') || (c == '"') || + (c == '\'') || (c == '[') || (c == ']') || (c == '+') || + (c == '_') || (c == ':') || (c == '@') || (c == '.')); +} + +bool is_valid(const std::string &str) { + return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); +} +} // namespace detail::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string CompilerKit::Encoder64x0::CheckLine(std::string &line, + const std::string &file) { + std::string err_str; + + if (line.empty() || ParserKit::find_word(line, "import") || + ParserKit::find_word(line, "export") || + line.find('#') != std::string::npos || ParserKit::find_word(line, ";")) { + if (line.find('#') != std::string::npos) { + line.erase(line.find('#')); + } else if (line.find(';') != std::string::npos) { + line.erase(line.find(';')); + } else { + // now check the line for validity + if (!detail::algorithm::is_valid(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + } + } + + return err_str; + } + + if (!detail::algorithm::is_valid(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + + return err_str; + } + + // check for a valid instruction format. + + if (line.find(',') != std::string::npos) { + if (line.find(',') + 1 == line.size()) { + err_str += "\nInstruction lacks right register, here -> "; + err_str += line.substr(line.find(',')); + + return err_str; + } else { + bool nothing_on_right = true; + + if (line.find(',') + 1 > line.size()) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + + auto substr = line.substr(line.find(',') + 1); + + for (auto &ch : substr) { + if (ch != ' ' && ch != '\t') { + nothing_on_right = false; + } + } + + // this means we found nothing after that ',' . + if (nothing_on_right) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + } + } + + // these do take an argument. + std::vector operands_inst = {"stw", "ldw", "lda", "sta"}; + + // these don't. + std::vector filter_inst = {"jlr", "jrl", "int"}; + + for (auto &opcode64x0 : kOpcodes64x0) { + if (line.find(opcode64x0.fName) != std::string::npos) { + if (opcode64x0.fFunct7 == kAsmNoArgs) return err_str; + + for (auto &op : operands_inst) { + // if only the instruction was found. + if (line == op) { + err_str += "\nMalformed "; + err_str += op; + err_str += " instruction, here -> "; + err_str += line; + } + } + + // if it is like that -> addr1, 0x0 + if (auto it = std::find(filter_inst.begin(), filter_inst.end(), + opcode64x0.fName); + it == filter_inst.cend()) { + if (ParserKit::find_word(line, opcode64x0.fName)) { + if (!isspace(line[line.find(opcode64x0.fName) + + strlen(opcode64x0.fName)])) { + err_str += "\nMissing space between "; + err_str += opcode64x0.fName; + err_str += " and operands.\nhere -> "; + err_str += line; + } + } + } + + return err_str; + } + } + + err_str += "Unrecognized instruction: " + line; + + return err_str; +} + +bool CompilerKit::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) { + detail::print_error("invalid hex number: " + jump_label, "64asm"); + throw std::runtime_error("invalid_hex_number"); + } + } + + CompilerKit::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char &i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "64asm: found a base 16 number here: " + << jump_label.substr(pos) << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); + !res) { + if (errno != 0) { + detail::print_error("invalid binary number: " + jump_label, "64asm"); + throw std::runtime_error("invalid_bin"); + } + } + + CompilerKit::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) { + kStdOut << "64asm: found a base 2 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char &i : num.number) { + kBytes.push_back(i); + } + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); + !res) { + if (errno != 0) { + detail::print_error("invalid octal number: " + jump_label, "64asm"); + throw std::runtime_error("invalid_octal"); + } + } + + CompilerKit::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) { + kStdOut << "64asm: found a base 8 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char &i : num.number) { + kBytes.push_back(i); + } + + return true; + } + default: { + break; + } + } + + /* check for errno and stuff like that */ + if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { + if (errno != 0) { + return false; + } + } + + CompilerKit::NumberCast64 num( + strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char &i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "64asm: found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerKit::Encoder64x0::WriteLine(std::string &line, + const std::string &file) { + if (ParserKit::find_word(line, "export ")) return true; + + for (auto &opcode64x0 : kOpcodes64x0) { + // strict check here + if (ParserKit::find_word(line, opcode64x0.fName) && + detail::algorithm::is_valid(line)) { + std::string name(opcode64x0.fName); + std::string jump_label, cpy_jump_label; + + kBytes.emplace_back(opcode64x0.fOpcode); + kBytes.emplace_back(opcode64x0.fFunct3); + kBytes.emplace_back(opcode64x0.fFunct7); + + // check funct7 type. + switch (opcode64x0.fFunct7) { + // reg to reg means register to register transfer operation. + case kAsmRegToReg: + case kAsmImmediate: { + // \brief how many registers we found. + std::size_t found_some = 0UL; + + for (size_t line_index = 0UL; line_index < line.size(); + line_index++) { + if (line[line_index] == kAsmRegisterPrefix[0] && + isdigit(line[line_index + 1])) { + std::string register_syntax = kAsmRegisterPrefix; + register_syntax += line[line_index + 1]; + + if (isdigit(line[line_index + 2])) + register_syntax += line[line_index + 2]; + + std::string reg_str; + reg_str += line[line_index + 1]; + + if (isdigit(line[line_index + 2])) + reg_str += line[line_index + 2]; + + // it ranges from r0 to r19 + // something like r190 doesn't exist in the instruction set. + if (kOutputArch == CompilerKit::kPefArch64000) { + if (isdigit(line[line_index + 3]) && + isdigit(line[line_index + 2])) { + reg_str += line[line_index + 3]; + detail::print_error( + "invalid register index, r" + reg_str + + "\nnote: The 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) { + detail::print_error("invalid register index, r" + reg_str, + file); + throw std::runtime_error("invalid_register_index"); + } + + kBytes.emplace_back(reg_index); + ++found_some; + + if (kVerbose) { + kStdOut << "64asm: Register found: " << register_syntax << "\n"; + kStdOut << "64asm: Register amount in instruction: " + << found_some << "\n"; + } + } + } + + // we're not in immediate addressing, reg to reg. + if (opcode64x0.fFunct7 != kAsmImmediate) { + // remember! register to register! + if (found_some == 1) { + detail::print_error( + "Too few registers.\ntip: each 64asm register " + "starts with 'r'.\nline: " + + line, + file); + throw std::runtime_error("not_a_register"); + } + } + + if (found_some < 1 && name != "ldw" && name != "lda" && + name != "stw") { + detail::print_error( + "invalid combination of opcode and registers.\nline: " + line, + file); + throw std::runtime_error("invalid_comb_op_reg"); + } else if (found_some == 1 && name == "add") { + detail::print_error( + "invalid combination of opcode and registers.\nline: " + line, + file); + throw std::runtime_error("invalid_comb_op_reg"); + } else if (found_some == 1 && name == "sub") { + detail::print_error( + "invalid combination of opcode and registers.\nline: " + line, + file); + throw std::runtime_error("invalid_comb_op_reg"); + } + + if (found_some > 0 && name == "pop") { + detail::print_error( + "invalid combination for opcode 'pop'.\ntip: it expects " + "nothing.\nline: " + + line, + file); + throw std::runtime_error("invalid_comb_op_pop"); + } + } + default: + break; + } + + // try to fetch a number from the name + if (name == "stw" || name == "ldw" || name == "lda" || name == "sta") { + auto where_string = name; + + // if we load something, we'd need it's symbol/literal + if (name == "stw" || name == "sta" || name == "ldw" || name == "lda" || + name == "sta") + where_string = ","; + + jump_label = line; + + auto found_sym = false; + + while (jump_label.find(where_string) != std::string::npos) { + jump_label = jump_label.substr(jump_label.find(where_string) + + where_string.size()); + + while (jump_label.find(" ") != std::string::npos) { + jump_label.erase(jump_label.find(" "), 1); + } + + if (jump_label[0] != kAsmRegisterPrefix[0] && + !isdigit(jump_label[1])) { + if (found_sym) { + detail::print_error( + "invalid combination of opcode and operands.\nhere -> " + + jump_label, + file); + throw std::runtime_error("invalid_comb_op_ops"); + } else { + // death trap installed. + found_sym = true; + } + } + } + + cpy_jump_label = jump_label; + + // replace any spaces with $ + if (jump_label[0] == ' ') { + while (jump_label.find(' ') != std::string::npos) { + if (isalnum(jump_label[0]) || isdigit(jump_label[0])) break; + + jump_label.erase(jump_label.find(' '), 1); + } + } + + if (!this->WriteNumber(0, jump_label)) { + // sta expects this: sta 0x000000, r0 + if (name == "sta") { + detail::print_error( + "invalid combination of opcode and operands.\nHere ->" + line, + file); + throw std::runtime_error("invalid_comb_op_ops"); + } + } else { + if (name == "sta" && + cpy_jump_label.find("import ") != std::string::npos) { + detail::print_error("invalid usage import on 'sta', here: " + line, + file); + throw std::runtime_error("invalid_sta_usage"); + } + } + + goto asm_write_label; + } + + // This is the case where we jump to a label, it is also used as a goto. + if (name == "lda" || name == "sta") { + asm_write_label: + if (cpy_jump_label.find('\n') != std::string::npos) + cpy_jump_label.erase(cpy_jump_label.find('\n'), 1); + + if (cpy_jump_label.find("import") != std::string::npos) { + cpy_jump_label.erase(cpy_jump_label.find("import"), strlen("import")); + + if (name == "sta") { + detail::print_error("import is not allowed on a sta operation.", + file); + throw std::runtime_error("import_sta_op"); + } else { + goto asm_end_label_cpy; + } + } + + if (name == "lda" || name == "sta") { + for (auto &label : kOriginLabel) { + if (cpy_jump_label == label.first) { + if (kVerbose) { + kStdOut << "64asm: Replace label " << cpy_jump_label + << " to address: " << label.second << std::endl; + } + + CompilerKit::NumberCast64 num(label.second); + + for (auto &num : num.number) { + kBytes.push_back(num); + } + + goto asm_end_label_cpy; + } + } + + if (cpy_jump_label[0] == '0') { + switch (cpy_jump_label[1]) { + case 'x': + case 'o': + case 'b': + if (this->WriteNumber(0, cpy_jump_label)) + goto asm_end_label_cpy; + + break; + default: + break; + } + + if (isdigit(cpy_jump_label[0])) { + if (this->WriteNumber(0, cpy_jump_label)) goto asm_end_label_cpy; + + break; + } + } + } + + if (cpy_jump_label.size() < 1) { + detail::print_error("label is empty, can't jump on it.", file); + throw std::runtime_error("label_empty"); + } + + /// don't go any further if: + /// load word (ldw) or store word. (stw) + + if (name == "ldw" || name == "stw") break; + + auto mld_reloc_str = std::to_string(cpy_jump_label.size()); + mld_reloc_str += kUndefinedSymbol; + mld_reloc_str += cpy_jump_label; + + bool ignore_back_slash = false; + + for (auto &reloc_chr : mld_reloc_str) { + if (reloc_chr == '\\') { + ignore_back_slash = true; + continue; + } + + if (ignore_back_slash) { + ignore_back_slash = false; + continue; + } + + kBytes.push_back(reloc_chr); + } + + kBytes.push_back('\0'); + goto asm_end_label_cpy; + } + + asm_end_label_cpy: + kOrigin += c64x0IPAlignment; + + break; + } + } + + return true; +} + +// Last rev 13-1-24 diff --git a/Sources/64x0-cc.cc b/Sources/64x0-cc.cc deleted file mode 100644 index efbf68f..0000000 --- a/Sources/64x0-cc.cc +++ /dev/null @@ -1,1627 +0,0 @@ -/* - * ======================================================== - * - * cc - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -/// BUGS: ? -/// TODO: - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* C driver */ -/* This is part of the NDK. */ -/* (c) ZKA Technologies */ - -/// @author Amlal El Mahrouss (amlel) -/// @file 64x0-cc.cc -/// @brief 64x0 C Compiler. - -/// TODO: support structures, else if, else, . and -> - -///////////////////// - -// ANSI ESCAPE CODES - -///////////////////// - -#define kOk (0) - -#define kBlank "\e[0;30m" -#define kRed "\e[0;31m" -#define kWhite "\e[0;97m" - -///////////////////////////////////// - -// INTERNAL STUFF OF THE C COMPILER - -///////////////////////////////////// - -namespace detail -{ - // \brief name to register struct. - struct CompilerRegisterMap final - { - std::string fName; - std::string fReg; - }; - - // \brief Map for C structs - // \author amlel - struct CompilerStructMap final - { - // 'my_foo' - std::string fName; - - // if instance: stores a valid register. - std::string fReg; - - // offset count - std::size_t fOffsetsCnt; - - // offset array. - std::vector> fOffsets; - }; - - struct CompilerState final - { - std::vector fSyntaxTreeList; - std::vector kStackFrame; - std::vector kStructMap; - ParserKit::SyntaxLeafList* fSyntaxTree{nullptr}; - std::unique_ptr fOutputAssembly; - std::string fLastFile; - std::string fLastError; - bool fVerbose; - }; -} // namespace detail - -static detail::CompilerState kState; -static SizeType kErrorLimit = 100; -static std::string kIfFunction = ""; -static Int32 kAcceptableErrors = 0; - -namespace detail -{ - /// @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 - { - if (reason[0] == '\n') - reason.erase(0, 1); - - if (file.find(".pp") != std::string::npos) - { - file.erase(file.find(".pp"), 3); - } - - if (kState.fLastFile != file) - { - std::cout << kRed << "[ cc ] " << kWhite - << ((file == "cc") ? "internal compiler error " - : ("in file, " + file)) - << kBlank << std::endl; - std::cout << kRed << "[ cc ] " << kWhite << reason << kBlank << std::endl; - - kState.fLastFile = file; - } - else - { - std::cout << kRed << "[ cc ] [ " << kState.fLastFile << " ] " << kWhite - << reason << kBlank << std::endl; - } - - if (kAcceptableErrors > kErrorLimit) - std::exit(3); - - ++kAcceptableErrors; - } - - struct CompilerType final - { - std::string fName; - std::string fValue; - }; -} // namespace detail - -///////////////////////////////////////////////////////////////////////////////////////// - -// Target architecture. -static int kMachine = 0; - -///////////////////////////////////////// - -// REGISTERS ACCORDING TO USED ASSEMBLER - -///////////////////////////////////////// - -static size_t kRegisterCnt = kAsmRegisterLimit; -static size_t kStartUsable = 2; -static size_t kUsableLimit = 15; -static size_t kRegisterCounter = kStartUsable; -static std::string kRegisterPrefix = kAsmRegisterPrefix; - -///////////////////////////////////////// - -// COMPILER PARSING UTILITIES/STATES. - -///////////////////////////////////////// - -static std::vector kFileList; -static CompilerKit::AssemblyFactory kFactory; -static bool kInStruct = false; -static bool kOnWhileLoop = false; -static bool kOnForLoop = false; -static bool kInBraces = false; -static bool kIfFound = false; -static size_t kBracesCount = 0UL; - -/* @brief C compiler backend for C */ -class CompilerBackendCLang final : public ParserKit::CompilerBackend -{ -public: - explicit CompilerBackendCLang() = default; - ~CompilerBackendCLang() override = default; - - MPCC_COPY_DEFAULT(CompilerBackendCLang); - - std::string Check(const char* text, const char* file); - bool Compile(const std::string& text, const char* file) override; - - const char* Language() override - { - return "64k C"; - } -}; - -static CompilerBackendCLang* kCompilerBackend = nullptr; -static std::vector kCompilerVariables; -static std::vector kCompilerFunctions; -static std::vector kCompilerTypes; - -namespace detail -{ - union number_cast final { - public: - number_cast(UInt64 _Raw) - : _Raw(_Raw) - { - } - - public: - char _Num[8]; - UInt64 _Raw; - }; - - union double_cast final { - public: - double_cast(float _Raw) - : _Raw(_Raw) - { - } - - public: - char _Sign; - char _Lh[8]; - char _Rh[23]; - - float _Raw; - }; -} // namespace detail - -///////////////////////////////////////////////////////////////////////////////////////// - -// @name Compile -// @brief Generate MASM from a C assignement. - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CompilerBackendCLang::Compile(const std::string& text, const char* file) -{ - std::string textBuffer = text; - - bool typeFound = false; - bool fnFound = false; - - // setup generator. - std::random_device rd; - - auto seed_data = std::array{}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - std::mt19937 generator(seq); - - // start parsing - for (size_t text_index = 0; text_index < textBuffer.size(); ++text_index) - { - auto syntaxLeaf = ParserKit::SyntaxLeafList::SyntaxLeaf(); - - auto gen = uuids::uuid_random_generator{generator}; - uuids::uuid out = gen(); - - detail::number_cast time_off = (UInt64)out.as_bytes().data(); - - if (!typeFound) - { - auto substr = textBuffer.substr(text_index); - std::string match_type; - - for (size_t y = 0; y < substr.size(); ++y) - { - if (substr[y] == ' ') - { - while (match_type.find(' ') != std::string::npos) - { - match_type.erase(match_type.find(' ')); - } - - for (auto& clType : kCompilerTypes) - { - if (clType.fName == match_type) - { - match_type.clear(); - - std::string buf; - - buf += clType.fValue; - buf += ' '; - - if (substr.find('=') != std::string::npos) - { - break; - } - - if (textBuffer.find('(') != std::string::npos) - { - syntaxLeaf.fUserValue = buf; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - } - - typeFound = true; - break; - } - } - - break; - } - - match_type += substr[y]; - } - } - - if (textBuffer[text_index] == '{') - { - if (kInStruct) - { - continue; - } - - kInBraces = true; - ++kBracesCount; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - } - - // return keyword handler - if (textBuffer[text_index] == 'r') - { - std::string return_keyword; - return_keyword += "return"; - - std::size_t index = 0UL; - - std::string value; - - for (size_t return_index = text_index; return_index < textBuffer.size(); - ++return_index) - { - if (textBuffer[return_index] != return_keyword[index]) - { - for (size_t value_index = return_index; - value_index < textBuffer.size(); ++value_index) - { - if (textBuffer[value_index] == ';') - break; - - value += textBuffer[value_index]; - } - - break; - } - - ++index; - } - - if (index == return_keyword.size()) - { - if (!value.empty()) - { - if (value.find('(') != std::string::npos) - { - value.erase(value.find('(')); - } - - if (!isdigit(value[value.find('(') + 2])) - { - std::string tmp = value; - bool reg_to_reg = false; - - value.clear(); - - value += " import"; - value += tmp; - } - - syntaxLeaf.fUserValue = "\tldw r19, "; - - // make it pretty. - if (value.find('\t') != std::string::npos) - value.erase(value.find('\t'), 1); - - syntaxLeaf.fUserValue += value + "\n"; - } - - syntaxLeaf.fUserValue += "\tjlr"; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - - break; - } - } - - if (textBuffer[text_index] == 'i' && textBuffer[text_index + 1] == 'f') - { - auto expr = textBuffer.substr(text_index + 2); - textBuffer.erase(text_index, 2); - - if (expr.find("{") != std::string::npos) - { - expr.erase(expr.find("{")); - } - - if (expr.find("(") != std::string::npos) - expr.erase(expr.find("(")); - - if (expr.find(")") != std::string::npos) - expr.erase(expr.find(")")); - - kIfFunction = "__MPCC_IF_PROC_"; - kIfFunction += std::to_string(time_off._Raw); - - syntaxLeaf.fUserValue = "\tlda r12, import "; - syntaxLeaf.fUserValue += - kIfFunction + - "\n\t#r12 = Code to jump on, r11 right cond, r10 left cond.\n\tbeq " - "r10, r11, r12\ndword export .code64 " + - kIfFunction + "\n"; - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - - kIfFound = true; - } - - // Parse expressions and instructions here. - // what does this mean? - // we encounter an assignment, or we reached the end of an expression. - if (textBuffer[text_index] == '=' || textBuffer[text_index] == ';') - { - if (fnFound) - continue; - if (kIfFound) - continue; - - if (textBuffer[text_index] == ';' && kInStruct) - continue; - - if (textBuffer.find("typedef ") != std::string::npos) - continue; - - if (textBuffer[text_index] == '=' && kInStruct) - { - detail::print_error("assignement of value in struct " + textBuffer, - file); - continue; - } - - if (textBuffer[text_index] == ';' && kInStruct) - { - bool space_found_ = false; - std::string sym; - - for (auto& ch : textBuffer) - { - if (ch == ' ') - { - space_found_ = true; - } - - if (ch == ';') - break; - - if (space_found_) - sym.push_back(ch); - } - - kState.kStructMap[kState.kStructMap.size() - 1].fOffsets.push_back( - std::make_pair( - kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt + 4, - sym)); - - kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt = - kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt + 4; - - continue; - } - - if (textBuffer[text_index] == '=' && kInStruct) - { - continue; - } - - if (textBuffer[text_index + 1] == '=' || - textBuffer[text_index - 1] == '!' || - textBuffer[text_index - 1] == '<' || - textBuffer[text_index - 1] == '>') - { - continue; - } - - std::string substr; - - if (textBuffer.find('=') != std::string::npos && kInBraces && !kIfFound) - { - if (textBuffer.find("*") != std::string::npos) - { - if (textBuffer.find("=") > textBuffer.find("*")) - substr += "\tlda "; - else - substr += "\tldw "; - } - else - { - substr += "\tldw "; - } - } - else if (textBuffer.find('=') != std::string::npos && !kInBraces) - { - substr += "stw export .data64 "; - } - - int first_encountered = 0; - - std::string str_name; - - for (size_t text_index_2 = 0; text_index_2 < textBuffer.size(); - ++text_index_2) - { - if (textBuffer[text_index_2] == '\"') - { - ++text_index_2; - - // want to add this, so that the parser recognizes that this is a - // string. - substr += '"'; - - for (; text_index_2 < textBuffer.size(); ++text_index_2) - { - if (textBuffer[text_index_2] == '\"') - break; - - substr += textBuffer[text_index_2]; - } - } - - if (textBuffer[text_index_2] == '{' || textBuffer[text_index_2] == '}') - continue; - - if (textBuffer[text_index_2] == ';') - { - break; - } - - if (textBuffer[text_index_2] == ' ' || - textBuffer[text_index_2] == '\t') - { - if (first_encountered != 2) - { - if (textBuffer[text_index] != '=' && - substr.find("export .data64") == std::string::npos && - !kInStruct) - substr += "export .data64 "; - } - - ++first_encountered; - - continue; - } - - if (textBuffer[text_index_2] == '=') - { - if (!kInBraces) - { - substr.replace(substr.find("export .data64"), - strlen("export .data64"), "export .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"), "import "); - - if (substr.find("export .data64") != std::string::npos) - substr.erase(substr.find("export .data64"), strlen("export .data64")); - } - - auto var_to_find = - std::find_if(kCompilerVariables.cbegin(), kCompilerVariables.cend(), - [&](detail::CompilerType type) { - return type.fName.find(substr) != std::string::npos; - }); - - if (kRegisterCounter == 5 || kRegisterCounter == 6) - ++kRegisterCounter; - - std::string reg = kAsmRegisterPrefix; - reg += std::to_string(kRegisterCounter); - - if (var_to_find == kCompilerVariables.cend()) - { - ++kRegisterCounter; - - kState.kStackFrame.push_back({.fName = substr, .fReg = reg}); - kCompilerVariables.push_back({.fName = substr}); - } - - syntaxLeaf.fUserValue += substr; - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - - if (textBuffer[text_index] == '=') - break; - } - - // function handler. - - if (textBuffer[text_index] == '(' && !fnFound && !kIfFound) - { - std::string substr; - std::string args_buffer; - std::string args; - - bool type_crossed = false; - - for (size_t idx = textBuffer.find('(') + 1; idx < textBuffer.size(); - ++idx) - { - if (textBuffer[idx] == ',') - continue; - - if (textBuffer[idx] == ' ') - continue; - - if (textBuffer[idx] == ')') - break; - } - - for (char substr_first_index : textBuffer) - { - if (substr_first_index != ',') - args_buffer += substr_first_index; - else - args_buffer += '$'; - - if (substr_first_index == ';') - { - args_buffer = args_buffer.erase(0, args_buffer.find('(')); - args_buffer = args_buffer.erase(args_buffer.find(';'), 1); - args_buffer = args_buffer.erase(args_buffer.find(')'), 1); - args_buffer = args_buffer.erase(args_buffer.find('('), 1); - - if (!args_buffer.empty()) - args += "\tldw r6, "; - - std::string register_type; - std::size_t index = 7UL; - - while (args_buffer.find("$") != std::string::npos) - { - register_type = kRegisterPrefix; - register_type += std::to_string(index); - - ++index; - - args_buffer.replace(args_buffer.find('$'), 1, - "\n\tldw " + register_type + ","); - } - - args += args_buffer; - args += "\n\tlda r19, "; - } - } - - for (char _text_i : textBuffer) - { - if (_text_i == '\t' || _text_i == ' ') - { - if (!type_crossed) - { - substr.clear(); - type_crossed = true; - } - - continue; - } - - if (_text_i == '(') - break; - - substr += _text_i; - } - - if (kInBraces) - { - syntaxLeaf.fUserValue = args; - syntaxLeaf.fUserValue += substr; - syntaxLeaf.fUserValue += "\n\tjrl\n"; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - - fnFound = true; - } - else - { - syntaxLeaf.fUserValue.clear(); - - syntaxLeaf.fUserValue += "export .code64 "; - - syntaxLeaf.fUserValue += substr; - syntaxLeaf.fUserValue += "\n"; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - - fnFound = true; - } - - kCompilerFunctions.push_back(textBuffer); - } - - if (textBuffer[text_index] == '-' && textBuffer[text_index + 1] == '-') - { - textBuffer = textBuffer.replace(textBuffer.find("--"), strlen("--"), ""); - - for (int _text_i = 0; _text_i < textBuffer.size(); ++_text_i) - { - if (textBuffer[_text_i] == '\t' || textBuffer[_text_i] == ' ') - textBuffer.erase(_text_i, 1); - } - - syntaxLeaf.fUserValue += "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 = ParserKit::SyntaxLeafList::SyntaxLeaf(); - syntaxLeaf.fUserValue = "\n"; - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - - return true; -} - -static bool kShouldHaveBraces = false; -static std::string kFnName; - -std::string CompilerBackendCLang::Check(const char* text, const char* file) -{ - std::string err_str; - std::string ln = text; - - if (ln.empty()) - { - return err_str; - } - - bool non_ascii_found = false; - - for (int i = 0; i < ln.size(); ++i) - { - if (isalnum(ln[i])) - { - non_ascii_found = true; - break; - } - } - - if (kShouldHaveBraces && ln.find('{') != std::string::npos) - { - kShouldHaveBraces = false; - } - - if (!non_ascii_found) - return err_str; - - size_t string_index = 1UL; - - if (ln.find('\'') != std::string::npos) - { - string_index = ln.find('\'') + 1; - - for (; string_index < ln.size(); ++string_index) - { - if (ln[string_index] == '\'') - { - if (ln[string_index + 1] != ';') - { - ln.erase(string_index, 1); - } - - return err_str; - } - } - } - else if (ln.find('"') != std::string::npos) - { - string_index = ln.find('"') + 1; - - for (; string_index < ln.size(); ++string_index) - { - if (ln[string_index] == '"') - { - if (ln[string_index + 1] != ';') - { - ln.erase(string_index, 1); - } - else - { - break; - } - } - } - } - else if (ln.find('"') == std::string::npos && - ln.find('\'') == std::string::npos) - { - std::vector 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 variables_list = { - {.fBegin = "static ", .fEnd = "="}, - {.fBegin = "=", .fEnd = ";"}, - {.fBegin = "if(", .fEnd = "="}, - {.fBegin = "if (", .fEnd = "="}, - {.fBegin = "if(", .fEnd = "<"}, - {.fBegin = "if (", .fEnd = "<"}, - {.fBegin = "if(", .fEnd = ">"}, - {.fBegin = "if (", .fEnd = ">"}, - {.fBegin = "if(", .fEnd = ")"}, - {.fBegin = "if (", .fEnd = ")"}, - - {.fBegin = "else(", .fEnd = "="}, - {.fBegin = "else (", .fEnd = "="}, - {.fBegin = "else(", .fEnd = "<"}, - {.fBegin = "else (", .fEnd = "<"}, - {.fBegin = "else(", .fEnd = ">"}, - {.fBegin = "else (", .fEnd = ">"}, - {.fBegin = "else(", .fEnd = ")"}, - {.fBegin = "else (", .fEnd = ")"}, - }; - - for (auto& variable : variables_list) - { - if (ln.find(variable.fBegin) != std::string::npos) - { - string_index = ln.find(variable.fBegin) + variable.fBegin.size(); - - while (ln[string_index] == ' ') - ++string_index; - - std::string keyword; - - for (; string_index < ln.size(); ++string_index) - { - if (ln[string_index] == variable.fEnd[0]) - { - std::string varname = ""; - - for (size_t index_keyword = ln.find(' '); - ln[index_keyword] != variable.fBegin[0]; ++index_keyword) - { - if (ln[index_keyword] == ' ') - { - continue; - } - - if (isdigit(ln[index_keyword])) - { - goto cc_next_loop; - } - - varname += ln[index_keyword]; - } - - if (varname.find(' ') != std::string::npos) - { - varname.erase(0, varname.find(' ')); - - if (variable.fBegin == "extern") - { - varname.erase(0, varname.find(' ')); - } - } - - if (kRegisterCounter == 5 || kRegisterCounter == 6) - ++kRegisterCounter; - - std::string reg = kAsmRegisterPrefix; - reg += std::to_string(kRegisterCounter); - - kCompilerVariables.push_back({.fValue = varname}); - goto cc_check_done; - } - - keyword.push_back(ln[string_index]); - } - - goto cc_next_loop; - - cc_check_done: - - // skip digit value. - if (isdigit(keyword[0]) || keyword[0] == '"') - { - goto cc_next_loop; - } - - while (keyword.find(' ') != std::string::npos) - keyword.erase(keyword.find(' '), 1); - - for (auto& var : kCompilerVariables) - { - if (var.fValue.find(keyword) != std::string::npos) - { - err_str.clear(); - goto cc_next; - } - } - - for (auto& fn : kCompilerFunctions) - { - if (fn.find(keyword[0]) != std::string::npos) - { - auto where_begin = fn.find(keyword[0]); - auto keyword_begin = 0UL; - auto failed = false; - - for (; where_begin < keyword.size(); ++where_begin) - { - if (fn[where_begin] == '(' && keyword[keyword_begin] == '(') - break; - - if (fn[where_begin] != keyword[keyword_begin]) - { - failed = true; - break; - } - - ++keyword_begin; - } - - if (!failed) - { - err_str.clear(); - goto cc_next; - } - else - { - continue; - } - } - } - - cc_error_value: - if (keyword.find("->") != std::string::npos) - return err_str; - - if (keyword.find(".") != std::string::npos) - return err_str; - - if (isalnum(keyword[0])) - err_str += "\nUndefined value: " + keyword; - - return err_str; - } - - cc_next_loop: - continue; - } - -cc_next: - - // extern does not declare anything, it imports a variable. - // so that's why it's not declare upper. - if (ParserKit::find_word(ln, "extern")) - { - auto substr = ln.substr(ln.find("extern") + strlen("extern")); - kCompilerVariables.push_back({.fValue = substr}); - } - - if (kShouldHaveBraces && ln.find('{') == std::string::npos) - { - err_str += "Missing '{' for function "; - err_str += kFnName; - err_str += "\n"; - - kShouldHaveBraces = false; - kFnName.clear(); - } - else if (kShouldHaveBraces && ln.find('{') != std::string::npos) - { - kShouldHaveBraces = false; - kFnName.clear(); - } - - bool type_not_found = true; - - if (ln.find('\'') != std::string::npos) - { - ln.replace(ln.find('\''), 3, "0"); - } - - auto first = ln.find('"'); - if (first != std::string::npos) - { - auto second = 0UL; - bool found_second_quote = false; - - for (size_t i = first + 1; i < ln.size(); ++i) - { - if (ln[i] == '\"') - { - found_second_quote = true; - second = i; - - break; - } - } - - if (!found_second_quote) - { - err_str += "Missing terminating \"."; - err_str += " here -> " + ln.substr(ln.find('"'), second); - } - } - - if (ln.find(')') != std::string::npos && ln.find(';') == std::string::npos) - { - if (ln.find('{') == std::string::npos) - { - kFnName = ln; - kShouldHaveBraces = true; - - goto skip_braces_check; - } - else if (ln.find('{') != std::string::npos) - { - kShouldHaveBraces = false; - } - } - -skip_braces_check: - - for (auto& key : kCompilerTypes) - { - if (ParserKit::find_word(ln, key.fName)) - { - if (isdigit(ln[ln.find(key.fName) + key.fName.size() + 1])) - { - err_str += "\nNumber cannot be set for "; - err_str += key.fName; - err_str += "'s name. here -> "; - err_str += ln; - } - - if (ln.find(key.fName) == 0 || ln[ln.find(key.fName) - 1] == ' ' || - ln[ln.find(key.fName) - 1] == '\t') - { - type_not_found = false; - - if (ln[ln.find(key.fName) + key.fName.size()] != ' ') - { - type_not_found = true; - - if (ln[ln.find(key.fName) + key.fName.size()] == '\t') - type_not_found = false; - - goto next; - } - else if (ln[ln.find(key.fName) + key.fName.size()] != '\t') - { - type_not_found = true; - - if (ln[ln.find(key.fName) + key.fName.size()] == ' ') - type_not_found = false; - } - } - - next: - - if (ln.find(';') == std::string::npos) - { - if (ln.find('(') != std::string::npos) - { - if (ln.find('=') == std::string::npos) - continue; - } - - err_str += "\nMissing ';', here -> "; - err_str += ln; - } - else - { - continue; - } - - if (ln.find('=') != std::string::npos) - { - if (ln.find('(') != std::string::npos) - { - if (ln.find(')') == std::string::npos) - { - err_str += "\nMissing ')', after '(' here -> "; - err_str += ln.substr(ln.find('(')); - } - } - } - } - } - - if (kInBraces && ln.find("struct") != std::string::npos && - ln.find("union") != std::string::npos && - ln.find("enum") != std::string::npos && - ln.find('=') != std::string::npos) - { - if (ln.find(';') == std::string::npos) - { - err_str += "\nMissing ';' after struct/union/enum declaration, here -> "; - err_str += ln; - } - } - - if (ln.find(';') != std::string::npos && - ln.find("for") == std::string::npos) - { - if (ln.find(';') + 1 != ln.size()) - { - for (int i = 0; i < ln.substr(ln.find(';') + 1).size(); ++i) - { - if ((ln.substr(ln.find(';') + 1)[i] != ' ') || - (ln.substr(ln.find(';') + 1)[i] != '\t')) - { - if (auto err = this->Check(ln.substr(ln.find(';') + 1).c_str(), file); - !err.empty()) - { - err_str += "\nUnexpected text after ';' -> "; - err_str += ln.substr(ln.find(';')); - err_str += err; - } - } - } - } - } - - if (ln.find('(') != std::string::npos) - { - if (ln.find(';') == std::string::npos && !ParserKit::find_word(ln, "|") && - !ParserKit::find_word(ln, "||") && !ParserKit::find_word(ln, "&") && - !ParserKit::find_word(ln, "&&") && !ParserKit::find_word(ln, "~")) - { - bool found_func = false; - size_t i = ln.find('('); - std::vector opens; - std::vector 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 : public CompilerKit::AssemblyInterface -{ -public: - explicit AssemblyCCInterface() = default; - ~AssemblyCCInterface() override = default; - - MPCC_COPY_DEFAULT(AssemblyCCInterface); - - [[maybe_unused]] static Int32 Arch() noexcept - { - return CompilerKit::AssemblyFactory::kArch64x0; - } - - Int32 CompileToFormat(std::string& src, Int32 arch) override - { - if (arch != AssemblyCCInterface::Arch()) - return -1; - - if (kCompilerBackend == nullptr) - return -1; - - /* @brief copy contents wihtout extension */ - std::string src_file = src.data(); - std::ifstream src_fp = std::ifstream(src_file, std::ios::in); - std::string dest; - - for (auto& ch : src_file) - { - if (ch == '.') - { - break; - } - - dest += ch; - } - - /* According to PEF ABI. */ - std::vector exts = kAsmFileExts; - dest += exts[4]; - - kState.fOutputAssembly = std::make_unique(dest); - - auto fmt = CompilerKit::current_date(); - - (*kState.fOutputAssembly) << "# Path: " << src_file << "\n"; - (*kState.fOutputAssembly) - << "# Language: 64x0 Assembly (Generated from ANSI C)\n"; - (*kState.fOutputAssembly) << "# Date: " << fmt << "\n\n"; - - ParserKit::SyntaxLeafList syntax; - - kState.fSyntaxTreeList.push_back(syntax); - kState.fSyntaxTree = - &kState.fSyntaxTreeList[kState.fSyntaxTreeList.size() - 1]; - - std::string line_src; - - while (std::getline(src_fp, line_src)) - { - if (auto err = kCompilerBackend->Check(line_src.c_str(), src.data()); - err.empty()) - { - kCompilerBackend->Compile(line_src, src.data()); - } - else - { - detail::print_error(err, src.data()); - } - } - - if (kAcceptableErrors > 0) - return -1; - - std::vector keywords = {"ldw", "stw", "lda", "sta", - "add", "sub", "mv"}; - - /// - /// Replace, optimize, fix assembly output. - /// - - for (auto& leaf : kState.fSyntaxTree->fLeafList) - { - std::vector access_keywords = {"->", "."}; - - for (auto& access_ident : access_keywords) - { - if (ParserKit::find_word(leaf.fUserValue, access_ident)) - { - for (auto& struc : kState.kStructMap) - { - /// TODO: - } - } - } - - for (auto& keyword : keywords) - { - if (ParserKit::find_word(leaf.fUserValue, keyword)) - { - std::size_t cnt = 0UL; - - for (auto& reg : kState.kStackFrame) - { - std::string needle; - - for (size_t i = 0; i < reg.fName.size(); i++) - { - if (reg.fName[i] == ' ') - { - ++i; - - for (; i < reg.fName.size(); i++) - { - if (reg.fName[i] == ',') - { - break; - } - - if (reg.fName[i] == ' ') - continue; - - needle += reg.fName[i]; - } - - break; - } - } - - if (ParserKit::find_word(leaf.fUserValue, needle)) - { - if (leaf.fUserValue.find("import " + needle) != - std::string::npos) - { - std::string range = "import " + needle; - leaf.fUserValue.replace( - leaf.fUserValue.find("import " + needle), range.size(), - needle); - } - - if (leaf.fUserValue.find("ldw r6") != std::string::npos) - { - std::string::difference_type countComma = std::count( - leaf.fUserValue.begin(), leaf.fUserValue.end(), ','); - - if (countComma == 1) - { - leaf.fUserValue.replace(leaf.fUserValue.find("ldw"), - strlen("ldw"), "mv"); - } - } - - leaf.fUserValue.replace(leaf.fUserValue.find(needle), - needle.size(), reg.fReg); - - ++cnt; - } - } - - if (cnt > 1 && keyword != "mv" && keyword != "add" && - keyword != "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 kOk; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -#include - -#define kPrintF printf -#define kSplashCxx() \ - kPrintF(kWhite "Zeta C Driver, %s, (c) ZKA Technologies\n", kDistVersion) - -static void cc_print_help() -{ - kSplashCxx(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#define kExt ".c" - -NDK_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 = CompilerKit::AssemblyFactory::kArch64x0; - kCompilerBackend = new CompilerBackendCLang(); - - for (auto index = 1UL; index < argc; ++index) - { - if (skip) - { - skip = false; - continue; - } - - if (argv[index][0] == '/') - { - if (strcmp(argv[index], "/v") == 0 || - strcmp(argv[index], "/version") == 0) - { - kSplashCxx(); - return kOk; - } - - if (strcmp(argv[index], "/verbose") == 0) - { - kState.fVerbose = true; - - continue; - } - - if (strcmp(argv[index], "/h") == 0 || strcmp(argv[index], "/help") == 0) - { - cc_print_help(); - - return kOk; - } - - if (strcmp(argv[index], "/dialect") == 0) - { - if (kCompilerBackend) - std::cout << kCompilerBackend->Language() << "\n"; - - return kOk; - } - - if (strcmp(argv[index], "/fmax-exceptions") == 0) - { - try - { - kErrorLimit = std::strtol(argv[index + 1], nullptr, 10); - } - // catch anything here - catch (...) - { - kErrorLimit = 0; - } - - skip = true; - - continue; - } - - std::string err = "Unknown command: "; - err += argv[index]; - - detail::print_error(err, "cc"); - - continue; - } - - kFileList.emplace_back(argv[index]); - - std::string srcFile = argv[index]; - - if (strstr(argv[index], kExt) == nullptr) - { - if (kState.fVerbose) - { - detail::print_error(srcFile + " is not a valid C source.\n", "cc"); - } - - return 1; - } - - if (kFactory.Compile(srcFile, kMachine) != kOk) - return -1; - } - - return kOk; -} - -// Last rev 8-1-24 diff --git a/Sources/64x0-cc.cxx b/Sources/64x0-cc.cxx new file mode 100644 index 0000000..47abfb5 --- /dev/null +++ b/Sources/64x0-cc.cxx @@ -0,0 +1,1627 @@ +/* + * ======================================================== + * + * cc + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +/// BUGS: ? +/// TODO: + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* C driver */ +/* This is part of the NDK. */ +/* (c) ZKA Technologies */ + +/// @author Amlal El Mahrouss (amlel) +/// @file 64x0-cc.cxx +/// @brief 64x0 C Compiler. + +/// TODO: support structures, else if, else, . and -> + +///////////////////// + +// ANSI ESCAPE CODES + +///////////////////// + +#define kOk (0) + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" + +///////////////////////////////////// + +// INTERNAL STUFF OF THE C COMPILER + +///////////////////////////////////// + +namespace detail +{ + // \brief name to register struct. + struct CompilerRegisterMap final + { + std::string fName; + std::string fReg; + }; + + // \brief Map for C structs + // \author amlel + struct CompilerStructMap final + { + // 'my_foo' + std::string fName; + + // if instance: stores a valid register. + std::string fReg; + + // offset count + std::size_t fOffsetsCnt; + + // offset array. + std::vector> fOffsets; + }; + + struct CompilerState final + { + std::vector fSyntaxTreeList; + std::vector kStackFrame; + std::vector kStructMap; + ParserKit::SyntaxLeafList* fSyntaxTree{nullptr}; + std::unique_ptr fOutputAssembly; + std::string fLastFile; + std::string fLastError; + bool fVerbose; + }; +} // namespace detail + +static detail::CompilerState kState; +static SizeType kErrorLimit = 100; +static std::string kIfFunction = ""; +static Int32 kAcceptableErrors = 0; + +namespace detail +{ + /// @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 + { + if (reason[0] == '\n') + reason.erase(0, 1); + + if (file.find(".pp") != std::string::npos) + { + file.erase(file.find(".pp"), 3); + } + + if (kState.fLastFile != file) + { + std::cout << kRed << "[ cc ] " << kWhite + << ((file == "cc") ? "internal compiler error " + : ("in file, " + file)) + << kBlank << std::endl; + std::cout << kRed << "[ cc ] " << kWhite << reason << kBlank << std::endl; + + kState.fLastFile = file; + } + else + { + std::cout << kRed << "[ cc ] [ " << kState.fLastFile << " ] " << kWhite + << reason << kBlank << std::endl; + } + + if (kAcceptableErrors > kErrorLimit) + std::exit(3); + + ++kAcceptableErrors; + } + + struct CompilerType final + { + std::string fName; + std::string fValue; + }; +} // namespace detail + +///////////////////////////////////////////////////////////////////////////////////////// + +// Target architecture. +static int kMachine = 0; + +///////////////////////////////////////// + +// REGISTERS ACCORDING TO USED ASSEMBLER + +///////////////////////////////////////// + +static size_t kRegisterCnt = kAsmRegisterLimit; +static size_t kStartUsable = 2; +static size_t kUsableLimit = 15; +static size_t kRegisterCounter = kStartUsable; +static std::string kRegisterPrefix = kAsmRegisterPrefix; + +///////////////////////////////////////// + +// COMPILER PARSING UTILITIES/STATES. + +///////////////////////////////////////// + +static std::vector kFileList; +static CompilerKit::AssemblyFactory kFactory; +static bool kInStruct = false; +static bool kOnWhileLoop = false; +static bool kOnForLoop = false; +static bool kInBraces = false; +static bool kIfFound = false; +static size_t kBracesCount = 0UL; + +/* @brief C compiler backend for C */ +class CompilerBackendCLang final : public ParserKit::CompilerBackend +{ +public: + explicit CompilerBackendCLang() = default; + ~CompilerBackendCLang() override = default; + + MPCC_COPY_DEFAULT(CompilerBackendCLang); + + std::string Check(const char* text, const char* file); + bool Compile(const std::string& text, const char* file) override; + + const char* Language() override + { + return "64k C"; + } +}; + +static CompilerBackendCLang* kCompilerBackend = nullptr; +static std::vector kCompilerVariables; +static std::vector kCompilerFunctions; +static std::vector kCompilerTypes; + +namespace detail +{ + union number_cast final { + public: + number_cast(UInt64 _Raw) + : _Raw(_Raw) + { + } + + public: + char _Num[8]; + UInt64 _Raw; + }; + + union double_cast final { + public: + double_cast(float _Raw) + : _Raw(_Raw) + { + } + + public: + char _Sign; + char _Lh[8]; + char _Rh[23]; + + float _Raw; + }; +} // namespace detail + +///////////////////////////////////////////////////////////////////////////////////////// + +// @name Compile +// @brief Generate MASM from a C assignement. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerBackendCLang::Compile(const std::string& text, const char* file) +{ + std::string textBuffer = text; + + bool typeFound = false; + bool fnFound = false; + + // setup generator. + std::random_device rd; + + auto seed_data = std::array{}; + std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::mt19937 generator(seq); + + // start parsing + for (size_t text_index = 0; text_index < textBuffer.size(); ++text_index) + { + auto syntaxLeaf = ParserKit::SyntaxLeafList::SyntaxLeaf(); + + auto gen = uuids::uuid_random_generator{generator}; + uuids::uuid out = gen(); + + detail::number_cast time_off = (UInt64)out.as_bytes().data(); + + if (!typeFound) + { + auto substr = textBuffer.substr(text_index); + std::string match_type; + + for (size_t y = 0; y < substr.size(); ++y) + { + if (substr[y] == ' ') + { + while (match_type.find(' ') != std::string::npos) + { + match_type.erase(match_type.find(' ')); + } + + for (auto& clType : kCompilerTypes) + { + if (clType.fName == match_type) + { + match_type.clear(); + + std::string buf; + + buf += clType.fValue; + buf += ' '; + + if (substr.find('=') != std::string::npos) + { + break; + } + + if (textBuffer.find('(') != std::string::npos) + { + syntaxLeaf.fUserValue = buf; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + } + + typeFound = true; + break; + } + } + + break; + } + + match_type += substr[y]; + } + } + + if (textBuffer[text_index] == '{') + { + if (kInStruct) + { + continue; + } + + kInBraces = true; + ++kBracesCount; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + } + + // return keyword handler + if (textBuffer[text_index] == 'r') + { + std::string return_keyword; + return_keyword += "return"; + + std::size_t index = 0UL; + + std::string value; + + for (size_t return_index = text_index; return_index < textBuffer.size(); + ++return_index) + { + if (textBuffer[return_index] != return_keyword[index]) + { + for (size_t value_index = return_index; + value_index < textBuffer.size(); ++value_index) + { + if (textBuffer[value_index] == ';') + break; + + value += textBuffer[value_index]; + } + + break; + } + + ++index; + } + + if (index == return_keyword.size()) + { + if (!value.empty()) + { + if (value.find('(') != std::string::npos) + { + value.erase(value.find('(')); + } + + if (!isdigit(value[value.find('(') + 2])) + { + std::string tmp = value; + bool reg_to_reg = false; + + value.clear(); + + value += " import"; + value += tmp; + } + + syntaxLeaf.fUserValue = "\tldw r19, "; + + // make it pretty. + if (value.find('\t') != std::string::npos) + value.erase(value.find('\t'), 1); + + syntaxLeaf.fUserValue += value + "\n"; + } + + syntaxLeaf.fUserValue += "\tjlr"; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + break; + } + } + + if (textBuffer[text_index] == 'i' && textBuffer[text_index + 1] == 'f') + { + auto expr = textBuffer.substr(text_index + 2); + textBuffer.erase(text_index, 2); + + if (expr.find("{") != std::string::npos) + { + expr.erase(expr.find("{")); + } + + if (expr.find("(") != std::string::npos) + expr.erase(expr.find("(")); + + if (expr.find(")") != std::string::npos) + expr.erase(expr.find(")")); + + kIfFunction = "__MPCC_IF_PROC_"; + kIfFunction += std::to_string(time_off._Raw); + + syntaxLeaf.fUserValue = "\tlda r12, import "; + syntaxLeaf.fUserValue += + kIfFunction + + "\n\t#r12 = Code to jump on, r11 right cond, r10 left cond.\n\tbeq " + "r10, r11, r12\ndword export .code64 " + + kIfFunction + "\n"; + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + kIfFound = true; + } + + // Parse expressions and instructions here. + // what does this mean? + // we encounter an assignment, or we reached the end of an expression. + if (textBuffer[text_index] == '=' || textBuffer[text_index] == ';') + { + if (fnFound) + continue; + if (kIfFound) + continue; + + if (textBuffer[text_index] == ';' && kInStruct) + continue; + + if (textBuffer.find("typedef ") != std::string::npos) + continue; + + if (textBuffer[text_index] == '=' && kInStruct) + { + detail::print_error("assignement of value in struct " + textBuffer, + file); + continue; + } + + if (textBuffer[text_index] == ';' && kInStruct) + { + bool space_found_ = false; + std::string sym; + + for (auto& ch : textBuffer) + { + if (ch == ' ') + { + space_found_ = true; + } + + if (ch == ';') + break; + + if (space_found_) + sym.push_back(ch); + } + + kState.kStructMap[kState.kStructMap.size() - 1].fOffsets.push_back( + std::make_pair( + kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt + 4, + sym)); + + kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt = + kState.kStructMap[kState.kStructMap.size() - 1].fOffsetsCnt + 4; + + continue; + } + + if (textBuffer[text_index] == '=' && kInStruct) + { + continue; + } + + if (textBuffer[text_index + 1] == '=' || + textBuffer[text_index - 1] == '!' || + textBuffer[text_index - 1] == '<' || + textBuffer[text_index - 1] == '>') + { + continue; + } + + std::string substr; + + if (textBuffer.find('=') != std::string::npos && kInBraces && !kIfFound) + { + if (textBuffer.find("*") != std::string::npos) + { + if (textBuffer.find("=") > textBuffer.find("*")) + substr += "\tlda "; + else + substr += "\tldw "; + } + else + { + substr += "\tldw "; + } + } + else if (textBuffer.find('=') != std::string::npos && !kInBraces) + { + substr += "stw export .data64 "; + } + + int first_encountered = 0; + + std::string str_name; + + for (size_t text_index_2 = 0; text_index_2 < textBuffer.size(); + ++text_index_2) + { + if (textBuffer[text_index_2] == '\"') + { + ++text_index_2; + + // want to add this, so that the parser recognizes that this is a + // string. + substr += '"'; + + for (; text_index_2 < textBuffer.size(); ++text_index_2) + { + if (textBuffer[text_index_2] == '\"') + break; + + substr += textBuffer[text_index_2]; + } + } + + if (textBuffer[text_index_2] == '{' || textBuffer[text_index_2] == '}') + continue; + + if (textBuffer[text_index_2] == ';') + { + break; + } + + if (textBuffer[text_index_2] == ' ' || + textBuffer[text_index_2] == '\t') + { + if (first_encountered != 2) + { + if (textBuffer[text_index] != '=' && + substr.find("export .data64") == std::string::npos && + !kInStruct) + substr += "export .data64 "; + } + + ++first_encountered; + + continue; + } + + if (textBuffer[text_index_2] == '=') + { + if (!kInBraces) + { + substr.replace(substr.find("export .data64"), + strlen("export .data64"), "export .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"), "import "); + + if (substr.find("export .data64") != std::string::npos) + substr.erase(substr.find("export .data64"), strlen("export .data64")); + } + + auto var_to_find = + std::find_if(kCompilerVariables.cbegin(), kCompilerVariables.cend(), + [&](detail::CompilerType type) { + return type.fName.find(substr) != std::string::npos; + }); + + if (kRegisterCounter == 5 || kRegisterCounter == 6) + ++kRegisterCounter; + + std::string reg = kAsmRegisterPrefix; + reg += std::to_string(kRegisterCounter); + + if (var_to_find == kCompilerVariables.cend()) + { + ++kRegisterCounter; + + kState.kStackFrame.push_back({.fName = substr, .fReg = reg}); + kCompilerVariables.push_back({.fName = substr}); + } + + syntaxLeaf.fUserValue += substr; + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + if (textBuffer[text_index] == '=') + break; + } + + // function handler. + + if (textBuffer[text_index] == '(' && !fnFound && !kIfFound) + { + std::string substr; + std::string args_buffer; + std::string args; + + bool type_crossed = false; + + for (size_t idx = textBuffer.find('(') + 1; idx < textBuffer.size(); + ++idx) + { + if (textBuffer[idx] == ',') + continue; + + if (textBuffer[idx] == ' ') + continue; + + if (textBuffer[idx] == ')') + break; + } + + for (char substr_first_index : textBuffer) + { + if (substr_first_index != ',') + args_buffer += substr_first_index; + else + args_buffer += '$'; + + if (substr_first_index == ';') + { + args_buffer = args_buffer.erase(0, args_buffer.find('(')); + args_buffer = args_buffer.erase(args_buffer.find(';'), 1); + args_buffer = args_buffer.erase(args_buffer.find(')'), 1); + args_buffer = args_buffer.erase(args_buffer.find('('), 1); + + if (!args_buffer.empty()) + args += "\tldw r6, "; + + std::string register_type; + std::size_t index = 7UL; + + while (args_buffer.find("$") != std::string::npos) + { + register_type = kRegisterPrefix; + register_type += std::to_string(index); + + ++index; + + args_buffer.replace(args_buffer.find('$'), 1, + "\n\tldw " + register_type + ","); + } + + args += args_buffer; + args += "\n\tlda r19, "; + } + } + + for (char _text_i : textBuffer) + { + if (_text_i == '\t' || _text_i == ' ') + { + if (!type_crossed) + { + substr.clear(); + type_crossed = true; + } + + continue; + } + + if (_text_i == '(') + break; + + substr += _text_i; + } + + if (kInBraces) + { + syntaxLeaf.fUserValue = args; + syntaxLeaf.fUserValue += substr; + syntaxLeaf.fUserValue += "\n\tjrl\n"; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + fnFound = true; + } + else + { + syntaxLeaf.fUserValue.clear(); + + syntaxLeaf.fUserValue += "export .code64 "; + + syntaxLeaf.fUserValue += substr; + syntaxLeaf.fUserValue += "\n"; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + fnFound = true; + } + + kCompilerFunctions.push_back(textBuffer); + } + + if (textBuffer[text_index] == '-' && textBuffer[text_index + 1] == '-') + { + textBuffer = textBuffer.replace(textBuffer.find("--"), strlen("--"), ""); + + for (int _text_i = 0; _text_i < textBuffer.size(); ++_text_i) + { + if (textBuffer[_text_i] == '\t' || textBuffer[_text_i] == ' ') + textBuffer.erase(_text_i, 1); + } + + syntaxLeaf.fUserValue += "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 = ParserKit::SyntaxLeafList::SyntaxLeaf(); + syntaxLeaf.fUserValue = "\n"; + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + return true; +} + +static bool kShouldHaveBraces = false; +static std::string kFnName; + +std::string CompilerBackendCLang::Check(const char* text, const char* file) +{ + std::string err_str; + std::string ln = text; + + if (ln.empty()) + { + return err_str; + } + + bool non_ascii_found = false; + + for (int i = 0; i < ln.size(); ++i) + { + if (isalnum(ln[i])) + { + non_ascii_found = true; + break; + } + } + + if (kShouldHaveBraces && ln.find('{') != std::string::npos) + { + kShouldHaveBraces = false; + } + + if (!non_ascii_found) + return err_str; + + size_t string_index = 1UL; + + if (ln.find('\'') != std::string::npos) + { + string_index = ln.find('\'') + 1; + + for (; string_index < ln.size(); ++string_index) + { + if (ln[string_index] == '\'') + { + if (ln[string_index + 1] != ';') + { + ln.erase(string_index, 1); + } + + return err_str; + } + } + } + else if (ln.find('"') != std::string::npos) + { + string_index = ln.find('"') + 1; + + for (; string_index < ln.size(); ++string_index) + { + if (ln[string_index] == '"') + { + if (ln[string_index + 1] != ';') + { + ln.erase(string_index, 1); + } + else + { + break; + } + } + } + } + else if (ln.find('"') == std::string::npos && + ln.find('\'') == std::string::npos) + { + std::vector 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 variables_list = { + {.fBegin = "static ", .fEnd = "="}, + {.fBegin = "=", .fEnd = ";"}, + {.fBegin = "if(", .fEnd = "="}, + {.fBegin = "if (", .fEnd = "="}, + {.fBegin = "if(", .fEnd = "<"}, + {.fBegin = "if (", .fEnd = "<"}, + {.fBegin = "if(", .fEnd = ">"}, + {.fBegin = "if (", .fEnd = ">"}, + {.fBegin = "if(", .fEnd = ")"}, + {.fBegin = "if (", .fEnd = ")"}, + + {.fBegin = "else(", .fEnd = "="}, + {.fBegin = "else (", .fEnd = "="}, + {.fBegin = "else(", .fEnd = "<"}, + {.fBegin = "else (", .fEnd = "<"}, + {.fBegin = "else(", .fEnd = ">"}, + {.fBegin = "else (", .fEnd = ">"}, + {.fBegin = "else(", .fEnd = ")"}, + {.fBegin = "else (", .fEnd = ")"}, + }; + + for (auto& variable : variables_list) + { + if (ln.find(variable.fBegin) != std::string::npos) + { + string_index = ln.find(variable.fBegin) + variable.fBegin.size(); + + while (ln[string_index] == ' ') + ++string_index; + + std::string keyword; + + for (; string_index < ln.size(); ++string_index) + { + if (ln[string_index] == variable.fEnd[0]) + { + std::string varname = ""; + + for (size_t index_keyword = ln.find(' '); + ln[index_keyword] != variable.fBegin[0]; ++index_keyword) + { + if (ln[index_keyword] == ' ') + { + continue; + } + + if (isdigit(ln[index_keyword])) + { + goto cc_next_loop; + } + + varname += ln[index_keyword]; + } + + if (varname.find(' ') != std::string::npos) + { + varname.erase(0, varname.find(' ')); + + if (variable.fBegin == "extern") + { + varname.erase(0, varname.find(' ')); + } + } + + if (kRegisterCounter == 5 || kRegisterCounter == 6) + ++kRegisterCounter; + + std::string reg = kAsmRegisterPrefix; + reg += std::to_string(kRegisterCounter); + + kCompilerVariables.push_back({.fValue = varname}); + goto cc_check_done; + } + + keyword.push_back(ln[string_index]); + } + + goto cc_next_loop; + + cc_check_done: + + // skip digit value. + if (isdigit(keyword[0]) || keyword[0] == '"') + { + goto cc_next_loop; + } + + while (keyword.find(' ') != std::string::npos) + keyword.erase(keyword.find(' '), 1); + + for (auto& var : kCompilerVariables) + { + if (var.fValue.find(keyword) != std::string::npos) + { + err_str.clear(); + goto cc_next; + } + } + + for (auto& fn : kCompilerFunctions) + { + if (fn.find(keyword[0]) != std::string::npos) + { + auto where_begin = fn.find(keyword[0]); + auto keyword_begin = 0UL; + auto failed = false; + + for (; where_begin < keyword.size(); ++where_begin) + { + if (fn[where_begin] == '(' && keyword[keyword_begin] == '(') + break; + + if (fn[where_begin] != keyword[keyword_begin]) + { + failed = true; + break; + } + + ++keyword_begin; + } + + if (!failed) + { + err_str.clear(); + goto cc_next; + } + else + { + continue; + } + } + } + + cc_error_value: + if (keyword.find("->") != std::string::npos) + return err_str; + + if (keyword.find(".") != std::string::npos) + return err_str; + + if (isalnum(keyword[0])) + err_str += "\nUndefined value: " + keyword; + + return err_str; + } + + cc_next_loop: + continue; + } + +cc_next: + + // extern does not declare anything, it imports a variable. + // so that's why it's not declare upper. + if (ParserKit::find_word(ln, "extern")) + { + auto substr = ln.substr(ln.find("extern") + strlen("extern")); + kCompilerVariables.push_back({.fValue = substr}); + } + + if (kShouldHaveBraces && ln.find('{') == std::string::npos) + { + err_str += "Missing '{' for function "; + err_str += kFnName; + err_str += "\n"; + + kShouldHaveBraces = false; + kFnName.clear(); + } + else if (kShouldHaveBraces && ln.find('{') != std::string::npos) + { + kShouldHaveBraces = false; + kFnName.clear(); + } + + bool type_not_found = true; + + if (ln.find('\'') != std::string::npos) + { + ln.replace(ln.find('\''), 3, "0"); + } + + auto first = ln.find('"'); + if (first != std::string::npos) + { + auto second = 0UL; + bool found_second_quote = false; + + for (size_t i = first + 1; i < ln.size(); ++i) + { + if (ln[i] == '\"') + { + found_second_quote = true; + second = i; + + break; + } + } + + if (!found_second_quote) + { + err_str += "Missing terminating \"."; + err_str += " here -> " + ln.substr(ln.find('"'), second); + } + } + + if (ln.find(')') != std::string::npos && ln.find(';') == std::string::npos) + { + if (ln.find('{') == std::string::npos) + { + kFnName = ln; + kShouldHaveBraces = true; + + goto skip_braces_check; + } + else if (ln.find('{') != std::string::npos) + { + kShouldHaveBraces = false; + } + } + +skip_braces_check: + + for (auto& key : kCompilerTypes) + { + if (ParserKit::find_word(ln, key.fName)) + { + if (isdigit(ln[ln.find(key.fName) + key.fName.size() + 1])) + { + err_str += "\nNumber cannot be set for "; + err_str += key.fName; + err_str += "'s name. here -> "; + err_str += ln; + } + + if (ln.find(key.fName) == 0 || ln[ln.find(key.fName) - 1] == ' ' || + ln[ln.find(key.fName) - 1] == '\t') + { + type_not_found = false; + + if (ln[ln.find(key.fName) + key.fName.size()] != ' ') + { + type_not_found = true; + + if (ln[ln.find(key.fName) + key.fName.size()] == '\t') + type_not_found = false; + + goto next; + } + else if (ln[ln.find(key.fName) + key.fName.size()] != '\t') + { + type_not_found = true; + + if (ln[ln.find(key.fName) + key.fName.size()] == ' ') + type_not_found = false; + } + } + + next: + + if (ln.find(';') == std::string::npos) + { + if (ln.find('(') != std::string::npos) + { + if (ln.find('=') == std::string::npos) + continue; + } + + err_str += "\nMissing ';', here -> "; + err_str += ln; + } + else + { + continue; + } + + if (ln.find('=') != std::string::npos) + { + if (ln.find('(') != std::string::npos) + { + if (ln.find(')') == std::string::npos) + { + err_str += "\nMissing ')', after '(' here -> "; + err_str += ln.substr(ln.find('(')); + } + } + } + } + } + + if (kInBraces && ln.find("struct") != std::string::npos && + ln.find("union") != std::string::npos && + ln.find("enum") != std::string::npos && + ln.find('=') != std::string::npos) + { + if (ln.find(';') == std::string::npos) + { + err_str += "\nMissing ';' after struct/union/enum declaration, here -> "; + err_str += ln; + } + } + + if (ln.find(';') != std::string::npos && + ln.find("for") == std::string::npos) + { + if (ln.find(';') + 1 != ln.size()) + { + for (int i = 0; i < ln.substr(ln.find(';') + 1).size(); ++i) + { + if ((ln.substr(ln.find(';') + 1)[i] != ' ') || + (ln.substr(ln.find(';') + 1)[i] != '\t')) + { + if (auto err = this->Check(ln.substr(ln.find(';') + 1).c_str(), file); + !err.empty()) + { + err_str += "\nUnexpected text after ';' -> "; + err_str += ln.substr(ln.find(';')); + err_str += err; + } + } + } + } + } + + if (ln.find('(') != std::string::npos) + { + if (ln.find(';') == std::string::npos && !ParserKit::find_word(ln, "|") && + !ParserKit::find_word(ln, "||") && !ParserKit::find_word(ln, "&") && + !ParserKit::find_word(ln, "&&") && !ParserKit::find_word(ln, "~")) + { + bool found_func = false; + size_t i = ln.find('('); + std::vector opens; + std::vector 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 : public CompilerKit::AssemblyInterface +{ +public: + explicit AssemblyCCInterface() = default; + ~AssemblyCCInterface() override = default; + + MPCC_COPY_DEFAULT(AssemblyCCInterface); + + [[maybe_unused]] static Int32 Arch() noexcept + { + return CompilerKit::AssemblyFactory::kArch64x0; + } + + Int32 CompileToFormat(std::string& src, Int32 arch) override + { + if (arch != AssemblyCCInterface::Arch()) + return -1; + + if (kCompilerBackend == nullptr) + return -1; + + /* @brief copy contents wihtout extension */ + std::string src_file = src.data(); + std::ifstream src_fp = std::ifstream(src_file, std::ios::in); + std::string dest; + + for (auto& ch : src_file) + { + if (ch == '.') + { + break; + } + + dest += ch; + } + + /* According to PEF ABI. */ + std::vector exts = kAsmFileExts; + dest += exts[4]; + + kState.fOutputAssembly = std::make_unique(dest); + + auto fmt = CompilerKit::current_date(); + + (*kState.fOutputAssembly) << "# Path: " << src_file << "\n"; + (*kState.fOutputAssembly) + << "# Language: 64x0 Assembly (Generated from ANSI C)\n"; + (*kState.fOutputAssembly) << "# Date: " << fmt << "\n\n"; + + ParserKit::SyntaxLeafList syntax; + + kState.fSyntaxTreeList.push_back(syntax); + kState.fSyntaxTree = + &kState.fSyntaxTreeList[kState.fSyntaxTreeList.size() - 1]; + + std::string line_src; + + while (std::getline(src_fp, line_src)) + { + if (auto err = kCompilerBackend->Check(line_src.c_str(), src.data()); + err.empty()) + { + kCompilerBackend->Compile(line_src, src.data()); + } + else + { + detail::print_error(err, src.data()); + } + } + + if (kAcceptableErrors > 0) + return -1; + + std::vector keywords = {"ldw", "stw", "lda", "sta", + "add", "sub", "mv"}; + + /// + /// Replace, optimize, fix assembly output. + /// + + for (auto& leaf : kState.fSyntaxTree->fLeafList) + { + std::vector access_keywords = {"->", "."}; + + for (auto& access_ident : access_keywords) + { + if (ParserKit::find_word(leaf.fUserValue, access_ident)) + { + for (auto& struc : kState.kStructMap) + { + /// TODO: + } + } + } + + for (auto& keyword : keywords) + { + if (ParserKit::find_word(leaf.fUserValue, keyword)) + { + std::size_t cnt = 0UL; + + for (auto& reg : kState.kStackFrame) + { + std::string needle; + + for (size_t i = 0; i < reg.fName.size(); i++) + { + if (reg.fName[i] == ' ') + { + ++i; + + for (; i < reg.fName.size(); i++) + { + if (reg.fName[i] == ',') + { + break; + } + + if (reg.fName[i] == ' ') + continue; + + needle += reg.fName[i]; + } + + break; + } + } + + if (ParserKit::find_word(leaf.fUserValue, needle)) + { + if (leaf.fUserValue.find("import " + needle) != + std::string::npos) + { + std::string range = "import " + needle; + leaf.fUserValue.replace( + leaf.fUserValue.find("import " + needle), range.size(), + needle); + } + + if (leaf.fUserValue.find("ldw r6") != std::string::npos) + { + std::string::difference_type countComma = std::count( + leaf.fUserValue.begin(), leaf.fUserValue.end(), ','); + + if (countComma == 1) + { + leaf.fUserValue.replace(leaf.fUserValue.find("ldw"), + strlen("ldw"), "mv"); + } + } + + leaf.fUserValue.replace(leaf.fUserValue.find(needle), + needle.size(), reg.fReg); + + ++cnt; + } + } + + if (cnt > 1 && keyword != "mv" && keyword != "add" && + keyword != "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 kOk; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +#include + +#define kPrintF printf +#define kSplashCxx() \ + kPrintF(kWhite "Zeta C Driver, %s, (c) ZKA Technologies\n", kDistVersion) + +static void cc_print_help() +{ + kSplashCxx(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define kExt ".c" + +NDK_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 = CompilerKit::AssemblyFactory::kArch64x0; + kCompilerBackend = new CompilerBackendCLang(); + + for (auto index = 1UL; index < argc; ++index) + { + if (skip) + { + skip = false; + continue; + } + + if (argv[index][0] == '/') + { + if (strcmp(argv[index], "/v") == 0 || + strcmp(argv[index], "/version") == 0) + { + kSplashCxx(); + return kOk; + } + + if (strcmp(argv[index], "/verbose") == 0) + { + kState.fVerbose = true; + + continue; + } + + if (strcmp(argv[index], "/h") == 0 || strcmp(argv[index], "/help") == 0) + { + cc_print_help(); + + return kOk; + } + + if (strcmp(argv[index], "/dialect") == 0) + { + if (kCompilerBackend) + std::cout << kCompilerBackend->Language() << "\n"; + + return kOk; + } + + if (strcmp(argv[index], "/fmax-exceptions") == 0) + { + try + { + kErrorLimit = std::strtol(argv[index + 1], nullptr, 10); + } + // catch anything here + catch (...) + { + kErrorLimit = 0; + } + + skip = true; + + continue; + } + + std::string err = "Unknown command: "; + err += argv[index]; + + detail::print_error(err, "cc"); + + continue; + } + + kFileList.emplace_back(argv[index]); + + std::string srcFile = argv[index]; + + if (strstr(argv[index], kExt) == nullptr) + { + if (kState.fVerbose) + { + detail::print_error(srcFile + " is not a valid C source.\n", "cc"); + } + + return 1; + } + + if (kFactory.Compile(srcFile, kMachine) != kOk) + return -1; + } + + return kOk; +} + +// Last rev 8-1-24 diff --git a/Sources/AssemblyFactory.cc b/Sources/AssemblyFactory.cc deleted file mode 100644 index 35d8cbd..0000000 --- a/Sources/AssemblyFactory.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#include -#include - -/** - * @file AssemblyFactory.cc - * @author amlal (amlal@zeta.com) - * @brief Assembler Kit - * @version 0.1 - * @date 2024-01-27 - * - * @copyright Copyright (c) 2024, ZKA Technologies - * - */ - -#include - -//! @file AsmKit.cpp -//! @brief AssemblyKit source implementation. - -namespace CompilerKit -{ - ///! @brief Compile for specific format (ELF, PEF, ZBIN) - Int32 AssemblyFactory::Compile(std::string& sourceFile, - const Int32& arch) noexcept - { - if (sourceFile.length() < 1 || !fMounted) - return MPCC_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 CompilerKit diff --git a/Sources/AssemblyFactory.cxx b/Sources/AssemblyFactory.cxx new file mode 100644 index 0000000..61dbebd --- /dev/null +++ b/Sources/AssemblyFactory.cxx @@ -0,0 +1,59 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#include +#include + +/** + * @file AssemblyFactory.cxx + * @author amlal (amlal@zeta.com) + * @brief Assembler Kit + * @version 0.1 + * @date 2024-01-27 + * + * @copyright Copyright (c) 2024, ZKA Technologies + * + */ + +#include + +//! @file AsmKit.cpp +//! @brief AssemblyKit source implementation. + +namespace CompilerKit +{ + ///! @brief Compile for specific format (ELF, PEF, ZBIN) + Int32 AssemblyFactory::Compile(std::string& sourceFile, + const Int32& arch) noexcept + { + if (sourceFile.length() < 1 || !fMounted) + return MPCC_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 CompilerKit diff --git a/Sources/Detail/asmutils.hxx b/Sources/Detail/asmutils.hxx index 00bfbd7..bf05830 100644 --- a/Sources/Detail/asmutils.hxx +++ b/Sources/Detail/asmutils.hxx @@ -6,8 +6,8 @@ #pragma once -#include -#include +#include +#include using namespace CompilerKit; diff --git a/Sources/Detail/compilerutils.hxx b/Sources/Detail/compilerutils.hxx index 4c46727..6f6361c 100644 --- a/Sources/Detail/compilerutils.hxx +++ b/Sources/Detail/compilerutils.hxx @@ -6,8 +6,8 @@ #pragma once -#include -#include +#include +#include #define kZero64Section ".zero64" #define kCode64Section ".code64" diff --git a/Sources/String.cc b/Sources/String.cc deleted file mode 100644 index accc1af..0000000 --- a/Sources/String.cc +++ /dev/null @@ -1,200 +0,0 @@ -/* - * ======================================================== - * - * CompilerKit - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -/** - * @file String.cc - * @author Amlal (amlal@mahrouss-logic.com) - * @brief C++ string manipulation API. - * @version 0.2 - * @date 2024-01-23 - * - * @copyright Copyright (c) ZKA Technologies - * - */ - -#include - -namespace CompilerKit { -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[8]; - if (!to_str(result, sizeof(int), 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 = idx; y_idx < res_len; ++y_idx) { - ret[result_cnt] = result[y_idx]; - ++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 CompilerKit diff --git a/Sources/String.cxx b/Sources/String.cxx new file mode 100644 index 0000000..8b8cb9c --- /dev/null +++ b/Sources/String.cxx @@ -0,0 +1,200 @@ +/* + * ======================================================== + * + * CompilerKit + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +/** + * @file String.cxx + * @author Amlal (amlal@mahrouss-logic.com) + * @brief C++ string manipulation API. + * @version 0.2 + * @date 2024-01-23 + * + * @copyright Copyright (c) ZKA Technologies + * + */ + +#include + +namespace CompilerKit { +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[8]; + if (!to_str(result, sizeof(int), 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 = idx; y_idx < res_len; ++y_idx) { + ret[result_cnt] = result[y_idx]; + ++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 CompilerKit diff --git a/Sources/bpp.cc b/Sources/bpp.cc deleted file mode 100644 index 1aefc33..0000000 --- a/Sources/bpp.cc +++ /dev/null @@ -1,899 +0,0 @@ -/* - * ======================================================== - * - * bpp - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -/// BUGS: 0 - -#include -#include -#include -#include -#include -#include -#include - -#define kMacroPrefix '%' - -/// @author Amlal El Mahrouss (amlel) -/// @file bpp.cc -/// @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 fArgs; - std::string fName; - std::string fValue; -}; - -class bpp_pragma final { - public: - explicit bpp_pragma() = default; - ~bpp_pragma() = default; - - MPCC_COPY_DEFAULT(bpp_pragma); - - std::string fMacroName; - bpp_parser_fn_t fParse; -}; -} // namespace details - -static std::vector kFiles; -static std::vector kMacros; -static std::vector kIncludes; - -static std::string kWorkingDir; - -static std::vector 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 ¯o, bool &inactive_code, - bool &defined, std::string ¯o_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 ¯o_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 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)) { - /// BPP Documentation. - if (hdr_line.find("@bdoc") != std::string::npos) { - hdr_line.erase(hdr_line.find("@bdoc")); - } - - 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 (ParserKit::find_word(hdr_line, macro.fName) && - hdr_line.find("%def") == std::string::npos) { - auto value = macro.fValue; - - hdr_line.replace(hdr_line.find(macro.fName), macro.fName.size(), - value); - } - } - - if (hdr_line[0] == kMacroPrefix && - hdr_line.find("def ") != std::string::npos) { - auto line_after_define = - hdr_line.substr(hdr_line.find("def ") + strlen("def ")); - - std::string macro_value; - std::string macro_key; - - std::size_t pos = 0UL; - - std::vector 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::vector dupls; - std::string str; - - line_after_define.erase(0, line_after_define.find("(") + 1); - - for (auto &subc : line_after_define) { - if (subc == ',' || subc == ')') { - if (str.empty()) continue; - - dupls.push_back(str); - args.push_back(str); - - str.clear(); - - continue; - } - - if (isalnum(subc)) str.push_back(subc); - } - - for (auto &dupl : dupls) { - std::size_t cnt = 0; - - for (auto &arg : args) { - if (dupl == arg) ++cnt; - } - - if (cnt > 1) { - auto it = std::find(args.begin(), args.end(), dupl); - - while (it != args.end()) { - args.erase(it); - it = std::find(args.begin(), args.end(), dupl); - } - } - } - - 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; - } - - for (auto ¯o : kMacros) { - if (hdr_line.find(macro.fName) != std::string::npos) { - std::vector arg_values; - - if (macro.fArgs.size() > 0) { - for (size_t i = 0; i < hdr_line.size(); ++i) { - if (hdr_line[i] == '(') { - std::string tmp_arg; - - for (size_t x = i; x < hdr_line.size(); x++) { - if (hdr_line[x] == ')') break; - - if (hdr_line[x] == ' ') continue; - - if (hdr_line[i] == '\\') continue; - - if (hdr_line[x] == ',') { - arg_values.push_back(tmp_arg); - tmp_arg.clear(); - continue; - } - - tmp_arg += hdr_line[x]; - } - - break; - } - } - - std::string symbol; - - for (char i : macro.fValue) { - if (i == '(') break; - - if (i == '\\') continue; - - symbol += i; - } - - hdr_line.replace(hdr_line.find(macro.fName), macro.fName.size(), - symbol); - - size_t x_arg_indx = 0; - - for (size_t i = hdr_line.find(macro.fValue); i < hdr_line.size(); - ++i) { - if (hdr_line.find(macro.fArgs[x_arg_indx]) == i) { - hdr_line.replace(i, macro.fArgs[x_arg_indx].size(), - arg_values[x_arg_indx]); - ++x_arg_indx; - } - } - } else { - std::string symbol; - - for (size_t i = 0; i < macro.fValue.size(); i++) { - if (macro.fValue[i] == ' ') continue; - - if (macro.fValue[i] == '\\') continue; - - symbol += macro.fValue[i]; - } - - hdr_line.replace(hdr_line.find(macro.fName), macro.fName.size(), - symbol); - } - - break; - } - } - - 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 ¯o_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 ¯o_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("pragma") != std::string::npos) { - line_after_include = hdr_line.substr(hdr_line.find("pragma once")); - - // search for this file - auto it = std::find(kAllIncludes.cbegin(), kAllIncludes.cend(), - line_after_include); - - if (it == kAllIncludes.cend()) { - goto kIncludeFile; - } - } else if (hdr_line[0] == kMacroPrefix && - hdr_line.find("if") != std::string::npos) { - inactive_code = true; - - std::vector 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 ¯o_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 ¯o_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 << "Warning: " << 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("inc ") != std::string::npos) { - line_after_include = - hdr_line.substr(hdr_line.find("inc ") + strlen("inc ")); - - 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 == '\'') { - enable = true; - continue; - } - - if (enable) { - if (not_local) { - if (ch == '>') break; - } else { - if (ch == '\'') { - break; - } - } - - path += ch; - } - } - - if (not_local) { - bool open = false; - - 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(kWorkingDir + 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. - -///////////////////////////////////////////////////////////////////////////////////////// - -NDK_MODULE(NewOSPreprocessor) { - 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_hcore; - macro_hcore.fName = "__MAHROUSS__"; - macro_hcore.fValue = "1"; - - kMacros.push_back(macro_hcore); - - 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], "/version") == 0) { - printf("%s\n", "bpp v1.11, (c) ZKA Technologies"); - return 0; - } - - if (strcmp(argv[index], "/help") == 0) { - printf("%s\n", "Zeta Preprocessor Driver v1.11, (c) ZKA Technologies"); - printf("%s\n", "/working-dir : set directory to working path."); - printf("%s\n", "/include-dir : add directory to include path."); - printf("%s\n", "/def : def macro."); - printf("%s\n", "/version: print the version."); - printf("%s\n", "/help: show help."); - - return 0; - } - - if (strcmp(argv[index], "/include-dir") == 0) { - std::string inc = argv[index + 1]; - - skip = true; - - kIncludes.push_back(inc); - } - - if (strcmp(argv[index], "/working-dir") == 0) { - std::string inc = argv[index + 1]; - skip = true; - kWorkingDir = inc; - } - - if (strcmp(argv[index], "/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 MPCC_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/Sources/bpp.cxx b/Sources/bpp.cxx new file mode 100644 index 0000000..e027c47 --- /dev/null +++ b/Sources/bpp.cxx @@ -0,0 +1,899 @@ +/* + * ======================================================== + * + * bpp + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +/// BUGS: 0 + +#include +#include +#include +#include +#include +#include +#include + +#define kMacroPrefix '%' + +/// @author Amlal El Mahrouss (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 fArgs; + std::string fName; + std::string fValue; +}; + +class bpp_pragma final { + public: + explicit bpp_pragma() = default; + ~bpp_pragma() = default; + + MPCC_COPY_DEFAULT(bpp_pragma); + + std::string fMacroName; + bpp_parser_fn_t fParse; +}; +} // namespace details + +static std::vector kFiles; +static std::vector kMacros; +static std::vector kIncludes; + +static std::string kWorkingDir; + +static std::vector 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 ¯o, bool &inactive_code, + bool &defined, std::string ¯o_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 ¯o_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 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)) { + /// BPP Documentation. + if (hdr_line.find("@bdoc") != std::string::npos) { + hdr_line.erase(hdr_line.find("@bdoc")); + } + + 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 (ParserKit::find_word(hdr_line, macro.fName) && + hdr_line.find("%def") == std::string::npos) { + auto value = macro.fValue; + + hdr_line.replace(hdr_line.find(macro.fName), macro.fName.size(), + value); + } + } + + if (hdr_line[0] == kMacroPrefix && + hdr_line.find("def ") != std::string::npos) { + auto line_after_define = + hdr_line.substr(hdr_line.find("def ") + strlen("def ")); + + std::string macro_value; + std::string macro_key; + + std::size_t pos = 0UL; + + std::vector 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::vector dupls; + std::string str; + + line_after_define.erase(0, line_after_define.find("(") + 1); + + for (auto &subc : line_after_define) { + if (subc == ',' || subc == ')') { + if (str.empty()) continue; + + dupls.push_back(str); + args.push_back(str); + + str.clear(); + + continue; + } + + if (isalnum(subc)) str.push_back(subc); + } + + for (auto &dupl : dupls) { + std::size_t cnt = 0; + + for (auto &arg : args) { + if (dupl == arg) ++cnt; + } + + if (cnt > 1) { + auto it = std::find(args.begin(), args.end(), dupl); + + while (it != args.end()) { + args.erase(it); + it = std::find(args.begin(), args.end(), dupl); + } + } + } + + 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; + } + + for (auto ¯o : kMacros) { + if (hdr_line.find(macro.fName) != std::string::npos) { + std::vector arg_values; + + if (macro.fArgs.size() > 0) { + for (size_t i = 0; i < hdr_line.size(); ++i) { + if (hdr_line[i] == '(') { + std::string tmp_arg; + + for (size_t x = i; x < hdr_line.size(); x++) { + if (hdr_line[x] == ')') break; + + if (hdr_line[x] == ' ') continue; + + if (hdr_line[i] == '\\') continue; + + if (hdr_line[x] == ',') { + arg_values.push_back(tmp_arg); + tmp_arg.clear(); + continue; + } + + tmp_arg += hdr_line[x]; + } + + break; + } + } + + std::string symbol; + + for (char i : macro.fValue) { + if (i == '(') break; + + if (i == '\\') continue; + + symbol += i; + } + + hdr_line.replace(hdr_line.find(macro.fName), macro.fName.size(), + symbol); + + size_t x_arg_indx = 0; + + for (size_t i = hdr_line.find(macro.fValue); i < hdr_line.size(); + ++i) { + if (hdr_line.find(macro.fArgs[x_arg_indx]) == i) { + hdr_line.replace(i, macro.fArgs[x_arg_indx].size(), + arg_values[x_arg_indx]); + ++x_arg_indx; + } + } + } else { + std::string symbol; + + for (size_t i = 0; i < macro.fValue.size(); i++) { + if (macro.fValue[i] == ' ') continue; + + if (macro.fValue[i] == '\\') continue; + + symbol += macro.fValue[i]; + } + + hdr_line.replace(hdr_line.find(macro.fName), macro.fName.size(), + symbol); + } + + break; + } + } + + 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 ¯o_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 ¯o_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("pragma") != std::string::npos) { + line_after_include = hdr_line.substr(hdr_line.find("pragma once")); + + // search for this file + auto it = std::find(kAllIncludes.cbegin(), kAllIncludes.cend(), + line_after_include); + + if (it == kAllIncludes.cend()) { + goto kIncludeFile; + } + } else if (hdr_line[0] == kMacroPrefix && + hdr_line.find("if") != std::string::npos) { + inactive_code = true; + + std::vector 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 ¯o_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 ¯o_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 << "Warning: " << 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("inc ") != std::string::npos) { + line_after_include = + hdr_line.substr(hdr_line.find("inc ") + strlen("inc ")); + + 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 == '\'') { + enable = true; + continue; + } + + if (enable) { + if (not_local) { + if (ch == '>') break; + } else { + if (ch == '\'') { + break; + } + } + + path += ch; + } + } + + if (not_local) { + bool open = false; + + 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(kWorkingDir + 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. + +///////////////////////////////////////////////////////////////////////////////////////// + +NDK_MODULE(NewOSPreprocessor) { + 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_hcore; + macro_hcore.fName = "__MAHROUSS__"; + macro_hcore.fValue = "1"; + + kMacros.push_back(macro_hcore); + + 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], "/version") == 0) { + printf("%s\n", "bpp v1.11, (c) ZKA Technologies"); + return 0; + } + + if (strcmp(argv[index], "/help") == 0) { + printf("%s\n", "Zeta Preprocessor Driver v1.11, (c) ZKA Technologies"); + printf("%s\n", "/working-dir : set directory to working path."); + printf("%s\n", "/include-dir : add directory to include path."); + printf("%s\n", "/def : def macro."); + printf("%s\n", "/version: print the version."); + printf("%s\n", "/help: show help."); + + return 0; + } + + if (strcmp(argv[index], "/include-dir") == 0) { + std::string inc = argv[index + 1]; + + skip = true; + + kIncludes.push_back(inc); + } + + if (strcmp(argv[index], "/working-dir") == 0) { + std::string inc = argv[index + 1]; + skip = true; + kWorkingDir = inc; + } + + if (strcmp(argv[index], "/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 MPCC_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/Sources/coff2ae.cc b/Sources/coff2ae.cc deleted file mode 100644 index 71f5bba..0000000 --- a/Sources/coff2ae.cc +++ /dev/null @@ -1,23 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#include -#include -#include -#include -#include -#include -#include -#include - -///////////////////////////////////////////////////////////////////////////////////////// - -/// @file coff2ae.cc -/// @brief COFF To AE, the program/module starts here. - -///////////////////////////////////////////////////////////////////////////////////////// - -NDK_MODULE(NewOSCOFFToAE) { return 0; } diff --git a/Sources/coff2ae.cxx b/Sources/coff2ae.cxx new file mode 100644 index 0000000..2f7e062 --- /dev/null +++ b/Sources/coff2ae.cxx @@ -0,0 +1,23 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @file coff2ae.cxx +/// @brief COFF To AE, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +NDK_MODULE(NewOSCOFFToAE) { return 0; } diff --git a/Sources/compile_flags.txt b/Sources/compile_flags.txt index f9acaba..6ec6b8a 100644 --- a/Sources/compile_flags.txt +++ b/Sources/compile_flags.txt @@ -1,5 +1,5 @@ -std=c++20 -I../ --I../Comm +-I../Headers -I./ -I./Detail/ diff --git a/Sources/cplusplus.cc b/Sources/cplusplus.cc deleted file mode 100644 index 4059413..0000000 --- a/Sources/cplusplus.cc +++ /dev/null @@ -1,905 +0,0 @@ -/* - * ======================================================== - * - * cplusplus - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -/// bugs: 0 - -#define __PK_USE_STRUCT_INSTEAD__ 1 - -#define kPrintF printf - -#define kSplashCxx() \ -kPrintF(kWhite "%s\n", "Zeta C++ Compiler Driver, (c) 2024 Zeta Electronics, all rights reserved.") - -// import, @MLAutoRelease { ... }, fn foo() -> auto { ... } - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define kOk 0 - -/* ZKA Technologies C++ driver */ -/* This is part of ZECC C++ compiler. */ -/* (c) ZKA Technologies */ - -/// @author Amlal El Mahrouss (amlel) -/// @file cc.cc -/// @brief Optimized C++ Compiler. -/// @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 detail -{ - 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> fOffsets; - }; - - struct CompilerState final - { - std::vector fSyntaxTreeList; - std::vector kStackFrame; - std::vector kStructMap; - ParserKit::SyntaxLeafList* fSyntaxTree{nullptr}; - std::unique_ptr fOutputAssembly; - std::string fLastFile; - std::string fLastError; - bool fVerbose; - }; -} // namespace detail - -static detail::CompilerState kState; -static SizeType kErrorLimit = 100; - -static Int32 kAcceptableErrors = 0; - -namespace detail -{ - /// @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 - { - if (reason[0] == '\n') - reason.erase(0, 1); - - if (file.find(".pp") != std::string::npos) - { - file.erase(file.find(".pp"), 3); - } - - if (kState.fLastFile != file) - { - std::cout << kRed << "[ cplusplus ] " << kWhite - << ((file == "cplusplus") ? "internal compiler error " - : ("in file, " + file)) - << kBlank << std::endl; - std::cout << kRed << "[ cplusplus ] " << kWhite << reason << kBlank - << std::endl; - - kState.fLastFile = file; - } - else - { - std::cout << kRed << "[ cplusplus ] [ " << kState.fLastFile << " ] " << kWhite - << reason << kBlank << std::endl; - } - - if (kAcceptableErrors > kErrorLimit) - std::exit(3); - - ++kAcceptableErrors; - } - - struct CompilerType - { - std::string fName; - std::string fValue; - }; -} // namespace detail - -///////////////////////////////////////////////////////////////////////////////////////// - -// Target architecture. -static int kMachine = CompilerKit::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 kKeywords; - -///////////////////////////////////////// - -// COMPILER PARSING UTILITIES/STATES. - -///////////////////////////////////////// - -static std::vector kFileList; -static CompilerKit::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 Zeta C++ driver */ -class CompilerBackendCPlusPlus final : public ParserKit::CompilerBackend -{ -public: - explicit CompilerBackendCPlusPlus() = default; - ~CompilerBackendCPlusPlus() override = default; - - MPCC_COPY_DEFAULT(CompilerBackendCPlusPlus); - - bool Compile(const std::string& text, const char* file) override; - - const char* Language() override; -}; - -/// @internal compiler variables - -static CompilerBackendCPlusPlus* kCompilerBackend = nullptr; -static std::vector kCompilerVariables; -static std::vector kCompilerFunctions; - -static std::vector kRegisterMap; - -static std::vector cRegisters = { - "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 cRegistersCall = { - "rcx", - "rdx", - "r8", - "r9", - "xmm8", - "xmm9", - "xmm10", - "xmm11", -}; - -static size_t kLevelFunction = 0UL; - -/// detail namespaces - -const char* CompilerBackendCPlusPlus::Language() -{ - return "Zeta C++"; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -/// @name Compile -/// @brief Generate MASM assembly from a C++ source. - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CompilerBackendCPlusPlus::Compile(const std::string& text, - const char* file) -{ - if (text.empty()) - return false; - - // if (expr) - // int name = expr; - // expr; - - std::size_t index = 0UL; - std::vector> 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 ParserKit::eKeywordKindCommentMultiLineStart: { - commentBlock = true; - return true; - } - case ParserKit::eKeywordKindCommentMultiLineEnd: { - commentBlock = false; - break; - } - case ParserKit::eKeywordKindCommentInline: { - break; - } - default: - break; - } - - if (text[text.find(keyword.keyword_name) - 1] == '+' && - keyword.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableAssign) - continue; - - if (text[text.find(keyword.keyword_name) - 1] == '-' && - keyword.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableAssign) - continue; - - if (text[text.find(keyword.keyword_name) + 1] == '=' && - keyword.keyword_kind == ParserKit::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])) - { - detail::print_error("syntax error: " + text, file); - return false; - } - } - } - - for (auto& keyword : keywords_list) - { - auto syntax_tree = ParserKit::SyntaxLeafList::SyntaxLeaf(); - - switch (keyword.first.keyword_kind) - { - case ParserKit::KeywordKind::eKeywordKindFunctionStart: { - std::string fnName = text; - fnName.erase(fnName.find(keyword.first.keyword_name)); - - for (auto& ch : fnName) - { - if (ch == ' ') - ch = '_'; - } - - syntax_tree.fUserValue = "export .code64 __MPCC_" + fnName + "\n"; - - ++kLevelFunction; - break; - } - case ParserKit::KeywordKind::eKeywordKindFunctionEnd: { - --kLevelFunction; - - if (kRegisterMap.size() > cRegisters.size()) - { - --kLevelFunction; - } - - if (kLevelFunction < 1) - kRegisterMap.clear(); - break; - } - case ParserKit::KeywordKind::eKeywordKindEndInstr: - case ParserKit::KeywordKind::eKeywordKindVariableInc: - case ParserKit::KeywordKind::eKeywordKindVariableDec: - case ParserKit::KeywordKind::eKeywordKindVariableAssign: { - std::string valueOfVar = ""; - - if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableInc) - { - valueOfVar = text.substr(text.find("+=") + 2); - } - else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableDec) - { - valueOfVar = text.substr(text.find("-=") + 2); - } - else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableAssign) - { - valueOfVar = text.substr(text.find("=") + 1); - } - else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindEndInstr) - { - valueOfVar = "0\n"; - } - - while (valueOfVar.find(";") != std::string::npos && - keyword.first.keyword_kind != ParserKit::KeywordKind::eKeywordKindEndInstr) - { - valueOfVar.erase(valueOfVar.find(";")); - } - - std::string varName = text; - - if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableInc) - { - varName.erase(varName.find("+=")); - } - else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableDec) - { - varName.erase(varName.find("-=")); - } - else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableAssign) - { - varName.erase(varName.find("=")); - } - else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindEndInstr) - { - varName.erase(varName.find(";")); - } - - bool typeFound = false; - - for (auto& keyword : kKeywords) - { - if (keyword.keyword_kind == ParserKit::eKeywordKindType) - { - if (varName.find(keyword.keyword_name) != std::string::npos) - { - typeFound = true; - varName.erase(varName.find(keyword.keyword_name), keyword.keyword_name.size()); - } - - /// in case we goot boolX or intX - if (text.find(keyword.keyword_name) != std::string::npos) - { - if (varName[text.find(keyword.keyword_name)] == ' ') - continue; - - typeFound = false; - } - } - } - - std::string instr = "mov "; - - if (typeFound) - { - if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableInc) - { - detail::print_error("Can't increment variable when it's being created.", file); - } - else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableDec) - { - detail::print_error("Can't decrement variable when it's being created.", file); - } - - if (kRegisterMap.size() > cRegisters.size()) - { - ++kLevelFunction; - } - - 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) - { - syntax_tree.fUserValue = instr + cRegisters[kRegisterMap.size()] + ", " + valueOfVar + "\n"; - continue; - } - - syntax_tree.fUserValue = instr + cRegisters[kRegisterMap.size()] + ", " + cRegisters[indexRight - 1] + "\n"; - break; - } - - if (((int)indexRight - 1) < 0) - { - syntax_tree.fUserValue = instr + cRegisters[kRegisterMap.size()] + ", " + valueOfVar + "\n"; - } - - kRegisterMap.push_back(varName); - } - else - { - if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindEndInstr) - { - syntax_tree.fUserValue = "\n"; - continue; - } - - if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableInc) - { - instr = "add "; - } - else if (keyword.first.keyword_kind == ParserKit::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); - } - - 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 != valueOfVar) - { - syntax_tree.fUserValue = instr + cRegisters[kRegisterMap.size()] + ", " + valueOfVar + "\n"; - continue; - } - - syntax_tree.fUserValue = instr + cRegisters[indxReg - 1] + ", " + cRegisters[indexRight - 1] + "\n"; - break; - } - - break; - } - - if (syntax_tree.fUserValue.empty()) - { - detail::print_error("Variable not declared: " + varErrCpy, file); - } - } - - break; - } - case ParserKit::KeywordKind::eKeywordKindReturn: { - 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, " + cRegisters[indxReg - 1] + "\r\nret\n"; - break; - } - - if (syntax_tree.fUserValue.empty()) - { - detail::print_error("Variable not declared: " + subText, file); - } - } - else - { - syntax_tree.fUserValue = "mov rax, " + subText + "\r\nret\n"; - } - } - else - { - syntax_tree.fUserValue = "mov rcx, " + subText + "\n"; - syntax_tree.fUserValue = "mov rax, rcx\r\nret\n"; - } - - break; - } - default: - break; - } - - syntax_tree.fUserData = keyword.first; - kState.fSyntaxTree->fLeafList.emplace_back(syntax_tree); - } - -_MpccOkay: - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -/** - * @brief C To Assembly mount-point. - */ - -///////////////////////////////////////////////////////////////////////////////////////// - -class AssemblyCPlusPlusInterface final : public CompilerKit::AssemblyInterface -{ -public: - explicit AssemblyCPlusPlusInterface() = default; - ~AssemblyCPlusPlusInterface() override = default; - - MPCC_COPY_DEFAULT(AssemblyCPlusPlusInterface); - - [[maybe_unused]] - static Int32 Arch() noexcept - { - return CompilerKit::AssemblyFactory::kArchAMD64; - } - - Int32 CompileToFormat(std::string& src, Int32 arch) override - { - if (arch != AssemblyCPlusPlusInterface::Arch()) - return -1; - - if (kCompilerBackend == nullptr) - return -1; - - /* @brief copy contents wihtout extension */ - std::string src_file = src.data(); - std::ifstream src_fp = std::ifstream(src_file, std::ios::in); - std::string dest; - - for (auto& ch : src_file) - { - if (ch == '.') - { - break; - } - - dest += ch; - } - - if (dest.empty()) - { - dest = "CXX-MPCC-"; - - std::random_device rd; - auto seed_data = std::array {}; - - 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); - } - - std::vector exts = kAsmFileExts; - - dest += exts[3]; - - std::cout << dest; - - kState.fOutputAssembly = std::make_unique(dest); - - auto fmt = CompilerKit::current_date(); - - (*kState.fOutputAssembly) << "; Path: " << src_file << "\n"; - (*kState.fOutputAssembly) - << "; Language: AMD64 assembly. (Generated from C++)\n"; - (*kState.fOutputAssembly) << "; Date: " << fmt << "\n"; - (*kState.fOutputAssembly) << "#bits 64\n#org 0x1000000" - << "\n"; - - ParserKit::SyntaxLeafList syntax; - - kState.fSyntaxTreeList.emplace_back(syntax); - kState.fSyntaxTree = - &kState.fSyntaxTreeList[kState.fSyntaxTreeList.size() - 1]; - - std::string source; - - while (std::getline(src_fp, source)) - { - // Compile into an object file. - kCompilerBackend->Compile(source.c_str(), src.c_str()); - } - - for (auto& ast : kState.fSyntaxTree->fLeafList) - { - (*kState.fOutputAssembly) << ast.fUserValue; - } - - if (kAcceptableErrors > 0) - return -1; - - return kOk; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -static void cxx_print_help() -{ - kSplashCxx(); - kPrintF("%s", "No help available, see:\n"); - kPrintF("%s", "www.zeta.com/developer/cplusplus\n"); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#define kExtListCxx \ - { \ - ".cpp", ".cxx", ".cc", ".c++", ".cp" \ - } - -NDK_MODULE(CompilerCPlusPlus) -{ - bool skip = false; - - kKeywords.push_back({.keyword_name = "class", .keyword_kind = ParserKit::eKeywordKindClass}); - kKeywords.push_back({.keyword_name = "struct", .keyword_kind = ParserKit::eKeywordKindClass}); - kKeywords.push_back({.keyword_name = "namespace", .keyword_kind = ParserKit::eKeywordKindNamespace}); - kKeywords.push_back({.keyword_name = "typedef", .keyword_kind = ParserKit::eKeywordKindTypedef}); - kKeywords.push_back({.keyword_name = "using", .keyword_kind = ParserKit::eKeywordKindTypedef}); - kKeywords.push_back({.keyword_name = "{", .keyword_kind = ParserKit::eKeywordKindBodyStart}); - kKeywords.push_back({.keyword_name = "}", .keyword_kind = ParserKit::eKeywordKindBodyEnd}); - kKeywords.push_back({.keyword_name = "auto", .keyword_kind = ParserKit::eKeywordKindVariable}); - kKeywords.push_back({.keyword_name = "int", .keyword_kind = ParserKit::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "bool", .keyword_kind = ParserKit::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "unsigned", .keyword_kind = ParserKit::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "short", .keyword_kind = ParserKit::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "char", .keyword_kind = ParserKit::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "long", .keyword_kind = ParserKit::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "float", .keyword_kind = ParserKit::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "double", .keyword_kind = ParserKit::eKeywordKindType}); - kKeywords.push_back({.keyword_name = "void", .keyword_kind = ParserKit::eKeywordKindType}); - - kKeywords.push_back({.keyword_name = "auto*", .keyword_kind = ParserKit::eKeywordKindVariablePtr}); - kKeywords.push_back({.keyword_name = "int*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "bool*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "unsigned*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "short*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "char*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "long*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "float*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "double*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); - kKeywords.push_back({.keyword_name = "void*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); - - kKeywords.push_back({.keyword_name = "(", .keyword_kind = ParserKit::eKeywordKindFunctionStart}); - kKeywords.push_back({.keyword_name = ")", .keyword_kind = ParserKit::eKeywordKindFunctionEnd}); - kKeywords.push_back({.keyword_name = "=", .keyword_kind = ParserKit::eKeywordKindVariableAssign}); - kKeywords.push_back({.keyword_name = "+=", .keyword_kind = ParserKit::eKeywordKindVariableInc}); - kKeywords.push_back({.keyword_name = "-=", .keyword_kind = ParserKit::eKeywordKindVariableDec}); - kKeywords.push_back({.keyword_name = "const", .keyword_kind = ParserKit::eKeywordKindConstant}); - kKeywords.push_back({.keyword_name = "*", .keyword_kind = ParserKit::eKeywordKindPtr}); - kKeywords.push_back({.keyword_name = "->", .keyword_kind = ParserKit::eKeywordKindPtrAccess}); - kKeywords.push_back({.keyword_name = ".", .keyword_kind = ParserKit::eKeywordKindAccess}); - kKeywords.push_back({.keyword_name = ",", .keyword_kind = ParserKit::eKeywordKindArgSeparator}); - kKeywords.push_back({.keyword_name = ";", .keyword_kind = ParserKit::eKeywordKindEndInstr}); - kKeywords.push_back({.keyword_name = ":", .keyword_kind = ParserKit::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "public:", .keyword_kind = ParserKit::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "private:", .keyword_kind = ParserKit::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "protected:", .keyword_kind = ParserKit::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "final", .keyword_kind = ParserKit::eKeywordKindSpecifier}); - kKeywords.push_back({.keyword_name = "return", .keyword_kind = ParserKit::eKeywordKindReturn}); - kKeywords.push_back({.keyword_name = "/*", .keyword_kind = ParserKit::eKeywordKindCommentMultiLineStart}); - kKeywords.push_back({.keyword_name = "*/", .keyword_kind = ParserKit::eKeywordKindCommentMultiLineStart}); - kKeywords.push_back({.keyword_name = "//", .keyword_kind = ParserKit::eKeywordKindCommentInline}); - kKeywords.push_back({.keyword_name = "==", .keyword_kind = ParserKit::eKeywordKindEq}); - kKeywords.push_back({.keyword_name = "!=", .keyword_kind = ParserKit::eKeywordKindNotEq}); - kKeywords.push_back({.keyword_name = ">=", .keyword_kind = ParserKit::eKeywordKindGreaterEq}); - kKeywords.push_back({.keyword_name = "<=", .keyword_kind = ParserKit::eKeywordKindLessEq}); - - kFactory.Mount(new AssemblyCPlusPlusInterface()); - kCompilerBackend = new CompilerBackendCPlusPlus(); - - for (auto index = 1UL; index < argc; ++index) - { - if (argv[index][0] == '/') - { - if (skip) - { - skip = false; - continue; - } - - if (strcmp(argv[index], "/v") == 0 || - strcmp(argv[index], "/version") == 0) - { - kSplashCxx(); - return kOk; - } - - if (strcmp(argv[index], "/verbose") == 0) - { - kState.fVerbose = true; - - continue; - } - - if (strcmp(argv[index], "/h") == 0 || strcmp(argv[index], "/help") == 0) - { - cxx_print_help(); - - return kOk; - } - - if (strcmp(argv[index], "/dialect") == 0) - { - if (kCompilerBackend) - std::cout << kCompilerBackend->Language() << "\n"; - - return kOk; - } - - if (strcmp(argv[index], "/max-errors") == 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]; - - detail::print_error(err, "cplusplus"); - - 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) - { - detail::print_error(argv_i + " is not a valid C++ source.\n", "cplusplus"); - } - - return 1; - } - - if (kFactory.Compile(argv_i, kMachine) != kOk) - return -1; - } - - return kOk; -} - -// Last rev 8-1-24 diff --git a/Sources/cplusplus.cxx b/Sources/cplusplus.cxx new file mode 100644 index 0000000..3eb6e9b --- /dev/null +++ b/Sources/cplusplus.cxx @@ -0,0 +1,905 @@ +/* + * ======================================================== + * + * cplusplus + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +/// bugs: 0 + +#define __PK_USE_STRUCT_INSTEAD__ 1 + +#define kPrintF printf + +#define kSplashCxx() \ +kPrintF(kWhite "%s\n", "Zeta C++ Compiler Driver, (c) 2024 Zeta Electronics, all rights reserved.") + +// import, @MLAutoRelease { ... }, fn foo() -> auto { ... } + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define kOk 0 + +/* ZKA Technologies C++ driver */ +/* This is part of ZECC C++ compiler. */ +/* (c) ZKA Technologies */ + +/// @author Amlal El Mahrouss (amlel) +/// @file cc.cxx +/// @brief Optimized C++ Compiler. +/// @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 detail +{ + 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> fOffsets; + }; + + struct CompilerState final + { + std::vector fSyntaxTreeList; + std::vector kStackFrame; + std::vector kStructMap; + ParserKit::SyntaxLeafList* fSyntaxTree{nullptr}; + std::unique_ptr fOutputAssembly; + std::string fLastFile; + std::string fLastError; + bool fVerbose; + }; +} // namespace detail + +static detail::CompilerState kState; +static SizeType kErrorLimit = 100; + +static Int32 kAcceptableErrors = 0; + +namespace detail +{ + /// @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 + { + if (reason[0] == '\n') + reason.erase(0, 1); + + if (file.find(".pp") != std::string::npos) + { + file.erase(file.find(".pp"), 3); + } + + if (kState.fLastFile != file) + { + std::cout << kRed << "[ cplusplus ] " << kWhite + << ((file == "cplusplus") ? "internal compiler error " + : ("in file, " + file)) + << kBlank << std::endl; + std::cout << kRed << "[ cplusplus ] " << kWhite << reason << kBlank + << std::endl; + + kState.fLastFile = file; + } + else + { + std::cout << kRed << "[ cplusplus ] [ " << kState.fLastFile << " ] " << kWhite + << reason << kBlank << std::endl; + } + + if (kAcceptableErrors > kErrorLimit) + std::exit(3); + + ++kAcceptableErrors; + } + + struct CompilerType + { + std::string fName; + std::string fValue; + }; +} // namespace detail + +///////////////////////////////////////////////////////////////////////////////////////// + +// Target architecture. +static int kMachine = CompilerKit::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 kKeywords; + +///////////////////////////////////////// + +// COMPILER PARSING UTILITIES/STATES. + +///////////////////////////////////////// + +static std::vector kFileList; +static CompilerKit::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 Zeta C++ driver */ +class CompilerBackendCPlusPlus final : public ParserKit::CompilerBackend +{ +public: + explicit CompilerBackendCPlusPlus() = default; + ~CompilerBackendCPlusPlus() override = default; + + MPCC_COPY_DEFAULT(CompilerBackendCPlusPlus); + + bool Compile(const std::string& text, const char* file) override; + + const char* Language() override; +}; + +/// @internal compiler variables + +static CompilerBackendCPlusPlus* kCompilerBackend = nullptr; +static std::vector kCompilerVariables; +static std::vector kCompilerFunctions; + +static std::vector kRegisterMap; + +static std::vector cRegisters = { + "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 cRegistersCall = { + "rcx", + "rdx", + "r8", + "r9", + "xmm8", + "xmm9", + "xmm10", + "xmm11", +}; + +static size_t kLevelFunction = 0UL; + +/// detail namespaces + +const char* CompilerBackendCPlusPlus::Language() +{ + return "Zeta C++"; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @name Compile +/// @brief Generate MASM assembly from a C++ source. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerBackendCPlusPlus::Compile(const std::string& text, + const char* file) +{ + if (text.empty()) + return false; + + // if (expr) + // int name = expr; + // expr; + + std::size_t index = 0UL; + std::vector> 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 ParserKit::eKeywordKindCommentMultiLineStart: { + commentBlock = true; + return true; + } + case ParserKit::eKeywordKindCommentMultiLineEnd: { + commentBlock = false; + break; + } + case ParserKit::eKeywordKindCommentInline: { + break; + } + default: + break; + } + + if (text[text.find(keyword.keyword_name) - 1] == '+' && + keyword.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableAssign) + continue; + + if (text[text.find(keyword.keyword_name) - 1] == '-' && + keyword.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableAssign) + continue; + + if (text[text.find(keyword.keyword_name) + 1] == '=' && + keyword.keyword_kind == ParserKit::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])) + { + detail::print_error("syntax error: " + text, file); + return false; + } + } + } + + for (auto& keyword : keywords_list) + { + auto syntax_tree = ParserKit::SyntaxLeafList::SyntaxLeaf(); + + switch (keyword.first.keyword_kind) + { + case ParserKit::KeywordKind::eKeywordKindFunctionStart: { + std::string fnName = text; + fnName.erase(fnName.find(keyword.first.keyword_name)); + + for (auto& ch : fnName) + { + if (ch == ' ') + ch = '_'; + } + + syntax_tree.fUserValue = "export .code64 __MPCC_" + fnName + "\n"; + + ++kLevelFunction; + break; + } + case ParserKit::KeywordKind::eKeywordKindFunctionEnd: { + --kLevelFunction; + + if (kRegisterMap.size() > cRegisters.size()) + { + --kLevelFunction; + } + + if (kLevelFunction < 1) + kRegisterMap.clear(); + break; + } + case ParserKit::KeywordKind::eKeywordKindEndInstr: + case ParserKit::KeywordKind::eKeywordKindVariableInc: + case ParserKit::KeywordKind::eKeywordKindVariableDec: + case ParserKit::KeywordKind::eKeywordKindVariableAssign: { + std::string valueOfVar = ""; + + if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableInc) + { + valueOfVar = text.substr(text.find("+=") + 2); + } + else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableDec) + { + valueOfVar = text.substr(text.find("-=") + 2); + } + else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableAssign) + { + valueOfVar = text.substr(text.find("=") + 1); + } + else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindEndInstr) + { + valueOfVar = "0\n"; + } + + while (valueOfVar.find(";") != std::string::npos && + keyword.first.keyword_kind != ParserKit::KeywordKind::eKeywordKindEndInstr) + { + valueOfVar.erase(valueOfVar.find(";")); + } + + std::string varName = text; + + if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableInc) + { + varName.erase(varName.find("+=")); + } + else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableDec) + { + varName.erase(varName.find("-=")); + } + else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableAssign) + { + varName.erase(varName.find("=")); + } + else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindEndInstr) + { + varName.erase(varName.find(";")); + } + + bool typeFound = false; + + for (auto& keyword : kKeywords) + { + if (keyword.keyword_kind == ParserKit::eKeywordKindType) + { + if (varName.find(keyword.keyword_name) != std::string::npos) + { + typeFound = true; + varName.erase(varName.find(keyword.keyword_name), keyword.keyword_name.size()); + } + + /// in case we goot boolX or intX + if (text.find(keyword.keyword_name) != std::string::npos) + { + if (varName[text.find(keyword.keyword_name)] == ' ') + continue; + + typeFound = false; + } + } + } + + std::string instr = "mov "; + + if (typeFound) + { + if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableInc) + { + detail::print_error("Can't increment variable when it's being created.", file); + } + else if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableDec) + { + detail::print_error("Can't decrement variable when it's being created.", file); + } + + if (kRegisterMap.size() > cRegisters.size()) + { + ++kLevelFunction; + } + + 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) + { + syntax_tree.fUserValue = instr + cRegisters[kRegisterMap.size()] + ", " + valueOfVar + "\n"; + continue; + } + + syntax_tree.fUserValue = instr + cRegisters[kRegisterMap.size()] + ", " + cRegisters[indexRight - 1] + "\n"; + break; + } + + if (((int)indexRight - 1) < 0) + { + syntax_tree.fUserValue = instr + cRegisters[kRegisterMap.size()] + ", " + valueOfVar + "\n"; + } + + kRegisterMap.push_back(varName); + } + else + { + if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindEndInstr) + { + syntax_tree.fUserValue = "\n"; + continue; + } + + if (keyword.first.keyword_kind == ParserKit::KeywordKind::eKeywordKindVariableInc) + { + instr = "add "; + } + else if (keyword.first.keyword_kind == ParserKit::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); + } + + 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 != valueOfVar) + { + syntax_tree.fUserValue = instr + cRegisters[kRegisterMap.size()] + ", " + valueOfVar + "\n"; + continue; + } + + syntax_tree.fUserValue = instr + cRegisters[indxReg - 1] + ", " + cRegisters[indexRight - 1] + "\n"; + break; + } + + break; + } + + if (syntax_tree.fUserValue.empty()) + { + detail::print_error("Variable not declared: " + varErrCpy, file); + } + } + + break; + } + case ParserKit::KeywordKind::eKeywordKindReturn: { + 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, " + cRegisters[indxReg - 1] + "\r\nret\n"; + break; + } + + if (syntax_tree.fUserValue.empty()) + { + detail::print_error("Variable not declared: " + subText, file); + } + } + else + { + syntax_tree.fUserValue = "mov rax, " + subText + "\r\nret\n"; + } + } + else + { + syntax_tree.fUserValue = "mov rcx, " + subText + "\n"; + syntax_tree.fUserValue = "mov rax, rcx\r\nret\n"; + } + + break; + } + default: + break; + } + + syntax_tree.fUserData = keyword.first; + kState.fSyntaxTree->fLeafList.emplace_back(syntax_tree); + } + +_MpccOkay: + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/** + * @brief C To Assembly mount-point. + */ + +///////////////////////////////////////////////////////////////////////////////////////// + +class AssemblyCPlusPlusInterface final : public CompilerKit::AssemblyInterface +{ +public: + explicit AssemblyCPlusPlusInterface() = default; + ~AssemblyCPlusPlusInterface() override = default; + + MPCC_COPY_DEFAULT(AssemblyCPlusPlusInterface); + + [[maybe_unused]] + static Int32 Arch() noexcept + { + return CompilerKit::AssemblyFactory::kArchAMD64; + } + + Int32 CompileToFormat(std::string& src, Int32 arch) override + { + if (arch != AssemblyCPlusPlusInterface::Arch()) + return -1; + + if (kCompilerBackend == nullptr) + return -1; + + /* @brief copy contents wihtout extension */ + std::string src_file = src.data(); + std::ifstream src_fp = std::ifstream(src_file, std::ios::in); + std::string dest; + + for (auto& ch : src_file) + { + if (ch == '.') + { + break; + } + + dest += ch; + } + + if (dest.empty()) + { + dest = "CXX-MPCC-"; + + std::random_device rd; + auto seed_data = std::array {}; + + 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); + } + + std::vector exts = kAsmFileExts; + + dest += exts[3]; + + std::cout << dest; + + kState.fOutputAssembly = std::make_unique(dest); + + auto fmt = CompilerKit::current_date(); + + (*kState.fOutputAssembly) << "; Path: " << src_file << "\n"; + (*kState.fOutputAssembly) + << "; Language: AMD64 assembly. (Generated from C++)\n"; + (*kState.fOutputAssembly) << "; Date: " << fmt << "\n"; + (*kState.fOutputAssembly) << "#bits 64\n#org 0x1000000" + << "\n"; + + ParserKit::SyntaxLeafList syntax; + + kState.fSyntaxTreeList.emplace_back(syntax); + kState.fSyntaxTree = + &kState.fSyntaxTreeList[kState.fSyntaxTreeList.size() - 1]; + + std::string source; + + while (std::getline(src_fp, source)) + { + // Compile into an object file. + kCompilerBackend->Compile(source.c_str(), src.c_str()); + } + + for (auto& ast : kState.fSyntaxTree->fLeafList) + { + (*kState.fOutputAssembly) << ast.fUserValue; + } + + if (kAcceptableErrors > 0) + return -1; + + return kOk; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +static void cxx_print_help() +{ + kSplashCxx(); + kPrintF("%s", "No help available, see:\n"); + kPrintF("%s", "www.zeta.com/developer/cplusplus\n"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define kExtListCxx \ + { \ + ".cpp", ".cxx", ".cc", ".c++", ".cp" \ + } + +NDK_MODULE(CompilerCPlusPlus) +{ + bool skip = false; + + kKeywords.push_back({.keyword_name = "class", .keyword_kind = ParserKit::eKeywordKindClass}); + kKeywords.push_back({.keyword_name = "struct", .keyword_kind = ParserKit::eKeywordKindClass}); + kKeywords.push_back({.keyword_name = "namespace", .keyword_kind = ParserKit::eKeywordKindNamespace}); + kKeywords.push_back({.keyword_name = "typedef", .keyword_kind = ParserKit::eKeywordKindTypedef}); + kKeywords.push_back({.keyword_name = "using", .keyword_kind = ParserKit::eKeywordKindTypedef}); + kKeywords.push_back({.keyword_name = "{", .keyword_kind = ParserKit::eKeywordKindBodyStart}); + kKeywords.push_back({.keyword_name = "}", .keyword_kind = ParserKit::eKeywordKindBodyEnd}); + kKeywords.push_back({.keyword_name = "auto", .keyword_kind = ParserKit::eKeywordKindVariable}); + kKeywords.push_back({.keyword_name = "int", .keyword_kind = ParserKit::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "bool", .keyword_kind = ParserKit::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "unsigned", .keyword_kind = ParserKit::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "short", .keyword_kind = ParserKit::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "char", .keyword_kind = ParserKit::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "long", .keyword_kind = ParserKit::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "float", .keyword_kind = ParserKit::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "double", .keyword_kind = ParserKit::eKeywordKindType}); + kKeywords.push_back({.keyword_name = "void", .keyword_kind = ParserKit::eKeywordKindType}); + + kKeywords.push_back({.keyword_name = "auto*", .keyword_kind = ParserKit::eKeywordKindVariablePtr}); + kKeywords.push_back({.keyword_name = "int*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "bool*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "unsigned*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "short*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "char*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "long*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "float*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "double*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); + kKeywords.push_back({.keyword_name = "void*", .keyword_kind = ParserKit::eKeywordKindTypePtr}); + + kKeywords.push_back({.keyword_name = "(", .keyword_kind = ParserKit::eKeywordKindFunctionStart}); + kKeywords.push_back({.keyword_name = ")", .keyword_kind = ParserKit::eKeywordKindFunctionEnd}); + kKeywords.push_back({.keyword_name = "=", .keyword_kind = ParserKit::eKeywordKindVariableAssign}); + kKeywords.push_back({.keyword_name = "+=", .keyword_kind = ParserKit::eKeywordKindVariableInc}); + kKeywords.push_back({.keyword_name = "-=", .keyword_kind = ParserKit::eKeywordKindVariableDec}); + kKeywords.push_back({.keyword_name = "const", .keyword_kind = ParserKit::eKeywordKindConstant}); + kKeywords.push_back({.keyword_name = "*", .keyword_kind = ParserKit::eKeywordKindPtr}); + kKeywords.push_back({.keyword_name = "->", .keyword_kind = ParserKit::eKeywordKindPtrAccess}); + kKeywords.push_back({.keyword_name = ".", .keyword_kind = ParserKit::eKeywordKindAccess}); + kKeywords.push_back({.keyword_name = ",", .keyword_kind = ParserKit::eKeywordKindArgSeparator}); + kKeywords.push_back({.keyword_name = ";", .keyword_kind = ParserKit::eKeywordKindEndInstr}); + kKeywords.push_back({.keyword_name = ":", .keyword_kind = ParserKit::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "public:", .keyword_kind = ParserKit::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "private:", .keyword_kind = ParserKit::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "protected:", .keyword_kind = ParserKit::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "final", .keyword_kind = ParserKit::eKeywordKindSpecifier}); + kKeywords.push_back({.keyword_name = "return", .keyword_kind = ParserKit::eKeywordKindReturn}); + kKeywords.push_back({.keyword_name = "/*", .keyword_kind = ParserKit::eKeywordKindCommentMultiLineStart}); + kKeywords.push_back({.keyword_name = "*/", .keyword_kind = ParserKit::eKeywordKindCommentMultiLineStart}); + kKeywords.push_back({.keyword_name = "//", .keyword_kind = ParserKit::eKeywordKindCommentInline}); + kKeywords.push_back({.keyword_name = "==", .keyword_kind = ParserKit::eKeywordKindEq}); + kKeywords.push_back({.keyword_name = "!=", .keyword_kind = ParserKit::eKeywordKindNotEq}); + kKeywords.push_back({.keyword_name = ">=", .keyword_kind = ParserKit::eKeywordKindGreaterEq}); + kKeywords.push_back({.keyword_name = "<=", .keyword_kind = ParserKit::eKeywordKindLessEq}); + + kFactory.Mount(new AssemblyCPlusPlusInterface()); + kCompilerBackend = new CompilerBackendCPlusPlus(); + + for (auto index = 1UL; index < argc; ++index) + { + if (argv[index][0] == '/') + { + if (skip) + { + skip = false; + continue; + } + + if (strcmp(argv[index], "/v") == 0 || + strcmp(argv[index], "/version") == 0) + { + kSplashCxx(); + return kOk; + } + + if (strcmp(argv[index], "/verbose") == 0) + { + kState.fVerbose = true; + + continue; + } + + if (strcmp(argv[index], "/h") == 0 || strcmp(argv[index], "/help") == 0) + { + cxx_print_help(); + + return kOk; + } + + if (strcmp(argv[index], "/dialect") == 0) + { + if (kCompilerBackend) + std::cout << kCompilerBackend->Language() << "\n"; + + return kOk; + } + + if (strcmp(argv[index], "/max-errors") == 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]; + + detail::print_error(err, "cplusplus"); + + 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) + { + detail::print_error(argv_i + " is not a valid C++ source.\n", "cplusplus"); + } + + return 1; + } + + if (kFactory.Compile(argv_i, kMachine) != kOk) + return -1; + } + + return kOk; +} + +// Last rev 8-1-24 diff --git a/Sources/elf2ae.cc b/Sources/elf2ae.cc deleted file mode 100644 index 70ab104..0000000 --- a/Sources/elf2ae.cc +++ /dev/null @@ -1,22 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -#include -#include -#include -#include -#include -#include -#include -#include - -///////////////////////////////////////////////////////////////////////////////////////// - -/// @brief COFF 2 AE entrypoint, the program/module starts here. - -///////////////////////////////////////////////////////////////////////////////////////// - -NDK_MODULE(NewOSELFToAE) { return 0; } diff --git a/Sources/elf2ae.cxx b/Sources/elf2ae.cxx new file mode 100644 index 0000000..3409f6e --- /dev/null +++ b/Sources/elf2ae.cxx @@ -0,0 +1,22 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @brief COFF 2 AE entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +NDK_MODULE(NewOSELFToAE) { return 0; } diff --git a/Sources/i64asm.cc b/Sources/i64asm.cc deleted file mode 100644 index c0dd351..0000000 --- a/Sources/i64asm.cc +++ /dev/null @@ -1,1484 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -///////////////////////////////////////////////////////////////////////////////////////// - -/// @file i64asm.cxx -/// @author Amlal EL Mahrouss -/// @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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -///////////////////// - -// 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 = CompilerKit::kPefArchAMD64; -static Boolean kOutputAsBinary = false; - -static UInt32 kErrorLimit = 10; -static UInt32 kAcceptableErrors = 0; - -constexpr auto cAMD64IPAlignment = 0x4U; - -static std::size_t kCounter = 1UL; - -static std::uintptr_t kOrigin = kPefBaseOrigin; -static std::vector> kOriginLabel; - -/// @brief keep it simple by default. -static std::int32_t kRegisterBitWidth = 16U; - -static bool kVerbose = false; - -static std::vector kAppBytes; - -static CompilerKit::AERecordHeader kCurrentRecord{ - .fName = "", .fKind = CompilerKit::kPefCode, .fSize = 0, .fOffset = 0}; - -static std::vector kRecords; -static std::vector kDefinedSymbols; -static std::vector kUndefinedSymbols; - -static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; -static const std::string kRelocSymbol = ":RuntimeSymbol:"; - -// \brief forward decl. -static bool asm_read_attributes(std::string& line); - -namespace detail -{ - void print_error(std::string reason, const std::string& file) noexcept - { - if (reason[0] == '\n') - reason.erase(0, 1); - - kStdErr << kRed << "[ i64asm ] " << kWhite - << ((file == "i64asm") ? "internal assembler error " - : ("in file, " + file)) - << kBlank << std::endl; - kStdErr << kRed << "[ i64asm ] " << kWhite << reason << kBlank << std::endl; - - if (kAcceptableErrors > kErrorLimit) - std::exit(3); - - ++kAcceptableErrors; - } - - void print_warning(std::string reason, const std::string& file) noexcept - { - if (reason[0] == '\n') - reason.erase(0, 1); - - if (!file.empty()) - { - kStdOut << kYellow << "[ file ] " << kWhite << file << kBlank << std::endl; - } - - kStdOut << kYellow << "[ i64asm ] " << kWhite << reason << kBlank - << std::endl; - } -} // namespace detail - -#include - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief AMD64 assembler entrypoint, the program/module starts here. - -///////////////////////////////////////////////////////////////////////////////////////// - -NDK_MODULE(NewOSAssemblerAMD64) -{ - //////////////// 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(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], "/version") == 0 || strcmp(argv[i], "/v") == 0) - { - kStdOut << "i64asm: AMD64 Assembler Driver.\ni64asm: v1.10\ni64asm: Copyright " - "(c) ZKA Technologies.\n"; - return 0; - } - else if (strcmp(argv[i], "/h") == 0) - { - kStdOut << "i64asm: AMD64 Assembler Driver.\ni64asm: Copyright (c) 2024 " - "ZKA Technologies.\n"; - kStdOut << "/version: Print program version.\n"; - kStdOut << "/verbose: Print verbose output.\n"; - kStdOut << "/binary: Output as flat binary.\n"; - - return 0; - } - else if (strcmp(argv[i], "/binary") == 0) - { - kOutputAsBinary = true; - continue; - } - else if (strcmp(argv[i], "/verbose") == 0) - { - kVerbose = true; - continue; - } - - kStdOut << "i64asm: ignore " << argv[i] << "\n"; - continue; - } - - if (!std::filesystem::exists(argv[i])) - { - kStdOut << "i64asm: 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 << "i64asm: error: " << strerror(errno) << "\n"; - } - } - - std::string line; - - CompilerKit::AEHeader hdr{0}; - - memset(hdr.fPad, kAEInvalidOpcode, kAEPad); - - hdr.fMagic[0] = kAEMag0; - hdr.fMagic[1] = kAEMag1; - hdr.fSize = sizeof(CompilerKit::AEHeader); - hdr.fArch = kOutputArch; - - ///////////////////////////////////////////////////////////////////////////////////////// - - // COMPILATION LOOP - - ///////////////////////////////////////////////////////////////////////////////////////// - - CompilerKit::EncoderAMD64 asm64; - - while (std::getline(file_ptr, line)) - { - if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) - { - detail::print_error(ln, argv[i]); - continue; - } - - try - { - asm_read_attributes(line); - asm64.WriteLine(line, argv[i]); - } - catch (const std::exception& e) - { - if (kVerbose) - { - std::string what = e.what(); - detail::print_warning("exit because of: " + what, "i64asm"); - } - - try - { - std::filesystem::remove(object_output); - } - catch (...) - { - } - - goto asm_fail_exit; - } - } - - if (!kOutputAsBinary) - { - if (kVerbose) - { - kStdOut << "i64asm: 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 << "i64asm: At least one record is needed to write an object " - "file.\ni64asm: Make one using `export .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 << "i64asm: Wrote record " << rec.fName << " to file...\n"; - - rec.fFlags |= CompilerKit::kKindRelocationAtRuntime; - rec.fOffset = record_count; - ++record_count; - - file_ptr_out << rec; - } - - // increment once again, so that we won't lie about the kUndefinedSymbols. - ++record_count; - - for (auto& sym : kUndefinedSymbols) - { - CompilerKit::AERecordHeader _record_hdr{0}; - - if (kVerbose) - kStdOut << "i64asm: Wrote symbol " << sym << " to file...\n"; - - _record_hdr.fKind = kAEInvalidOpcode; - _record_hdr.fSize = sym.size(); - _record_hdr.fOffset = record_count; - - ++record_count; - - memset(_record_hdr.fPad, kAEInvalidOpcode, kAEPad); - memcpy(_record_hdr.fName, sym.c_str(), sym.size()); - - file_ptr_out << _record_hdr; - - ++kCounter; - } - - auto pos_end = file_ptr_out.tellp(); - - file_ptr_out.seekp(pos); - - hdr.fStartCode = pos_end; - hdr.fCodeSize = kAppBytes.size(); - - file_ptr_out << hdr; - - file_ptr_out.seekp(pos_end); - } - else - { - if (kVerbose) - { - kStdOut << "i64asm: 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(&byte)[0]; - } - - if (kVerbose) - kStdOut << "i64asm: Wrote file with program in it.\n"; - - file_ptr_out.flush(); - file_ptr_out.close(); - - if (kVerbose) - kStdOut << "i64asm: Exit succeeded.\n"; - - return 0; - } - -asm_fail_exit: - - if (kVerbose) - kStdOut << "i64asm: Exit failed.\n"; - - return -1; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief Check for attributes -// returns true if any was found. - -///////////////////////////////////////////////////////////////////////////////////////// - -static bool asm_read_attributes(std::string& line) -{ - // import is the opposite of export, it signals to the ld - // that we need this symbol. - if (ParserKit::find_word(line, "import")) - { - if (kOutputAsBinary) - { - detail::print_error("Invalid directive in flat binary mode.", "i64asm"); - throw std::runtime_error("invalid_import_bin"); - } - - auto name = line.substr(line.find("import") + strlen("import") + 1); - - if (name.size() == 0) - { - detail::print_error("Invalid import", "ppcasm"); - throw std::runtime_error("invalid_import"); - } - - std::string result = std::to_string(name.size()); - result += kUndefinedSymbol; - - // mangle this - for (char& j : name) - { - if (j == ' ' || j == ',') - j = '$'; - } - - result += name; - - if (name.find(".code64") != std::string::npos) - { - // data is treated as code. - kCurrentRecord.fKind = CompilerKit::kPefCode; - } - else if (name.find(".data64") != std::string::npos) - { - // no code will be executed from here. - kCurrentRecord.fKind = CompilerKit::kPefData; - } - else if (name.find(".zero64") != std::string::npos) - { - // this is a bss section. - kCurrentRecord.fKind = CompilerKit::kPefZero; - } - - // this is a special case for the start stub. - // we want this so that ld can find it. - - if (name == kPefStart) - { - kCurrentRecord.fKind = CompilerKit::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, kAEInvalidOpcode, kAEPad); - - kRecords.emplace_back(kCurrentRecord); - - return true; - } - // export is a special keyword used by i64asm to tell the AE output stage to - // mark this section as a header. it currently supports .code64, .data64 and - // .zero64. - else if (ParserKit::find_word(line, "export")) - { - if (kOutputAsBinary) - { - detail::print_error("Invalid directive in flat binary mode.", "i64asm"); - throw std::runtime_error("invalid_export_bin"); - } - - auto name = line.substr(line.find("export") + strlen("export") + 1); - - std::string name_copy = name; - - for (char& j : name) - { - if (j == ' ') - j = '$'; - } - - if (std::find(kDefinedSymbols.begin(), kDefinedSymbols.end(), name) != - kDefinedSymbols.end()) - { - detail::print_error("Symbol already defined.", "i64asm"); - throw std::runtime_error("invalid_export_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 = CompilerKit::kPefCode; - } - else if (name.find(".data64") != std::string::npos) - { - // no code will be executed from here. - - name_copy.erase(name_copy.find(".data64"), strlen(".data64")); - kCurrentRecord.fKind = CompilerKit::kPefData; - } - else if (name.find(".zero64") != std::string::npos) - { - // this is a bss section. - - name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); - kCurrentRecord.fKind = CompilerKit::kPefZero; - } - - // this is a special case for the start stub. - // we want this so that ld can find it. - - if (name == kPefStart) - { - kCurrentRecord.fKind = CompilerKit::kPefCode; - } - - while (name_copy.find(" ") != std::string::npos) - name_copy.erase(name_copy.find(" "), 1); - - kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); - ++kOrigin; - - // now we can tell the code size of the previous kCurrentRecord. - - if (!kRecords.empty()) - kRecords[kRecords.size() - 1].fSize = kAppBytes.size(); - - memset(kCurrentRecord.fName, 0, kAESymbolLen); - memcpy(kCurrentRecord.fName, name.c_str(), name.size()); - - ++kCounter; - - memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); - - kRecords.emplace_back(kCurrentRecord); - - return true; - } - - return false; -} - -// \brief algorithms and helpers. - -namespace detail::algorithm -{ - // \brief authorize a brief set of characters. - static inline bool is_not_alnum_space(char c) - { - return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || - (c == ',') || (c == '(') || (c == ')') || (c == '"') || - (c == '\'') || (c == '[') || (c == ']') || (c == '+') || - (c == '_') || (c == ':') || (c == '@') || (c == '.') || (c == '#')); - } - - bool is_valid(const std::string& str) - { - return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); - } -} // namespace detail::algorithm - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief Check for line (syntax check) - -///////////////////////////////////////////////////////////////////////////////////////// - -std::string CompilerKit::EncoderAMD64::CheckLine(std::string& line, - const std::string& file) -{ - std::string err_str; - - if (line.empty() || ParserKit::find_word(line, "import") || - ParserKit::find_word(line, "export") || - ParserKit::find_word(line, kAssemblerPragmaSymStr) || - ParserKit::find_word(line, ";") || line[0] == kAssemblerPragmaSym) - { - if (line.find(';') != std::string::npos) - { - line.erase(line.find(';')); - } - else - { - // now check the line for validity - if (!detail::algorithm::is_valid(line)) - { - err_str = "Line contains non valid characters.\nhere -> "; - err_str += line; - } - } - - return err_str; - } - - if (!detail::algorithm::is_valid(line)) - { - err_str = "Line contains non alphanumeric characters.\nHere -> "; - err_str += line; - - return err_str; - } - - // check for a valid instruction format. - - if (line.find(',') != std::string::npos) - { - if (line.find(',') + 1 == line.size()) - { - err_str += "\nInstruction lacks right register, here -> "; - err_str += line.substr(line.find(',')); - - return err_str; - } - else - { - bool nothing_on_right = true; - - if (line.find(',') + 1 > line.size()) - { - err_str += "\nInstruction not complete, here -> "; - err_str += line; - - return err_str; - } - - auto substr = line.substr(line.find(',') + 1); - - for (auto& ch : substr) - { - if (ch != ' ' && ch != '\t') - { - nothing_on_right = false; - } - } - - // this means we found nothing after that ',' . - if (nothing_on_right) - { - err_str += "\nInstruction not complete, here -> "; - err_str += line; - - return err_str; - } - } - } - for (auto& opcodeAMD64 : kOpcodesAMD64) - { - if (ParserKit::find_word(line, opcodeAMD64.fName)) - { - return err_str; - } - } - - err_str += "\nUnrecognized instruction -> " + line; - - return err_str; -} - -bool CompilerKit::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) - { - detail::print_error("invalid hex number: " + jump_label, "i64asm"); - throw std::runtime_error("invalid_hex"); - } - } - - CompilerKit::NumberCast64 num = CompilerKit::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 << "i64asm: Found a base 16 number here: " - << jump_label.substr(pos) << "\n"; - } - - return true; - } - case 'b': { - if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); - !res) - { - if (errno != 0) - { - detail::print_error("invalid binary number: " + jump_label, "i64asm"); - throw std::runtime_error("invalid_bin"); - } - } - - CompilerKit::NumberCast64 num = CompilerKit::NumberCast64( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); - - if (kVerbose) - { - kStdOut << "i64asm: 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) - { - detail::print_error("invalid octal number: " + jump_label, "i64asm"); - throw std::runtime_error("invalid_octal"); - } - } - - CompilerKit::NumberCast64 num = CompilerKit::NumberCast64( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); - - if (kVerbose) - { - kStdOut << "i64asm: 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; - } - } - - CompilerKit::NumberCast64 num = CompilerKit::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 << "i64asm: Found a base 10 number here: " << jump_label.substr(pos) - << "\n"; - } - - return true; -} - -bool CompilerKit::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; - } - - CompilerKit::NumberCast32 num = CompilerKit::NumberCast32(res); - - for (char& i : num.number) - { - if (i == 0) - i = 0xFF; - - kAppBytes.push_back(i); - } - - if (kVerbose) - { - kStdOut << "i64asm: 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; - } - - CompilerKit::NumberCast32 num = CompilerKit::NumberCast32(res); - - if (kVerbose) - { - kStdOut << "i64asm: 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; - } - - CompilerKit::NumberCast32 num = CompilerKit::NumberCast32(res); - - if (kVerbose) - { - kStdOut << "i64asm: 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; - } - - CompilerKit::NumberCast32 num = CompilerKit::NumberCast32(res); - - for (char& i : num.number) - { - if (i == 0) - i = 0xFF; - - kAppBytes.push_back(i); - } - - if (kVerbose) - { - kStdOut << "i64asm: Found a base 10 number here: " << jump_label.substr(pos) - << "\n"; - } - - return true; -} - -bool CompilerKit::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) - { - detail::print_error("invalid hex number: " + jump_label, "i64asm"); - throw std::runtime_error("invalid_hex"); - } - } - - CompilerKit::NumberCast16 num = CompilerKit::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 << "i64asm: Found a base 16 number here: " - << jump_label.substr(pos) << "\n"; - } - - return true; - } - case 'b': { - if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); - !res) - { - if (errno != 0) - { - detail::print_error("invalid binary number: " + jump_label, "i64asm"); - throw std::runtime_error("invalid_bin"); - } - } - - CompilerKit::NumberCast16 num = CompilerKit::NumberCast16( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); - - if (kVerbose) - { - kStdOut << "i64asm: 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) - { - detail::print_error("invalid octal number: " + jump_label, "i64asm"); - throw std::runtime_error("invalid_octal"); - } - } - - CompilerKit::NumberCast16 num = CompilerKit::NumberCast16( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); - - if (kVerbose) - { - kStdOut << "i64asm: 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; - } - } - - CompilerKit::NumberCast16 num = CompilerKit::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 << "i64asm: Found a base 10 number here: " << jump_label.substr(pos) - << "\n"; - } - - return true; -} - -bool CompilerKit::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) - { - detail::print_error("invalid hex number: " + jump_label, "i64asm"); - throw std::runtime_error("invalid_hex"); - } - } - - CompilerKit::NumberCast8 num = CompilerKit::NumberCast8( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); - - kAppBytes.push_back(num.number); - - if (kVerbose) - { - kStdOut << "i64asm: Found a base 16 number here: " - << jump_label.substr(pos) << "\n"; - } - - return true; - } - case 'b': { - if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); - !res) - { - if (errno != 0) - { - detail::print_error("invalid binary number: " + jump_label, "i64asm"); - throw std::runtime_error("invalid_bin"); - } - } - - CompilerKit::NumberCast8 num = CompilerKit::NumberCast8( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); - - if (kVerbose) - { - kStdOut << "i64asm: 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) - { - detail::print_error("invalid octal number: " + jump_label, "i64asm"); - throw std::runtime_error("invalid_octal"); - } - } - - CompilerKit::NumberCast8 num = CompilerKit::NumberCast8( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); - - if (kVerbose) - { - kStdOut << "i64asm: 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; - } - } - - CompilerKit::NumberCast8 num = CompilerKit::NumberCast8( - strtol(jump_label.substr(pos).c_str(), nullptr, 10)); - - kAppBytes.push_back(num.number); - - if (kVerbose) - { - kStdOut << "i64asm: Found a base 10 number here: " << jump_label.substr(pos) - << "\n"; - } - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief Read and write an instruction to the output array. - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CompilerKit::EncoderAMD64::WriteLine(std::string& line, - const std::string& file) -{ - if (ParserKit::find_word(line, "export ")) - return true; - - struct RegMapAMD64 - { - std::string fName; - i64_byte_t fModRM; - }; - - std::vector REGISTER_LIST{ - {.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 (ParserKit::find_word(line, opcodeAMD64.fName) && - detail::algorithm::is_valid(line)) - { - foundInstruction = true; - std::string name(opcodeAMD64.fName); - - /// Move instruction handler. - if (name.find("mov") != std::string::npos) - { - std::string substr = line.substr(line.find(name) + name.size()); - - uint64_t bits = kRegisterBitWidth; - - if (substr.find(",") == std::string::npos) - { - detail::print_error("Syntax error.", "i64asm"); - throw std::runtime_error("syntax_err"); - } - - bool onlyOneReg = true; - - std::vector currentRegList; - - for (auto& reg : REGISTER_LIST) - { - std::vector 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') - { - detail::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); - } - - kAppBytes.emplace_back(0x89); - } - else if (bits == 16) - { - if (hasRBasedRegs) - { - detail::print_error( - "Invalid combination of operands and registers.", "i64asm"); - throw std::runtime_error("comb_op_reg"); - } - - kAppBytes.emplace_back(0x66); - kAppBytes.emplace_back(0x89); - } - - if (currentRegList[1].fName[0] == 'r' && - currentRegList[0].fName[0] == 'e') - { - detail::print_error("Invalid combination of operands and registers.", - "i64asm"); - throw std::runtime_error("comb_op_reg"); - } - - if (currentRegList[0].fName[0] == 'r' && - currentRegList[1].fName[0] == 'e') - { - detail::print_error("Invalid combination of operands and registers.", - "i64asm"); - throw std::runtime_error("comb_op_reg"); - } - - if (bits == 16) - { - if (currentRegList[0].fName[0] == 'r' || - currentRegList[0].fName[0] == 'e') - { - detail::print_error("Invalid combination of operands and registers.", - "i64asm"); - throw std::runtime_error("comb_op_reg"); - } - - if (currentRegList[1].fName[0] == 'r' || - currentRegList[1].fName[0] == 'e') - { - detail::print_error("Invalid combination of operands and registers.", - "i64asm"); - throw std::runtime_error("comb_op_reg"); - } - } - else - { - if (currentRegList[0].fName[0] != 'r' || - currentRegList[0].fName[0] == 'e') - { - detail::print_error("Invalid combination of operands and registers.", - "i64asm"); - throw std::runtime_error("comb_op_reg"); - } - - if (currentRegList[1].fName[0] != 'r' || - currentRegList[1].fName[0] == 'e') - { - detail::print_error("Invalid combination of operands and registers.", - "i64asm"); - 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) - { - detail::print_error("Syntax error: " + line, "i64asm"); - 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 << "i64asm: 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 += cAMD64IPAlignment; - - return true; -} - -// Last rev 13-1-24 diff --git a/Sources/i64asm.cxx b/Sources/i64asm.cxx new file mode 100644 index 0000000..a1f57b3 --- /dev/null +++ b/Sources/i64asm.cxx @@ -0,0 +1,1484 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @file i64asm.cxx +/// @author Amlal EL Mahrouss +/// @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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////// + +// 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 = CompilerKit::kPefArchAMD64; +static Boolean kOutputAsBinary = false; + +static UInt32 kErrorLimit = 10; +static UInt32 kAcceptableErrors = 0; + +constexpr auto cAMD64IPAlignment = 0x4U; + +static std::size_t kCounter = 1UL; + +static std::uintptr_t kOrigin = kPefBaseOrigin; +static std::vector> kOriginLabel; + +/// @brief keep it simple by default. +static std::int32_t kRegisterBitWidth = 16U; + +static bool kVerbose = false; + +static std::vector kAppBytes; + +static CompilerKit::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = CompilerKit::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector kRecords; +static std::vector kDefinedSymbols; +static std::vector kUndefinedSymbols; + +static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; +static const std::string kRelocSymbol = ":RuntimeSymbol:"; + +// \brief forward decl. +static bool asm_read_attributes(std::string& line); + +namespace detail +{ + void print_error(std::string reason, const std::string& file) noexcept + { + if (reason[0] == '\n') + reason.erase(0, 1); + + kStdErr << kRed << "[ i64asm ] " << kWhite + << ((file == "i64asm") ? "internal assembler error " + : ("in file, " + file)) + << kBlank << std::endl; + kStdErr << kRed << "[ i64asm ] " << kWhite << reason << kBlank << std::endl; + + if (kAcceptableErrors > kErrorLimit) + std::exit(3); + + ++kAcceptableErrors; + } + + void print_warning(std::string reason, const std::string& file) noexcept + { + if (reason[0] == '\n') + reason.erase(0, 1); + + if (!file.empty()) + { + kStdOut << kYellow << "[ file ] " << kWhite << file << kBlank << std::endl; + } + + kStdOut << kYellow << "[ i64asm ] " << kWhite << reason << kBlank + << std::endl; + } +} // namespace detail + +#include + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief AMD64 assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +NDK_MODULE(NewOSAssemblerAMD64) +{ + //////////////// 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(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], "/version") == 0 || strcmp(argv[i], "/v") == 0) + { + kStdOut << "i64asm: AMD64 Assembler Driver.\ni64asm: v1.10\ni64asm: Copyright " + "(c) ZKA Technologies.\n"; + return 0; + } + else if (strcmp(argv[i], "/h") == 0) + { + kStdOut << "i64asm: AMD64 Assembler Driver.\ni64asm: Copyright (c) 2024 " + "ZKA Technologies.\n"; + kStdOut << "/version: Print program version.\n"; + kStdOut << "/verbose: Print verbose output.\n"; + kStdOut << "/binary: Output as flat binary.\n"; + + return 0; + } + else if (strcmp(argv[i], "/binary") == 0) + { + kOutputAsBinary = true; + continue; + } + else if (strcmp(argv[i], "/verbose") == 0) + { + kVerbose = true; + continue; + } + + kStdOut << "i64asm: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) + { + kStdOut << "i64asm: 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 << "i64asm: error: " << strerror(errno) << "\n"; + } + } + + std::string line; + + CompilerKit::AEHeader hdr{0}; + + memset(hdr.fPad, kAEInvalidOpcode, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(CompilerKit::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + CompilerKit::EncoderAMD64 asm64; + + while (std::getline(file_ptr, line)) + { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) + { + detail::print_error(ln, argv[i]); + continue; + } + + try + { + asm_read_attributes(line); + asm64.WriteLine(line, argv[i]); + } + catch (const std::exception& e) + { + if (kVerbose) + { + std::string what = e.what(); + detail::print_warning("exit because of: " + what, "i64asm"); + } + + try + { + std::filesystem::remove(object_output); + } + catch (...) + { + } + + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) + { + if (kVerbose) + { + kStdOut << "i64asm: 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 << "i64asm: At least one record is needed to write an object " + "file.\ni64asm: Make one using `export .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 << "i64asm: Wrote record " << rec.fName << " to file...\n"; + + rec.fFlags |= CompilerKit::kKindRelocationAtRuntime; + rec.fOffset = record_count; + ++record_count; + + file_ptr_out << rec; + } + + // increment once again, so that we won't lie about the kUndefinedSymbols. + ++record_count; + + for (auto& sym : kUndefinedSymbols) + { + CompilerKit::AERecordHeader _record_hdr{0}; + + if (kVerbose) + kStdOut << "i64asm: Wrote symbol " << sym << " to file...\n"; + + _record_hdr.fKind = kAEInvalidOpcode; + _record_hdr.fSize = sym.size(); + _record_hdr.fOffset = record_count; + + ++record_count; + + memset(_record_hdr.fPad, kAEInvalidOpcode, kAEPad); + memcpy(_record_hdr.fName, sym.c_str(), sym.size()); + + file_ptr_out << _record_hdr; + + ++kCounter; + } + + auto pos_end = file_ptr_out.tellp(); + + file_ptr_out.seekp(pos); + + hdr.fStartCode = pos_end; + hdr.fCodeSize = kAppBytes.size(); + + file_ptr_out << hdr; + + file_ptr_out.seekp(pos_end); + } + else + { + if (kVerbose) + { + kStdOut << "i64asm: 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(&byte)[0]; + } + + if (kVerbose) + kStdOut << "i64asm: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) + kStdOut << "i64asm: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) + kStdOut << "i64asm: Exit failed.\n"; + + return -1; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string& line) +{ + // import is the opposite of export, it signals to the ld + // that we need this symbol. + if (ParserKit::find_word(line, "import")) + { + if (kOutputAsBinary) + { + detail::print_error("Invalid directive in flat binary mode.", "i64asm"); + throw std::runtime_error("invalid_import_bin"); + } + + auto name = line.substr(line.find("import") + strlen("import") + 1); + + if (name.size() == 0) + { + detail::print_error("Invalid import", "ppcasm"); + throw std::runtime_error("invalid_import"); + } + + std::string result = std::to_string(name.size()); + result += kUndefinedSymbol; + + // mangle this + for (char& j : name) + { + if (j == ' ' || j == ',') + j = '$'; + } + + result += name; + + if (name.find(".code64") != std::string::npos) + { + // data is treated as code. + kCurrentRecord.fKind = CompilerKit::kPefCode; + } + else if (name.find(".data64") != std::string::npos) + { + // no code will be executed from here. + kCurrentRecord.fKind = CompilerKit::kPefData; + } + else if (name.find(".zero64") != std::string::npos) + { + // this is a bss section. + kCurrentRecord.fKind = CompilerKit::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) + { + kCurrentRecord.fKind = CompilerKit::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, kAEInvalidOpcode, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // export is a special keyword used by i64asm to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64 and + // .zero64. + else if (ParserKit::find_word(line, "export")) + { + if (kOutputAsBinary) + { + detail::print_error("Invalid directive in flat binary mode.", "i64asm"); + throw std::runtime_error("invalid_export_bin"); + } + + auto name = line.substr(line.find("export") + strlen("export") + 1); + + std::string name_copy = name; + + for (char& j : name) + { + if (j == ' ') + j = '$'; + } + + if (std::find(kDefinedSymbols.begin(), kDefinedSymbols.end(), name) != + kDefinedSymbols.end()) + { + detail::print_error("Symbol already defined.", "i64asm"); + throw std::runtime_error("invalid_export_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 = CompilerKit::kPefCode; + } + else if (name.find(".data64") != std::string::npos) + { + // no code will be executed from here. + + name_copy.erase(name_copy.find(".data64"), strlen(".data64")); + kCurrentRecord.fKind = CompilerKit::kPefData; + } + else if (name.find(".zero64") != std::string::npos) + { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = CompilerKit::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == kPefStart) + { + kCurrentRecord.fKind = CompilerKit::kPefCode; + } + + while (name_copy.find(" ") != std::string::npos) + name_copy.erase(name_copy.find(" "), 1); + + kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); + ++kOrigin; + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) + kRecords[kRecords.size() - 1].fSize = kAppBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, name.c_str(), name.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace detail::algorithm +{ + // \brief authorize a brief set of characters. + static inline bool is_not_alnum_space(char c) + { + return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || + (c == ',') || (c == '(') || (c == ')') || (c == '"') || + (c == '\'') || (c == '[') || (c == ']') || (c == '+') || + (c == '_') || (c == ':') || (c == '@') || (c == '.') || (c == '#')); + } + + bool is_valid(const std::string& str) + { + return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); + } +} // namespace detail::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string CompilerKit::EncoderAMD64::CheckLine(std::string& line, + const std::string& file) +{ + std::string err_str; + + if (line.empty() || ParserKit::find_word(line, "import") || + ParserKit::find_word(line, "export") || + ParserKit::find_word(line, kAssemblerPragmaSymStr) || + ParserKit::find_word(line, ";") || line[0] == kAssemblerPragmaSym) + { + if (line.find(';') != std::string::npos) + { + line.erase(line.find(';')); + } + else + { + // now check the line for validity + if (!detail::algorithm::is_valid(line)) + { + err_str = "Line contains non valid characters.\nhere -> "; + err_str += line; + } + } + + return err_str; + } + + if (!detail::algorithm::is_valid(line)) + { + err_str = "Line contains non alphanumeric characters.\nHere -> "; + err_str += line; + + return err_str; + } + + // check for a valid instruction format. + + if (line.find(',') != std::string::npos) + { + if (line.find(',') + 1 == line.size()) + { + err_str += "\nInstruction lacks right register, here -> "; + err_str += line.substr(line.find(',')); + + return err_str; + } + else + { + bool nothing_on_right = true; + + if (line.find(',') + 1 > line.size()) + { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + + auto substr = line.substr(line.find(',') + 1); + + for (auto& ch : substr) + { + if (ch != ' ' && ch != '\t') + { + nothing_on_right = false; + } + } + + // this means we found nothing after that ',' . + if (nothing_on_right) + { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + } + } + for (auto& opcodeAMD64 : kOpcodesAMD64) + { + if (ParserKit::find_word(line, opcodeAMD64.fName)) + { + return err_str; + } + } + + err_str += "\nUnrecognized instruction -> " + line; + + return err_str; +} + +bool CompilerKit::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) + { + detail::print_error("invalid hex number: " + jump_label, "i64asm"); + throw std::runtime_error("invalid_hex"); + } + } + + CompilerKit::NumberCast64 num = CompilerKit::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 << "i64asm: Found a base 16 number here: " + << jump_label.substr(pos) << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); + !res) + { + if (errno != 0) + { + detail::print_error("invalid binary number: " + jump_label, "i64asm"); + throw std::runtime_error("invalid_bin"); + } + } + + CompilerKit::NumberCast64 num = CompilerKit::NumberCast64( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) + { + kStdOut << "i64asm: 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) + { + detail::print_error("invalid octal number: " + jump_label, "i64asm"); + throw std::runtime_error("invalid_octal"); + } + } + + CompilerKit::NumberCast64 num = CompilerKit::NumberCast64( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) + { + kStdOut << "i64asm: 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; + } + } + + CompilerKit::NumberCast64 num = CompilerKit::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 << "i64asm: Found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +bool CompilerKit::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; + } + + CompilerKit::NumberCast32 num = CompilerKit::NumberCast32(res); + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "i64asm: 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; + } + + CompilerKit::NumberCast32 num = CompilerKit::NumberCast32(res); + + if (kVerbose) + { + kStdOut << "i64asm: 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; + } + + CompilerKit::NumberCast32 num = CompilerKit::NumberCast32(res); + + if (kVerbose) + { + kStdOut << "i64asm: 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; + } + + CompilerKit::NumberCast32 num = CompilerKit::NumberCast32(res); + + for (char& i : num.number) + { + if (i == 0) + i = 0xFF; + + kAppBytes.push_back(i); + } + + if (kVerbose) + { + kStdOut << "i64asm: Found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +bool CompilerKit::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) + { + detail::print_error("invalid hex number: " + jump_label, "i64asm"); + throw std::runtime_error("invalid_hex"); + } + } + + CompilerKit::NumberCast16 num = CompilerKit::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 << "i64asm: Found a base 16 number here: " + << jump_label.substr(pos) << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); + !res) + { + if (errno != 0) + { + detail::print_error("invalid binary number: " + jump_label, "i64asm"); + throw std::runtime_error("invalid_bin"); + } + } + + CompilerKit::NumberCast16 num = CompilerKit::NumberCast16( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) + { + kStdOut << "i64asm: 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) + { + detail::print_error("invalid octal number: " + jump_label, "i64asm"); + throw std::runtime_error("invalid_octal"); + } + } + + CompilerKit::NumberCast16 num = CompilerKit::NumberCast16( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) + { + kStdOut << "i64asm: 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; + } + } + + CompilerKit::NumberCast16 num = CompilerKit::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 << "i64asm: Found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +bool CompilerKit::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) + { + detail::print_error("invalid hex number: " + jump_label, "i64asm"); + throw std::runtime_error("invalid_hex"); + } + } + + CompilerKit::NumberCast8 num = CompilerKit::NumberCast8( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + kAppBytes.push_back(num.number); + + if (kVerbose) + { + kStdOut << "i64asm: Found a base 16 number here: " + << jump_label.substr(pos) << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); + !res) + { + if (errno != 0) + { + detail::print_error("invalid binary number: " + jump_label, "i64asm"); + throw std::runtime_error("invalid_bin"); + } + } + + CompilerKit::NumberCast8 num = CompilerKit::NumberCast8( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) + { + kStdOut << "i64asm: 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) + { + detail::print_error("invalid octal number: " + jump_label, "i64asm"); + throw std::runtime_error("invalid_octal"); + } + } + + CompilerKit::NumberCast8 num = CompilerKit::NumberCast8( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) + { + kStdOut << "i64asm: 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; + } + } + + CompilerKit::NumberCast8 num = CompilerKit::NumberCast8( + strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + kAppBytes.push_back(num.number); + + if (kVerbose) + { + kStdOut << "i64asm: Found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerKit::EncoderAMD64::WriteLine(std::string& line, + const std::string& file) +{ + if (ParserKit::find_word(line, "export ")) + return true; + + struct RegMapAMD64 + { + std::string fName; + i64_byte_t fModRM; + }; + + std::vector REGISTER_LIST{ + {.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 (ParserKit::find_word(line, opcodeAMD64.fName) && + detail::algorithm::is_valid(line)) + { + foundInstruction = true; + std::string name(opcodeAMD64.fName); + + /// Move instruction handler. + if (name.find("mov") != std::string::npos) + { + std::string substr = line.substr(line.find(name) + name.size()); + + uint64_t bits = kRegisterBitWidth; + + if (substr.find(",") == std::string::npos) + { + detail::print_error("Syntax error.", "i64asm"); + throw std::runtime_error("syntax_err"); + } + + bool onlyOneReg = true; + + std::vector currentRegList; + + for (auto& reg : REGISTER_LIST) + { + std::vector 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') + { + detail::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); + } + + kAppBytes.emplace_back(0x89); + } + else if (bits == 16) + { + if (hasRBasedRegs) + { + detail::print_error( + "Invalid combination of operands and registers.", "i64asm"); + throw std::runtime_error("comb_op_reg"); + } + + kAppBytes.emplace_back(0x66); + kAppBytes.emplace_back(0x89); + } + + if (currentRegList[1].fName[0] == 'r' && + currentRegList[0].fName[0] == 'e') + { + detail::print_error("Invalid combination of operands and registers.", + "i64asm"); + throw std::runtime_error("comb_op_reg"); + } + + if (currentRegList[0].fName[0] == 'r' && + currentRegList[1].fName[0] == 'e') + { + detail::print_error("Invalid combination of operands and registers.", + "i64asm"); + throw std::runtime_error("comb_op_reg"); + } + + if (bits == 16) + { + if (currentRegList[0].fName[0] == 'r' || + currentRegList[0].fName[0] == 'e') + { + detail::print_error("Invalid combination of operands and registers.", + "i64asm"); + throw std::runtime_error("comb_op_reg"); + } + + if (currentRegList[1].fName[0] == 'r' || + currentRegList[1].fName[0] == 'e') + { + detail::print_error("Invalid combination of operands and registers.", + "i64asm"); + throw std::runtime_error("comb_op_reg"); + } + } + else + { + if (currentRegList[0].fName[0] != 'r' || + currentRegList[0].fName[0] == 'e') + { + detail::print_error("Invalid combination of operands and registers.", + "i64asm"); + throw std::runtime_error("comb_op_reg"); + } + + if (currentRegList[1].fName[0] != 'r' || + currentRegList[1].fName[0] == 'e') + { + detail::print_error("Invalid combination of operands and registers.", + "i64asm"); + 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) + { + detail::print_error("Syntax error: " + line, "i64asm"); + 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 << "i64asm: 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 += cAMD64IPAlignment; + + return true; +} + +// Last rev 13-1-24 diff --git a/Sources/link.cc b/Sources/link.cc deleted file mode 100644 index d94cbf1..0000000 --- a/Sources/link.cc +++ /dev/null @@ -1,738 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -/// @file link.cc -/// @author Amlal EL Mahrouss (amlel) -/// @brief ZKA Technologies Linker. - -/// Last Rev: Sat Feb 24 CET 2024 - -/// @note Do not look up for anything with .code64/.data64/.zero64! -/// It will be loaded when program will start up! - -#include - -//! Assembler Kit -#include - -//! Preferred Executable Format -#include -#include -#include -#include -#include - -//! Dist version -#include - -//! Advanced Executable Object Format -#include - -//! C++ I/O headers. -#include -#include - -#define kLinkerVersion "Zeta Linker Driver %s, (c) ZKA Technologies 2024, all rights reserved.\n" - -#define StringCompare(DST, SRC) strcmp(DST, SRC) - -#define kPefNoCpu 0U -#define kPefNoSubCpu 0U - -#define kWhite "\e[0;97m" -#define kStdOut (std::cout << kWhite) - -#define kLinkerDefaultOrigin kPefBaseOrigin -#define kLinkerId 0x5046FF -#define kLinkerAbiContainer "Container:Abi:" - -enum -{ - kStandardAbi = 0x5046 /* PF */ -}; - -static std::string kOutput; -static Int32 kAbi = kStandardAbi; -static Int32 kSubArch = kPefNoSubCpu; -static Int32 kArch = CompilerKit::kPefArch64000; -static Bool kFatBinaryEnable = false; -static Bool kStartFound = false; -static Bool kDuplicateSymbols = false; -static Bool kVerbose = false; - -/* link 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 kObjectList; -static std::vector kObjectBytes; - -#define kPrintF printf -#define kLinkerSplash() kPrintF(kWhite kLinkerVersion, kDistVersion) - -NDK_MODULE(NewOSLinker) -{ - bool is_executable = true; - - /** - * @brief parse flags and such. - * - */ - for (size_t i = 1; i < argc; ++i) - { - if (StringCompare(argv[i], "/help") == 0) - { - kLinkerSplash(); - kStdOut << "/version: Show linker version.\n"; - kStdOut << "/help: Show linker help.\n"; - kStdOut << "/verbose: Enable linker trace.\n"; - kStdOut << "/shared: Output as a shared PEF.\n"; - kStdOut << "/fat-bin: Output as a FAT PEF.\n"; - kStdOut << "/32x0: Output as a 32x0 PEF.\n"; - kStdOut << "/64x0: Output as a 64x0 PEF.\n"; - kStdOut << "/amd64: Output as a AMD64 PEF.\n"; - kStdOut << "/rv64: Output as a RISC-V PEF.\n"; - kStdOut << "/power64: Output as a POWER PEF.\n"; - kStdOut << "/arm64: Output as a ARM64 PEF.\n"; - kStdOut << "/output-file: Select the output file name.\n"; - - return 0; - } - else if (StringCompare(argv[i], "/version") == 0) - { - kLinkerSplash(); - return 0; - } - else if (StringCompare(argv[i], "/fat-bin") == 0) - { - kFatBinaryEnable = true; - - continue; - } - else if (StringCompare(argv[i], "/64x0") == 0) - { - kArch = CompilerKit::kPefArch64000; - - continue; - } - else if (StringCompare(argv[i], "/amd64") == 0) - { - kArch = CompilerKit::kPefArchAMD64; - - continue; - } - else if (StringCompare(argv[i], "/32x0") == 0) - { - kArch = CompilerKit::kPefArch32000; - - continue; - } - else if (StringCompare(argv[i], "/power64") == 0) - { - kArch = CompilerKit::kPefArchPowerPC; - - continue; - } - else if (StringCompare(argv[i], "/arm64") == 0) - { - kArch = CompilerKit::kPefArchARM64; - - continue; - } - else if (StringCompare(argv[i], "/verbose") == 0) - { - kVerbose = true; - - continue; - } - else if (StringCompare(argv[i], "/shared") == 0) - { - if (kOutput.empty()) - { - continue; - } - - if (kOutput.find(kPefExt) != std::string::npos) - kOutput.erase(kOutput.find(kPefExt), strlen(kPefExt)); - - kOutput += kPefDylibExt; - - is_executable = false; - - continue; - } - else if (StringCompare(argv[i], "/output-file") == 0) - { - kOutput = argv[i + 1]; - ++i; - - continue; - } - else - { - if (argv[i][0] == '/') - { - kStdOut << "link: unknown flag: " << argv[i] << "\n"; - return MPCC_EXEC_ERROR; - } - - kObjectList.emplace_back(argv[i]); - - continue; - } - } - - if (kOutput.empty()) - { - kStdOut << "link: no output filename set." << std::endl; - return MPCC_EXEC_ERROR; - } - - // sanity check. - if (kObjectList.empty()) - { - kStdOut << "link: no input files." << std::endl; - return MPCC_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 << "link: no such file: " << obj << std::endl; - return MPCC_EXEC_ERROR; - } - } - } - - // PEF expects a valid architecture when outputing a binary. - if (kArch == 0) - { - kStdOut << "link: no target architecture set, can't continue." << std::endl; - return MPCC_EXEC_ERROR; - } - - CompilerKit::PEFContainer pef_container{}; - - int32_t archs = kArch; - - pef_container.Count = 0UL; - pef_container.Kind = CompilerKit::kPefKindExec; - pef_container.SubCpu = kSubArch; - pef_container.Linker = kLinkerId; // ZKA Technologies 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(CompilerKit::PEFContainer); - - std::ofstream outputFc(kOutput, std::ofstream::binary); - - if (outputFc.bad()) - { - if (kVerbose) - { - kStdOut << "link: error: " << strerror(errno) << "\n"; - } - - return MPCC_FILE_NOT_FOUND; - } - - //! Read AE to convert as PEF. - - std::vector commandHdrsList; - CompilerKit::Utils::AEReadableProtocol readProto{}; - - for (const auto& i : kObjectList) - { - if (!std::filesystem::exists(i)) - continue; - - CompilerKit::AEHeader hdr{}; - - readProto.FP = std::ifstream(i, std::ifstream::binary); - readProto.FP >> hdr; - - auto ae_header = hdr; - - if (ae_header.fMagic[0] == kAEMag0 && ae_header.fMagic[1] == kAEMag1 && - ae_header.fSize == sizeof(CompilerKit::AEHeader)) - { - if (ae_header.fArch != kArch) - { - if (kVerbose) - kStdOut << "link: info: is this a FAT binary? : "; - - if (!kFatBinaryEnable) - { - if (kVerbose) - kStdOut << "No.\n"; - - kStdOut << "link: error: object " << i - << " is a different kind of architecture and output isn't " - "treated as a FAT binary." - << std::endl; - - std::remove(kOutput.c_str()); - return MPCC_FAT_ERROR; - } - else - { - if (kVerbose) - { - kStdOut << "Yes.\n"; - } - } - } - - // append arch type to archs varaible. - archs |= ae_header.fArch; - std::size_t cnt = ae_header.fCount; - - if (kVerbose) - kStdOut << "link: object header found, record count: " << cnt << "\n"; - - pef_container.Count = cnt; - - char_type* raw_ae_records = - new char_type[cnt * sizeof(CompilerKit::AERecordHeader)]; - memset(raw_ae_records, 0, cnt * sizeof(CompilerKit::AERecordHeader)); - - auto* ae_records = readProto.Read(raw_ae_records, cnt); - for (size_t ae_record_index = 0; ae_record_index < cnt; - ++ae_record_index) - { - CompilerKit::PEFCommandHeader command_header{0}; - size_t offsetOfData = ae_records[ae_record_index].fOffset + ae_header.fSize; - - memcpy(command_header.Name, ae_records[ae_record_index].fName, - kPefNameLen); - - // check this header if it's any valid. - if (std::string(command_header.Name).find(".code64") == - std::string::npos && - std::string(command_header.Name).find(".data64") == - std::string::npos && - std::string(command_header.Name).find(".zero64") == - std::string::npos) - { - if (std::string(command_header.Name).find(kPefStart) == - std::string::npos && - *command_header.Name == 0) - { - if (std::string(command_header.Name).find(kLdDefineSymbol) != - std::string::npos) - { - goto ld_mark_header; - } - else - { - continue; - } - } - } - - if (std::string(command_header.Name).find(kPefStart) != - std::string::npos && - std::string(command_header.Name).find(".code64") != - std::string::npos) - { - kStartFound = true; - } - - ld_mark_header: - command_header.Offset = offsetOfData; - 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 << "link: object record: " - << ae_records[ae_record_index].fName << " was marked.\n"; - - kStdOut << "link: object record offset: " << command_header.Offset << "\n"; - } - - commandHdrsList.emplace_back(command_header); - } - - delete[] raw_ae_records; - - std::vector bytes; - bytes.resize(ae_header.fCodeSize); - - readProto.FP.seekg(std::streamsize(ae_header.fStartCode)); - readProto.FP.read(bytes.data(), std::streamsize(ae_header.fCodeSize)); - - for (auto& byte : bytes) - { - kObjectBytes.push_back(byte); - } - - readProto.FP.close(); - - continue; - } - - kStdOut << "link: not an object: " << i << std::endl; - std::remove(kOutput.c_str()); - - // don't continue, it is a fatal error. - return MPCC_EXEC_ERROR; - } - - pef_container.Cpu = archs; - - outputFc << pef_container; - - if (kVerbose) - { - kStdOut << "link: wrote container header.\n"; - } - - outputFc.seekp(std::streamsize(pef_container.HdrSz)); - - std::vector not_found; - std::vector symbols; - - // step 2: check for errors (multiple symbols, undefined ones) - - for (auto& commandHdr : commandHdrsList) - { - // check if this symbol needs to be resolved. - if (std::string(commandHdr.Name).find(kLdDefineSymbol) != - std::string::npos && - std::string(commandHdr.Name).find(kLdDynamicSym) == std::string::npos) - { - if (kVerbose) - kStdOut << "link: found undefined symbol: " << commandHdr.Name << "\n"; - - if (auto it = std::find(not_found.begin(), not_found.end(), - std::string(commandHdr.Name)); - it == not_found.end()) - { - not_found.emplace_back(commandHdr.Name); - } - } - - symbols.emplace_back(commandHdr.Name); - } - - // Now try to solve these symbols. - - for (size_t not_found_idx = 0; not_found_idx < commandHdrsList.size(); - ++not_found_idx) - { - if (auto it = std::find(not_found.begin(), not_found.end(), - std::string(commandHdrsList[not_found_idx].Name)); - it != not_found.end()) - { - std::string symbol_imp = *it; - - if (symbol_imp.find(kLdDefineSymbol) == std::string::npos) - continue; - - // erase the lookup prefix. - symbol_imp.erase( - 0, symbol_imp.find(kLdDefineSymbol) + strlen(kLdDefineSymbol)); - - // demangle everything. - while (symbol_imp.find('$') != std::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& commandHdr : commandHdrsList) - { - if (std::string(commandHdr.Name).find(symbol_imp) != - std::string::npos && - std::string(commandHdr.Name).find(kLdDefineSymbol) == - std::string::npos) - { - std::string undefined_symbol = commandHdr.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 << "link: found symbol: " << commandHdr.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 - << "link: undefined entrypoint: " << kPefStart << ", you may have forget to link " - "against your compiler's runtime library.\n"; - - kStdOut << "link: undefined entrypoint " << kPefStart - << " for executable: " << kOutput << "\n"; - } - - // step 4: write all PEF commands. - - CompilerKit::PEFCommandHeader dateHeader{}; - - time_t timestamp = time(nullptr); - - std::string timeStampStr = "Container:BuildEpoch:"; - timeStampStr += std::to_string(timestamp); - - strcpy(dateHeader.Name, timeStampStr.c_str()); - - dateHeader.Flags = 0; - dateHeader.Kind = CompilerKit::kPefZero; - dateHeader.Offset = outputFc.tellp(); - dateHeader.Size = timeStampStr.size(); - - commandHdrsList.push_back(dateHeader); - - CompilerKit::PEFCommandHeader abiHeader{}; - - std::string abi = kLinkerAbiContainer; - - switch (kArch) - { - case CompilerKit::kPefArchAMD64: { - abi += "MSFT"; - break; - } - case CompilerKit::kPefArchPowerPC: { - abi += "SYSV"; - break; - } - case CompilerKit::kPefArch32000: - case CompilerKit::kPefArch64000: { - abi += "MHRA"; - break; - } - default: { - abi += " IDK"; - break; - } - } - - memcpy(abiHeader.Name, abi.c_str(), abi.size()); - - abiHeader.Size = abi.size(); - abiHeader.Offset = outputFc.tellp(); - abiHeader.Flags = 0; - abiHeader.Kind = CompilerKit::kPefLinkerID; - - commandHdrsList.push_back(abiHeader); - - CompilerKit::PEFCommandHeader uuidHeader{}; - - std::random_device rd; - - auto seedData = std::array{}; - 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); - - memcpy(uuidHeader.Name, "Container:GUID:4:", strlen("Container:GUID:4:")); - memcpy(uuidHeader.Name + strlen("Container:GUID:4:"), uuidStr.c_str(), - uuidStr.size()); - - uuidHeader.Size = 16; - uuidHeader.Offset = outputFc.tellp(); - uuidHeader.Flags = 0; - uuidHeader.Kind = CompilerKit::kPefZero; - - commandHdrsList.push_back(uuidHeader); - - // prepare a symbol vector. - std::vector undefSymbols; - std::vector duplSymbols; - std::vector resolveSymbols; - - constexpr Int32 cPaddingOffset = 16; - - size_t previousOffset = (commandHdrsList.size() * sizeof(CompilerKit::PEFCommandHeader)) + cPaddingOffset; - - // Finally write down the command headers. - // And check for any duplications - for (size_t commandHeaderIndex = 0UL; - commandHeaderIndex < commandHdrsList.size(); ++commandHeaderIndex) - { - if (std::string(commandHdrsList[commandHeaderIndex].Name) - .find(kLdDefineSymbol) != std::string::npos && - std::string(commandHdrsList[commandHeaderIndex].Name) - .find(kLdDynamicSym) == std::string::npos) - { - // ignore :UndefinedSymbol: headers, they do not contain code. - continue; - } - - std::string symbolName = commandHdrsList[commandHeaderIndex].Name; - - if (!symbolName.empty()) - { - undefSymbols.emplace_back(symbolName); - } - - commandHdrsList[commandHeaderIndex].Offset += previousOffset; - previousOffset += commandHdrsList[commandHeaderIndex].Size; - - std::string name = commandHdrsList[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) != std::string::npos && - name.find(".code64") != std::string::npos) - { - pef_container.Start = commandHdrsList[commandHeaderIndex].Offset; - auto tellCurPos = outputFc.tellp(); - - outputFc.seekp(0); - outputFc << pef_container; - - outputFc.seekp(tellCurPos); - } - - if (kVerbose) - { - kStdOut << "link: command header name: " << name << "\n"; - kStdOut << "link: real address of command header content: " << commandHdrsList[commandHeaderIndex].Offset << "\n"; - } - - outputFc << commandHdrsList[commandHeaderIndex]; - - for (size_t subCommandHeaderIndex = 0UL; - subCommandHeaderIndex < commandHdrsList.size(); - ++subCommandHeaderIndex) - { - if (subCommandHeaderIndex == commandHeaderIndex) - continue; - - if (std::string(commandHdrsList[subCommandHeaderIndex].Name) - .find(kLdDefineSymbol) != std::string::npos && - std::string(commandHdrsList[subCommandHeaderIndex].Name) - .find(kLdDynamicSym) == std::string::npos) - { - if (kVerbose) - { - kStdOut << "link: ignore :UndefinedSymbol: command header...\n"; - } - - // ignore :UndefinedSymbol: headers, they do not contain code. - continue; - } - - auto& commandHdr = commandHdrsList[subCommandHeaderIndex]; - - if (commandHdr.Name == - std::string(commandHdrsList[commandHeaderIndex].Name)) - { - if (std::find(duplSymbols.cbegin(), duplSymbols.cend(), - commandHdr.Name) == duplSymbols.cend()) - { - duplSymbols.emplace_back(commandHdr.Name); - } - - if (kVerbose) - kStdOut << "link: found duplicate symbol: " << commandHdr.Name - << "\n"; - - kDuplicateSymbols = true; - } - } - } - - if (!duplSymbols.empty()) - { - for (auto& symbol : duplSymbols) - { - kStdOut << "link: multiple symbols of " << symbol << ".\n"; - } - - std::remove(kOutput.c_str()); - return MPCC_EXEC_ERROR; - } - - // step 2.5: write program bytes. - - for (auto byte : kObjectBytes) - { - outputFc << byte; - } - - if (kVerbose) - kStdOut << "link: wrote contents of: " << kOutput << "\n"; - - // step 3: check if we have those symbols - - std::vector unrefSyms; - - for (auto& commandHdr : commandHdrsList) - { - if (auto it = std::find(not_found.begin(), not_found.end(), - std::string(commandHdr.Name)); - it != not_found.end()) - { - unrefSyms.emplace_back(commandHdr.Name); - } - } - - if (!unrefSyms.empty()) - { - for (auto& unreferenced_symbol : unrefSyms) - { - kStdOut << "link: undefined symbol " << unreferenced_symbol << "\n"; - } - } - - if (!kStartFound || kDuplicateSymbols && std::filesystem::exists(kOutput) || - !unrefSyms.empty()) - { - if (kVerbose) - kStdOut << "link: file: " << kOutput - << ", is corrupt, removing file...\n"; - - std::remove(kOutput.c_str()); - return MPCC_EXEC_ERROR; - } - - return 0; -} - -// Last rev 13-1-24 diff --git a/Sources/link.cxx b/Sources/link.cxx new file mode 100644 index 0000000..e671d84 --- /dev/null +++ b/Sources/link.cxx @@ -0,0 +1,741 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +/// @file link.cxx +/// @author Amlal EL Mahrouss (amlel) +/// @brief ZKA Technologies Linker. + +/// Last Rev: Sat Feb 24 CET 2024 + +/// @note Do not look up for anything with .code64/.data64/.zero64! +/// It will be loaded when program will start up! + +#include + +//! Assembler Kit +#include + +//! Preferred Executable Format +#include +#include +#include +#include +#include + +//! Dist version +#include + +//! Advanced Executable Object Format +#include + +//! C++ I/O headers. +#include +#include + +#define kLinkerVersion "Zeta Linker Driver %s, (c) ZKA Technologies 2024, all rights reserved.\n" + +#define StringCompare(DST, SRC) strcmp(DST, SRC) + +#define kPefNoCpu 0U +#define kPefNoSubCpu 0U + +#define kWhite "\e[0;97m" +#define kStdOut (std::cout << kWhite) + +#define kLinkerDefaultOrigin kPefBaseOrigin +#define kLinkerId 0x5046FF +#define kLinkerAbiContainer "Container:Abi:" + +enum +{ + eABIStart = 0x1010, /* Invalid ABI start of ABI list. */ + eABINewOSKrnl = 0x5046, /* PF (NewOSKrnl) */ + eABIMTL = 0x4650, /* FP (MTL firmware) */ + eABIInvalid = 1, +}; + +static std::string kOutput; +static Int32 kAbi = eABINewOSKrnl; +static Int32 kSubArch = kPefNoSubCpu; +static Int32 kArch = CompilerKit::kPefArchInvalid; +static Bool kFatBinaryEnable = false; +static Bool kStartFound = false; +static Bool kDuplicateSymbols = false; +static Bool kVerbose = false; + +/* link 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 kObjectList; +static std::vector kObjectBytes; + +#define kPrintF printf +#define kLinkerSplash() kPrintF(kWhite kLinkerVersion, kDistVersion) + +NDK_MODULE(NewOSLinker) +{ + bool is_executable = true; + + /** + * @brief parse flags and such. + * + */ + for (size_t i = 1; i < argc; ++i) + { + if (StringCompare(argv[i], "/help") == 0) + { + kLinkerSplash(); + kStdOut << "/version: Show linker version.\n"; + kStdOut << "/help: Show linker help.\n"; + kStdOut << "/verbose: Enable linker trace.\n"; + kStdOut << "/shared: Output as a shared PEF.\n"; + kStdOut << "/fat-bin: Output as a FAT PEF.\n"; + kStdOut << "/32x0: Output as a 32x0 PEF.\n"; + kStdOut << "/64x0: Output as a 64x0 PEF.\n"; + kStdOut << "/amd64: Output as a AMD64 PEF.\n"; + kStdOut << "/rv64: Output as a RISC-V PEF.\n"; + kStdOut << "/power64: Output as a POWER PEF.\n"; + kStdOut << "/arm64: Output as a ARM64 PEF.\n"; + kStdOut << "/output-file: Select the output file name.\n"; + + return 0; + } + else if (StringCompare(argv[i], "/version") == 0) + { + kLinkerSplash(); + return 0; + } + else if (StringCompare(argv[i], "/fat-bin") == 0) + { + kFatBinaryEnable = true; + + continue; + } + else if (StringCompare(argv[i], "/64x0") == 0) + { + kArch = CompilerKit::kPefArch64000; + + continue; + } + else if (StringCompare(argv[i], "/amd64") == 0) + { + kArch = CompilerKit::kPefArchAMD64; + + continue; + } + else if (StringCompare(argv[i], "/32x0") == 0) + { + kArch = CompilerKit::kPefArch32000; + + continue; + } + else if (StringCompare(argv[i], "/power64") == 0) + { + kArch = CompilerKit::kPefArchPowerPC; + + continue; + } + else if (StringCompare(argv[i], "/arm64") == 0) + { + kArch = CompilerKit::kPefArchARM64; + + continue; + } + else if (StringCompare(argv[i], "/verbose") == 0) + { + kVerbose = true; + + continue; + } + else if (StringCompare(argv[i], "/shared") == 0) + { + if (kOutput.empty()) + { + continue; + } + + if (kOutput.find(kPefExt) != std::string::npos) + kOutput.erase(kOutput.find(kPefExt), strlen(kPefExt)); + + kOutput += kPefDylibExt; + + is_executable = false; + + continue; + } + else if (StringCompare(argv[i], "/output-file") == 0) + { + kOutput = argv[i + 1]; + ++i; + + continue; + } + else + { + if (argv[i][0] == '/') + { + kStdOut << "link: unknown flag: " << argv[i] << "\n"; + return MPCC_EXEC_ERROR; + } + + kObjectList.emplace_back(argv[i]); + + continue; + } + } + + if (kOutput.empty()) + { + kStdOut << "link: no output filename set." << std::endl; + return MPCC_EXEC_ERROR; + } + + // sanity check. + if (kObjectList.empty()) + { + kStdOut << "link: no input files." << std::endl; + return MPCC_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 << "link: no such file: " << obj << std::endl; + return MPCC_EXEC_ERROR; + } + } + } + + // PEF expects a valid architecture when outputing a binary. + if (kArch == 0) + { + kStdOut << "link: no target architecture set, can't continue." << std::endl; + return MPCC_EXEC_ERROR; + } + + CompilerKit::PEFContainer pef_container{}; + + int32_t archs = kArch; + + pef_container.Count = 0UL; + pef_container.Kind = CompilerKit::kPefKindExec; + pef_container.SubCpu = kSubArch; + pef_container.Linker = kLinkerId; // ZKA Technologies 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(CompilerKit::PEFContainer); + + std::ofstream outputFc(kOutput, std::ofstream::binary); + + if (outputFc.bad()) + { + if (kVerbose) + { + kStdOut << "link: error: " << strerror(errno) << "\n"; + } + + return MPCC_FILE_NOT_FOUND; + } + + //! Read AE to convert as PEF. + + std::vector commandHdrsList; + CompilerKit::Utils::AEReadableProtocol readProto{}; + + for (const auto& i : kObjectList) + { + if (!std::filesystem::exists(i)) + continue; + + CompilerKit::AEHeader hdr{}; + + readProto.FP = std::ifstream(i, std::ifstream::binary); + readProto.FP >> hdr; + + auto ae_header = hdr; + + if (ae_header.fMagic[0] == kAEMag0 && ae_header.fMagic[1] == kAEMag1 && + ae_header.fSize == sizeof(CompilerKit::AEHeader)) + { + if (ae_header.fArch != kArch) + { + if (kVerbose) + kStdOut << "link: info: is this a FAT binary? : "; + + if (!kFatBinaryEnable) + { + if (kVerbose) + kStdOut << "No.\n"; + + kStdOut << "link: error: object " << i + << " is a different kind of architecture and output isn't " + "treated as a FAT binary." + << std::endl; + + std::remove(kOutput.c_str()); + return MPCC_FAT_ERROR; + } + else + { + if (kVerbose) + { + kStdOut << "Yes.\n"; + } + } + } + + // append arch type to archs varaible. + archs |= ae_header.fArch; + std::size_t cnt = ae_header.fCount; + + if (kVerbose) + kStdOut << "link: object header found, record count: " << cnt << "\n"; + + pef_container.Count = cnt; + + char_type* raw_ae_records = + new char_type[cnt * sizeof(CompilerKit::AERecordHeader)]; + memset(raw_ae_records, 0, cnt * sizeof(CompilerKit::AERecordHeader)); + + auto* ae_records = readProto.Read(raw_ae_records, cnt); + for (size_t ae_record_index = 0; ae_record_index < cnt; + ++ae_record_index) + { + CompilerKit::PEFCommandHeader command_header{0}; + size_t offsetOfData = ae_records[ae_record_index].fOffset + ae_header.fSize; + + memcpy(command_header.Name, ae_records[ae_record_index].fName, + kPefNameLen); + + // check this header if it's any valid. + if (std::string(command_header.Name).find(".code64") == + std::string::npos && + std::string(command_header.Name).find(".data64") == + std::string::npos && + std::string(command_header.Name).find(".zero64") == + std::string::npos) + { + if (std::string(command_header.Name).find(kPefStart) == + std::string::npos && + *command_header.Name == 0) + { + if (std::string(command_header.Name).find(kLdDefineSymbol) != + std::string::npos) + { + goto ld_mark_header; + } + else + { + continue; + } + } + } + + if (std::string(command_header.Name).find(kPefStart) != + std::string::npos && + std::string(command_header.Name).find(".code64") != + std::string::npos) + { + kStartFound = true; + } + + ld_mark_header: + command_header.Offset = offsetOfData; + 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 << "link: object record: " + << ae_records[ae_record_index].fName << " was marked.\n"; + + kStdOut << "link: object record offset: " << command_header.Offset << "\n"; + } + + commandHdrsList.emplace_back(command_header); + } + + delete[] raw_ae_records; + + std::vector bytes; + bytes.resize(ae_header.fCodeSize); + + readProto.FP.seekg(std::streamsize(ae_header.fStartCode)); + readProto.FP.read(bytes.data(), std::streamsize(ae_header.fCodeSize)); + + for (auto& byte : bytes) + { + kObjectBytes.push_back(byte); + } + + readProto.FP.close(); + + continue; + } + + kStdOut << "link: not an object: " << i << std::endl; + std::remove(kOutput.c_str()); + + // don't continue, it is a fatal error. + return MPCC_EXEC_ERROR; + } + + pef_container.Cpu = archs; + + outputFc << pef_container; + + if (kVerbose) + { + kStdOut << "link: wrote container header.\n"; + } + + outputFc.seekp(std::streamsize(pef_container.HdrSz)); + + std::vector not_found; + std::vector symbols; + + // step 2: check for errors (multiple symbols, undefined ones) + + for (auto& commandHdr : commandHdrsList) + { + // check if this symbol needs to be resolved. + if (std::string(commandHdr.Name).find(kLdDefineSymbol) != + std::string::npos && + std::string(commandHdr.Name).find(kLdDynamicSym) == std::string::npos) + { + if (kVerbose) + kStdOut << "link: found undefined symbol: " << commandHdr.Name << "\n"; + + if (auto it = std::find(not_found.begin(), not_found.end(), + std::string(commandHdr.Name)); + it == not_found.end()) + { + not_found.emplace_back(commandHdr.Name); + } + } + + symbols.emplace_back(commandHdr.Name); + } + + // Now try to solve these symbols. + + for (size_t not_found_idx = 0; not_found_idx < commandHdrsList.size(); + ++not_found_idx) + { + if (auto it = std::find(not_found.begin(), not_found.end(), + std::string(commandHdrsList[not_found_idx].Name)); + it != not_found.end()) + { + std::string symbol_imp = *it; + + if (symbol_imp.find(kLdDefineSymbol) == std::string::npos) + continue; + + // erase the lookup prefix. + symbol_imp.erase( + 0, symbol_imp.find(kLdDefineSymbol) + strlen(kLdDefineSymbol)); + + // demangle everything. + while (symbol_imp.find('$') != std::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& commandHdr : commandHdrsList) + { + if (std::string(commandHdr.Name).find(symbol_imp) != + std::string::npos && + std::string(commandHdr.Name).find(kLdDefineSymbol) == + std::string::npos) + { + std::string undefined_symbol = commandHdr.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 << "link: found symbol: " << commandHdr.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 + << "link: undefined entrypoint: " << kPefStart << ", you may have forget to link " + "against your compiler's runtime library.\n"; + + kStdOut << "link: undefined entrypoint " << kPefStart + << " for executable: " << kOutput << "\n"; + } + + // step 4: write all PEF commands. + + CompilerKit::PEFCommandHeader dateHeader{}; + + time_t timestamp = time(nullptr); + + std::string timeStampStr = "Container:BuildEpoch:"; + timeStampStr += std::to_string(timestamp); + + strcpy(dateHeader.Name, timeStampStr.c_str()); + + dateHeader.Flags = 0; + dateHeader.Kind = CompilerKit::kPefZero; + dateHeader.Offset = outputFc.tellp(); + dateHeader.Size = timeStampStr.size(); + + commandHdrsList.push_back(dateHeader); + + CompilerKit::PEFCommandHeader abiHeader{}; + + std::string abi = kLinkerAbiContainer; + + switch (kArch) + { + case CompilerKit::kPefArchAMD64: { + abi += "MSFT"; + break; + } + case CompilerKit::kPefArchPowerPC: { + abi += "SYSV"; + break; + } + case CompilerKit::kPefArch32000: + case CompilerKit::kPefArch64000: { + abi += "MHRA"; + break; + } + default: { + abi += " IDK"; + break; + } + } + + memcpy(abiHeader.Name, abi.c_str(), abi.size()); + + abiHeader.Size = abi.size(); + abiHeader.Offset = outputFc.tellp(); + abiHeader.Flags = 0; + abiHeader.Kind = CompilerKit::kPefLinkerID; + + commandHdrsList.push_back(abiHeader); + + CompilerKit::PEFCommandHeader uuidHeader{}; + + std::random_device rd; + + auto seedData = std::array{}; + 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); + + memcpy(uuidHeader.Name, "Container:GUID:4:", strlen("Container:GUID:4:")); + memcpy(uuidHeader.Name + strlen("Container:GUID:4:"), uuidStr.c_str(), + uuidStr.size()); + + uuidHeader.Size = 16; + uuidHeader.Offset = outputFc.tellp(); + uuidHeader.Flags = 0; + uuidHeader.Kind = CompilerKit::kPefZero; + + commandHdrsList.push_back(uuidHeader); + + // prepare a symbol vector. + std::vector undefSymbols; + std::vector duplSymbols; + std::vector resolveSymbols; + + constexpr Int32 cPaddingOffset = 16; + + size_t previousOffset = (commandHdrsList.size() * sizeof(CompilerKit::PEFCommandHeader)) + cPaddingOffset; + + // Finally write down the command headers. + // And check for any duplications + for (size_t commandHeaderIndex = 0UL; + commandHeaderIndex < commandHdrsList.size(); ++commandHeaderIndex) + { + if (std::string(commandHdrsList[commandHeaderIndex].Name) + .find(kLdDefineSymbol) != std::string::npos && + std::string(commandHdrsList[commandHeaderIndex].Name) + .find(kLdDynamicSym) == std::string::npos) + { + // ignore :UndefinedSymbol: headers, they do not contain code. + continue; + } + + std::string symbolName = commandHdrsList[commandHeaderIndex].Name; + + if (!symbolName.empty()) + { + undefSymbols.emplace_back(symbolName); + } + + commandHdrsList[commandHeaderIndex].Offset += previousOffset; + previousOffset += commandHdrsList[commandHeaderIndex].Size; + + std::string name = commandHdrsList[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) != std::string::npos && + name.find(".code64") != std::string::npos) + { + pef_container.Start = commandHdrsList[commandHeaderIndex].Offset; + auto tellCurPos = outputFc.tellp(); + + outputFc.seekp(0); + outputFc << pef_container; + + outputFc.seekp(tellCurPos); + } + + if (kVerbose) + { + kStdOut << "link: command header name: " << name << "\n"; + kStdOut << "link: real address of command header content: " << commandHdrsList[commandHeaderIndex].Offset << "\n"; + } + + outputFc << commandHdrsList[commandHeaderIndex]; + + for (size_t subCommandHeaderIndex = 0UL; + subCommandHeaderIndex < commandHdrsList.size(); + ++subCommandHeaderIndex) + { + if (subCommandHeaderIndex == commandHeaderIndex) + continue; + + if (std::string(commandHdrsList[subCommandHeaderIndex].Name) + .find(kLdDefineSymbol) != std::string::npos && + std::string(commandHdrsList[subCommandHeaderIndex].Name) + .find(kLdDynamicSym) == std::string::npos) + { + if (kVerbose) + { + kStdOut << "link: ignore :UndefinedSymbol: command header...\n"; + } + + // ignore :UndefinedSymbol: headers, they do not contain code. + continue; + } + + auto& commandHdr = commandHdrsList[subCommandHeaderIndex]; + + if (commandHdr.Name == + std::string(commandHdrsList[commandHeaderIndex].Name)) + { + if (std::find(duplSymbols.cbegin(), duplSymbols.cend(), + commandHdr.Name) == duplSymbols.cend()) + { + duplSymbols.emplace_back(commandHdr.Name); + } + + if (kVerbose) + kStdOut << "link: found duplicate symbol: " << commandHdr.Name + << "\n"; + + kDuplicateSymbols = true; + } + } + } + + if (!duplSymbols.empty()) + { + for (auto& symbol : duplSymbols) + { + kStdOut << "link: multiple symbols of " << symbol << ".\n"; + } + + std::remove(kOutput.c_str()); + return MPCC_EXEC_ERROR; + } + + // step 2.5: write program bytes. + + for (auto byte : kObjectBytes) + { + outputFc << byte; + } + + if (kVerbose) + kStdOut << "link: wrote contents of: " << kOutput << "\n"; + + // step 3: check if we have those symbols + + std::vector unrefSyms; + + for (auto& commandHdr : commandHdrsList) + { + if (auto it = std::find(not_found.begin(), not_found.end(), + std::string(commandHdr.Name)); + it != not_found.end()) + { + unrefSyms.emplace_back(commandHdr.Name); + } + } + + if (!unrefSyms.empty()) + { + for (auto& unreferenced_symbol : unrefSyms) + { + kStdOut << "link: undefined symbol " << unreferenced_symbol << "\n"; + } + } + + if (!kStartFound || kDuplicateSymbols && std::filesystem::exists(kOutput) || + !unrefSyms.empty()) + { + if (kVerbose) + kStdOut << "link: file: " << kOutput + << ", is corrupt, removing file...\n"; + + std::remove(kOutput.c_str()); + return MPCC_EXEC_ERROR; + } + + return 0; +} + +// Last rev 13-1-24 diff --git a/Sources/power-cc.cc b/Sources/power-cc.cc deleted file mode 100644 index 332fa53..0000000 --- a/Sources/power-cc.cc +++ /dev/null @@ -1,1645 +0,0 @@ -/* - * ======================================================== - * - * cc - * Copyright ZKA Technologies, all rights reserved. - * - * ======================================================== - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define kOk 0 - -/// @author Amlal El Mahrouss (amlel) -/// @file cc.cc -/// @brief POWER C Compiler. - -///////////////////// - -/// ANSI ESCAPE CODES - -///////////////////// - -#define kBlank "\e[0;30m" -#define kRed "\e[0;31m" -#define kWhite "\e[0;97m" - -///////////////////////////////////// - -/// INTERNAL STRUCT OF THE C COMPILER - -///////////////////////////////////// - -namespace detail -{ - // \brief name to register struct. - struct CompilerRegisterMap final - { - std::string fName; - std::string fReg; - }; - - // \brief Map for C structs - // \author amlel - struct CompilerStructMap final - { - /// '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> fOffsets; - }; - - struct CompilerState final - { - std::vector fSyntaxTreeList; - std::vector kStackFrame; - std::vector kStructMap; - ParserKit::SyntaxLeafList* fSyntaxTree{nullptr}; - std::unique_ptr fOutputAssembly; - std::string fLastFile; - std::string fLastError; - bool fVerbose; - }; -} // namespace detail - -static detail::CompilerState kState; -static SizeType kErrorLimit = 100; -static std::string kIfFunction = ""; -static Int32 kAcceptableErrors = 0; - -namespace detail -{ - /// @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 - { - if (reason[0] == '\n') - reason.erase(0, 1); - - if (file.find(".pp") != std::string::npos) - file.erase(file.find(".pp"), 3); - - if (kState.fLastFile != file) - { - std::cout << kRed << "[ cc ] " << kWhite - << ((file == "cc") ? "internal compiler error " - : ("in file, " + file)) - << kBlank << std::endl; - std::cout << kRed << "[ cc ] " << kWhite << reason << kBlank << std::endl; - - kState.fLastFile = file; - } - else - { - std::cout << kRed << "[ cc ] [ " << kState.fLastFile << " ] " << kWhite - << reason << kBlank << std::endl; - } - - if (kAcceptableErrors > kErrorLimit) - std::exit(3); - - ++kAcceptableErrors; - } - - struct CompilerType final - { - std::string fName; - std::string fValue; - }; -} // namespace detail - -///////////////////////////////////////////////////////////////////////////////////////// - -// Target architecture. -static int kMachine = 0; - -///////////////////////////////////////// - -// REGISTERS ACCORDING TO USED ASSEMBLER - -///////////////////////////////////////// - -static size_t kRegisterCnt = kAsmRegisterLimit; -static size_t kStartUsable = 2; -static size_t kUsableLimit = 15; -static size_t kRegisterCounter = kStartUsable; -static std::string kRegisterPrefix = kAsmRegisterPrefix; - -///////////////////////////////////////// - -// COMPILER PARSING UTILITIES/STATES. - -///////////////////////////////////////// - -static std::vector kFileList; -static CompilerKit::AssemblyFactory kFactory; -static bool kInStruct = false; -static bool kOnWhileLoop = false; -static bool kOnForLoop = false; -static bool kInBraces = false; -static bool kIfFound = false; -static size_t kBracesCount = 0UL; - -/* @brief C compiler backend for C */ -class CompilerBackendCLang final : public ParserKit::CompilerBackend -{ -public: - explicit CompilerBackendCLang() = default; - ~CompilerBackendCLang() override = default; - - MPCC_COPY_DEFAULT(CompilerBackendCLang); - - std::string Check(const char* text, const char* file); - bool Compile(const std::string& text, const char* file) override; - - const char* Language() override - { - return "POWER C"; - } -}; - -static CompilerBackendCLang* kCompilerBackend = nullptr; -static std::vector kCompilerVariables; -static std::vector kCompilerFunctions; -static std::vector kCompilerTypes; - -namespace detail -{ - union number_cast final { - public: - number_cast(UInt64 _Raw) - : _Raw(_Raw) - { - } - - public: - char _Num[8]; - UInt64 _Raw; - }; - - union double_cast final { - public: - double_cast(float _Raw) - : _Raw(_Raw) - { - } - - public: - char _Sign; - char _Lh[8]; - char _Rh[23]; - - float _Raw; - }; -} // namespace detail - -///////////////////////////////////////////////////////////////////////////////////////// - -// @name Compile -// @brief Generate MASM from a C assignement. - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CompilerBackendCLang::Compile(const std::string& text, const char* file) -{ - std::string textBuffer = text; - - bool typeFound = false; - bool fnFound = false; - - // setup generator. - std::random_device rd; - - auto seed_data = std::array{}; - std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); - std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); - std::mt19937 generator(seq); - - // start parsing - for (size_t text_index = 0; text_index < textBuffer.size(); ++text_index) - { - auto syntaxLeaf = ParserKit::SyntaxLeafList::SyntaxLeaf(); - - auto gen = uuids::uuid_random_generator{generator}; - uuids::uuid out = gen(); - - detail::number_cast time_off = (UInt64)out.as_bytes().data(); - - if (!typeFound) - { - auto substr = textBuffer.substr(text_index); - std::string match_type; - - for (size_t y = 0; y < substr.size(); ++y) - { - if (substr[y] == ' ') - { - while (match_type.find(' ') != std::string::npos) - { - match_type.erase(match_type.find(' ')); - } - - for (auto& clType : kCompilerTypes) - { - if (clType.fName == match_type) - { - match_type.clear(); - - std::string buf; - - buf += clType.fValue; - buf += ' '; - - if (substr.find('=') != std::string::npos) - { - break; - } - - if (textBuffer.find('(') != std::string::npos) - { - syntaxLeaf.fUserValue = buf; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - } - - typeFound = true; - break; - } - } - - break; - } - - match_type += substr[y]; - } - } - - if (textBuffer[text_index] == '{') - { - if (kInStruct) - { - continue; - } - - kInBraces = true; - ++kBracesCount; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - } - - // return keyword handler - if (textBuffer[text_index] == 'r') - { - std::string return_keyword; - return_keyword += "return"; - - std::size_t index = 0UL; - - std::string value; - - for (size_t return_index = text_index; return_index < textBuffer.size(); - ++return_index) - { - if (textBuffer[return_index] != return_keyword[index]) - { - for (size_t value_index = return_index; - value_index < textBuffer.size(); ++value_index) - { - if (textBuffer[value_index] == ';') - break; - - value += textBuffer[value_index]; - } - - break; - } - - ++index; - } - - if (index == return_keyword.size()) - { - if (!value.empty()) - { - if (value.find('(') != std::string::npos) - { - value.erase(value.find('(')); - } - - if (!isdigit(value[value.find('(') + 2])) - { - std::string tmp = value; - bool reg_to_reg = false; - - value.clear(); - - value += " import"; - value += tmp; - } - - syntaxLeaf.fUserValue = "\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("import") != std::string::npos) - value.erase(value.find("import"), strlen("import")); - - 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 = "__MPCC_IF_PROC_"; - kIfFunction += std::to_string(time_off._Raw); - - syntaxLeaf.fUserValue = - "\tcmpw " - "r10, r11"; - - syntaxLeaf.fUserValue += "\n\tbeq import " + kIfFunction + - " \ndword export .code64 " + kIfFunction + "\n"; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - - kIfFound = true; - } - - // Parse expressions and instructions here. - // what does this mean? - // we encounter an assignment, or we reached the end of an expression. - if (textBuffer[text_index] == '=' || textBuffer[text_index] == ';') - { - if (fnFound) - continue; - if (kIfFound) - continue; - - if (textBuffer[text_index] == ';' && kInStruct) - continue; - - if (textBuffer.find("typedef ") != std::string::npos) - continue; - - if (textBuffer[text_index] == '=' && kInStruct) - { - detail::print_error( - "assignement of value 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 export .data64 "; - } - - int first_encountered = 0; - - std::string str_name; - - for (size_t text_index_2 = 0; text_index_2 < textBuffer.size(); - ++text_index_2) - { - if (textBuffer[text_index_2] == '\"') - { - ++text_index_2; - - // want to add this, so that the parser recognizes that this is a - // string. - substr += '"'; - - for (; text_index_2 < textBuffer.size(); ++text_index_2) - { - if (textBuffer[text_index_2] == '\"') - break; - - substr += textBuffer[text_index_2]; - } - } - - if (textBuffer[text_index_2] == '{' || textBuffer[text_index_2] == '}') - continue; - - if (textBuffer[text_index_2] == ';') - { - break; - } - - if (textBuffer[text_index_2] == ' ' || - textBuffer[text_index_2] == '\t') - { - if (first_encountered != 2) - { - if (textBuffer[text_index] != '=' && - substr.find("export .data64") == std::string::npos && - !kInStruct) - substr += "export .data64 "; - } - - ++first_encountered; - - continue; - } - - if (textBuffer[text_index_2] == '=') - { - if (!kInBraces) - { - substr.replace(substr.find("export .data64"), - strlen("export .data64"), "export .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"), "import "); - - if (substr.find("export .data64") != std::string::npos) - substr.erase(substr.find("export .data64"), strlen("export .data64")); - } - - auto var_to_find = - std::find_if(kCompilerVariables.cbegin(), kCompilerVariables.cend(), - [&](detail::CompilerType type) { - return type.fName.find(substr) != std::string::npos; - }); - - 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 += "export .code64 "; - - syntaxLeaf.fUserValue += substr; - syntaxLeaf.fUserValue += "\n"; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - - fnFound = true; - } - - kCompilerFunctions.push_back(textBuffer); - } - - if (textBuffer[text_index] == '-' && textBuffer[text_index + 1] == '-') - { - textBuffer = textBuffer.replace(textBuffer.find("--"), strlen("--"), ""); - - for (int _text_i = 0; _text_i < textBuffer.size(); ++_text_i) - { - if (textBuffer[_text_i] == '\t' || textBuffer[_text_i] == ' ') - textBuffer.erase(_text_i, 1); - } - - syntaxLeaf.fUserValue += "dec "; - syntaxLeaf.fUserValue += textBuffer; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - break; - } - - if (textBuffer[text_index] == '}') - { - kRegisterCounter = kStartUsable; - - --kBracesCount; - - if (kBracesCount < 1) - { - kInBraces = false; - kBracesCount = 0; - } - - if (kIfFound) - kIfFound = false; - - if (kInStruct) - kInStruct = false; - - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - } - - syntaxLeaf.fUserValue.clear(); - } - - auto syntaxLeaf = ParserKit::SyntaxLeafList::SyntaxLeaf(); - syntaxLeaf.fUserValue = "\n"; - kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); - - return true; -} - -static bool kShouldHaveBraces = false; -static std::string kFnName; - -std::string CompilerBackendCLang::Check(const char* text, const char* file) -{ - std::string err_str; - std::string ln = text; - - if (ln.empty()) - { - return err_str; - } - - bool non_ascii_found = false; - - for (int i = 0; i < ln.size(); ++i) - { - if (isalnum(ln[i])) - { - non_ascii_found = true; - break; - } - } - - if (kShouldHaveBraces && ln.find('{') != std::string::npos) - { - kShouldHaveBraces = false; - } - - if (!non_ascii_found) - return err_str; - - size_t string_index = 1UL; - - if (ln.find('\'') != std::string::npos) - { - string_index = ln.find('\'') + 1; - - for (; string_index < ln.size(); ++string_index) - { - if (ln[string_index] == '\'') - { - if (ln[string_index + 1] != ';') - { - ln.erase(string_index, 1); - } - - return err_str; - } - } - } - else if (ln.find('"') != std::string::npos) - { - string_index = ln.find('"') + 1; - - for (; string_index < ln.size(); ++string_index) - { - if (ln[string_index] == '"') - { - if (ln[string_index + 1] != ';') - { - ln.erase(string_index, 1); - } - else - { - break; - } - } - } - } - else if (ln.find('"') == std::string::npos && - ln.find('\'') == std::string::npos) - { - std::vector 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 variables_list = { - {.fBegin = "static ", .fEnd = "="}, - {.fBegin = "=", .fEnd = ";"}, - {.fBegin = "if(", .fEnd = "="}, - {.fBegin = "if (", .fEnd = "="}, - {.fBegin = "if(", .fEnd = "<"}, - {.fBegin = "if (", .fEnd = "<"}, - {.fBegin = "if(", .fEnd = ">"}, - {.fBegin = "if (", .fEnd = ">"}, - {.fBegin = "if(", .fEnd = ")"}, - {.fBegin = "if (", .fEnd = ")"}, - - {.fBegin = "else(", .fEnd = "="}, - {.fBegin = "else (", .fEnd = "="}, - {.fBegin = "else(", .fEnd = "<"}, - {.fBegin = "else (", .fEnd = "<"}, - {.fBegin = "else(", .fEnd = ">"}, - {.fBegin = "else (", .fEnd = ">"}, - {.fBegin = "else(", .fEnd = ")"}, - {.fBegin = "else (", .fEnd = ")"}, - }; - - for (auto& variable : variables_list) - { - if (ln.find(variable.fBegin) != std::string::npos) - { - string_index = ln.find(variable.fBegin) + variable.fBegin.size(); - - while (ln[string_index] == ' ') - ++string_index; - - std::string keyword; - - for (; string_index < ln.size(); ++string_index) - { - if (ln[string_index] == variable.fEnd[0]) - { - std::string varname = ""; - - for (size_t index_keyword = ln.find(' '); - ln[index_keyword] != variable.fBegin[0]; ++index_keyword) - { - if (ln[index_keyword] == ' ') - { - continue; - } - - if (isdigit(ln[index_keyword])) - { - goto cc_next_loop; - } - - varname += ln[index_keyword]; - } - - if (varname.find(' ') != std::string::npos) - { - varname.erase(0, varname.find(' ')); - - if (variable.fBegin == "extern") - { - varname.erase(0, varname.find(' ')); - } - } - - if (kRegisterCounter == 5 || kRegisterCounter == 6) - ++kRegisterCounter; - - std::string reg = kAsmRegisterPrefix; - reg += std::to_string(kRegisterCounter); - - kCompilerVariables.push_back({.fValue = varname}); - goto cc_check_done; - } - - keyword.push_back(ln[string_index]); - } - - goto cc_next_loop; - - cc_check_done: - - // skip digit value. - if (isdigit(keyword[0]) || keyword[0] == '"') - { - goto cc_next_loop; - } - - while (keyword.find(' ') != std::string::npos) - keyword.erase(keyword.find(' '), 1); - - for (auto& var : kCompilerVariables) - { - if (var.fValue.find(keyword) != std::string::npos) - { - err_str.clear(); - goto cc_next; - } - } - - for (auto& fn : kCompilerFunctions) - { - if (fn.find(keyword[0]) != std::string::npos) - { - auto where_begin = fn.find(keyword[0]); - auto keyword_begin = 0UL; - auto failed = false; - - for (; where_begin < keyword.size(); ++where_begin) - { - if (fn[where_begin] == '(' && keyword[keyword_begin] == '(') - break; - - if (fn[where_begin] != keyword[keyword_begin]) - { - failed = true; - break; - } - - ++keyword_begin; - } - - if (!failed) - { - err_str.clear(); - goto cc_next; - } - else - { - continue; - } - } - } - - cc_error_value: - if (keyword.find("->") != std::string::npos) - return err_str; - - if (keyword.find(".") != std::string::npos) - return err_str; - - if (isalnum(keyword[0])) - err_str += "\nUndefined value: " + keyword; - - return err_str; - } - - cc_next_loop: - continue; - } - -cc_next: - - // extern does not declare anything, it imports a variable. - // so that's why it's not declare upper. - if (ParserKit::find_word(ln, "extern")) - { - auto substr = ln.substr(ln.find("extern") + strlen("extern")); - kCompilerVariables.push_back({.fValue = substr}); - } - - if (kShouldHaveBraces && ln.find('{') == std::string::npos) - { - err_str += "Missing '{' for function "; - err_str += kFnName; - err_str += "\n"; - - kShouldHaveBraces = false; - kFnName.clear(); - } - else if (kShouldHaveBraces && ln.find('{') != std::string::npos) - { - kShouldHaveBraces = false; - kFnName.clear(); - } - - bool type_not_found = true; - - if (ln.find('\'') != std::string::npos) - { - ln.replace(ln.find('\''), 3, "0"); - } - - auto first = ln.find('"'); - if (first != std::string::npos) - { - auto second = 0UL; - bool found_second_quote = false; - - for (size_t i = first + 1; i < ln.size(); ++i) - { - if (ln[i] == '\"') - { - found_second_quote = true; - second = i; - - break; - } - } - - if (!found_second_quote) - { - err_str += "Missing terminating \"."; - err_str += " here -> " + ln.substr(ln.find('"'), second); - } - } - - if (ln.find(')') != std::string::npos && ln.find(';') == std::string::npos) - { - if (ln.find('{') == std::string::npos) - { - kFnName = ln; - kShouldHaveBraces = true; - - goto skip_braces_check; - } - else if (ln.find('{') != std::string::npos) - { - kShouldHaveBraces = false; - } - } - -skip_braces_check: - - for (auto& key : kCompilerTypes) - { - if (ParserKit::find_word(ln, key.fName)) - { - if (isdigit(ln[ln.find(key.fName) + key.fName.size() + 1])) - { - err_str += "\nNumber cannot be set for "; - err_str += key.fName; - err_str += "'s name. here -> "; - err_str += ln; - } - - if (ln.find(key.fName) == 0 || ln[ln.find(key.fName) - 1] == ' ' || - ln[ln.find(key.fName) - 1] == '\t') - { - type_not_found = false; - - if (ln[ln.find(key.fName) + key.fName.size()] != ' ') - { - type_not_found = true; - - if (ln[ln.find(key.fName) + key.fName.size()] == '\t') - type_not_found = false; - - goto next; - } - else if (ln[ln.find(key.fName) + key.fName.size()] != '\t') - { - type_not_found = true; - - if (ln[ln.find(key.fName) + key.fName.size()] == ' ') - type_not_found = false; - } - } - - next: - - if (ln.find(';') == std::string::npos) - { - if (ln.find('(') != std::string::npos) - { - if (ln.find('=') == std::string::npos) - continue; - } - - err_str += "\nMissing ';', here -> "; - err_str += ln; - } - else - { - continue; - } - - if (ln.find('=') != std::string::npos) - { - if (ln.find('(') != std::string::npos) - { - if (ln.find(')') == std::string::npos) - { - err_str += "\nMissing ')', after '(' here -> "; - err_str += ln.substr(ln.find('(')); - } - } - } - } - } - - if (kInBraces && ln.find("struct") != std::string::npos && - ln.find("union") != std::string::npos && - ln.find("enum") != std::string::npos && - ln.find('=') != std::string::npos) - { - if (ln.find(';') == std::string::npos) - { - err_str += "\nMissing ';' after struct/union/enum declaration, here -> "; - err_str += ln; - } - } - - if (ln.find(';') != std::string::npos && - ln.find("for") == std::string::npos) - { - if (ln.find(';') + 1 != ln.size()) - { - for (int i = 0; i < ln.substr(ln.find(';') + 1).size(); ++i) - { - if ((ln.substr(ln.find(';') + 1)[i] != ' ') || - (ln.substr(ln.find(';') + 1)[i] != '\t')) - { - if (auto err = this->Check(ln.substr(ln.find(';') + 1).c_str(), file); - !err.empty()) - { - err_str += "\nUnexpected text after ';' -> "; - err_str += ln.substr(ln.find(';')); - err_str += err; - } - } - } - } - } - - if (ln.find('(') != std::string::npos) - { - if (ln.find(';') == std::string::npos && !ParserKit::find_word(ln, "|") && - !ParserKit::find_word(ln, "||") && !ParserKit::find_word(ln, "&") && - !ParserKit::find_word(ln, "&&") && !ParserKit::find_word(ln, "~")) - { - bool found_func = false; - size_t i = ln.find('('); - std::vector opens; - std::vector closes; - - for (; i < ln.size(); ++i) - { - if (ln[i] == ')') - { - closes.push_back(1); - } - - if (ln[i] == '(') - { - opens.push_back(1); - } - } - - if (closes.size() != opens.size()) - err_str += "Unterminated (), here -> " + ln; - - bool space_found = false; - - for (int i = 0; i < ln.size(); ++i) - { - if (ln[i] == ')' && !space_found) - { - space_found = true; - continue; - } - - if (space_found) - { - if (ln[i] == ' ' && isalnum(ln[i + 1])) - { - err_str += "\nBad function format here -> "; - err_str += ln; - } - } - } - } - - if (ln.find('(') < 1) - { - err_str += "\nMissing identifier before '(' here -> "; - err_str += ln; - } - else - { - if (type_not_found && ln.find(';') == std::string::npos && - ln.find("if") == std::string::npos && - ln.find("|") == std::string::npos && - ln.find("&") == std::string::npos && - ln.find("(") == std::string::npos && - ln.find(")") == std::string::npos) - { - err_str += "\n Missing ';' or type, here -> "; - err_str += ln; - } - } - - if (ln.find(')') == std::string::npos) - { - err_str += "\nMissing ')', after '(' here -> "; - err_str += ln.substr(ln.find('(')); - } - } - else - { - if (ln.find("for") != std::string::npos || - ln.find("while") != std::string::npos) - { - err_str += "\nMissing '(', after \"for\", here -> "; - err_str += ln; - } - } - - if (ln.find('}') != std::string::npos && !kInBraces) - { - if (!kInStruct && ln.find(';') == std::string::npos) - { - err_str += "\nMismatched '}', here -> "; - err_str += ln; - } - } - - if (!ln.empty()) - { - if (ln.find(';') == std::string::npos && - ln.find('{') == std::string::npos && - ln.find('}') == std::string::npos && - ln.find(')') == std::string::npos && - ln.find('(') == std::string::npos && - ln.find(',') == std::string::npos) - { - if (ln.size() <= 2) - return err_str; - - err_str += "\nMissing ';', here -> "; - err_str += ln; - } - } - - return err_str; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -/** - * @brief C To Assembly mount-point. - */ - -///////////////////////////////////////////////////////////////////////////////////////// - -class AssemblyMountpointCLang final : public CompilerKit::AssemblyInterface -{ -public: - explicit AssemblyMountpointCLang() = default; - ~AssemblyMountpointCLang() override = default; - - MPCC_COPY_DEFAULT(AssemblyMountpointCLang); - - [[maybe_unused]] static Int32 Arch() noexcept - { - return CompilerKit::AssemblyFactory::kArchPowerPC; - } - - Int32 CompileToFormat(std::string& src, Int32 arch) override - { - if (arch != AssemblyMountpointCLang::Arch()) - return -1; - - if (kCompilerBackend == nullptr) - return -1; - - /* @brief copy contents wihtout extension */ - std::string src_file = src.data(); - std::ifstream src_fp = std::ifstream(src_file, std::ios::in); - std::string dest; - - for (auto& ch : src_file) - { - if (ch == '.') - { - break; - } - - dest += ch; - } - - /* According to PEF ABI. */ - std::vector exts = kAsmFileExts; - dest += exts[4]; - - kState.fOutputAssembly = std::make_unique(dest); - - auto fmt = CompilerKit::current_date(); - - (*kState.fOutputAssembly) << "# Path: " << src_file << "\n"; - (*kState.fOutputAssembly) - << "# Language: POWER Assembly (Generated from C)\n"; - (*kState.fOutputAssembly) << "# Date: " << fmt << "\n\n"; - - ParserKit::SyntaxLeafList syntax; - - kState.fSyntaxTreeList.push_back(syntax); - kState.fSyntaxTree = - &kState.fSyntaxTreeList[kState.fSyntaxTreeList.size() - 1]; - - std::string line_src; - - while (std::getline(src_fp, line_src)) - { - if (auto err = kCompilerBackend->Check(line_src.c_str(), src.data()); - err.empty()) - { - kCompilerBackend->Compile(line_src, src.data()); - } - else - { - detail::print_error(err, src.data()); - } - } - - if (kAcceptableErrors > 0) - return -1; - - std::vector keywords = {"ld", "stw", "add", "sub", "or"}; - - /// - /// Replace, optimize, fix assembly output. - /// - - for (auto& leaf : kState.fSyntaxTree->fLeafList) - { - std::vector access_keywords = {"->", "."}; - - for (auto& access_ident : access_keywords) - { - if (ParserKit::find_word(leaf.fUserValue, access_ident)) - { - for (auto& struc : kState.kStructMap) - { - /// TODO: - } - } - } - - for (auto& keyword : keywords) - { - if (ParserKit::find_word(leaf.fUserValue, keyword)) - { - std::size_t cnt = 0UL; - - for (auto& reg : kState.kStackFrame) - { - std::string needle; - - for (size_t i = 0; i < reg.fName.size(); i++) - { - if (reg.fName[i] == ' ') - { - ++i; - - for (; i < reg.fName.size(); i++) - { - if (reg.fName[i] == ',') - { - break; - } - - if (reg.fName[i] == ' ') - continue; - - needle += reg.fName[i]; - } - - break; - } - } - - if (ParserKit::find_word(leaf.fUserValue, needle)) - { - if (leaf.fUserValue.find("import ") != std::string::npos) - { - std::string range = "import "; - 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 kOk; - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -#include - -#define kPrintF printf -#define kSplashCxx() \ - kPrintF(kWhite "cc, %s, (c) ZKA Technologies\n", kDistVersion) - -static void cc_print_help() -{ - kSplashCxx(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -#define kExt ".c" - -NDK_MODULE(NewOSCompilerCLangPowerPC) -{ - kCompilerTypes.push_back({.fName = "void", .fValue = "void"}); - kCompilerTypes.push_back({.fName = "char", .fValue = "byte"}); - kCompilerTypes.push_back({.fName = "short", .fValue = "hword"}); - kCompilerTypes.push_back({.fName = "int", .fValue = "dword"}); - kCompilerTypes.push_back({.fName = "long", .fValue = "qword"}); - kCompilerTypes.push_back({.fName = "*", .fValue = "offset"}); - - bool skip = false; - - kFactory.Mount(new AssemblyMountpointCLang()); - kMachine = CompilerKit::AssemblyFactory::kArchPowerPC; - kCompilerBackend = new CompilerBackendCLang(); - - for (auto index = 1UL; index < argc; ++index) - { - if (skip) - { - skip = false; - continue; - } - - if (argv[index][0] == '-') - { - if (strcmp(argv[index], "-v") == 0 || - strcmp(argv[index], "-version") == 0) - { - kSplashCxx(); - return kOk; - } - - if (strcmp(argv[index], "-verbose") == 0) - { - kState.fVerbose = true; - - continue; - } - - if (strcmp(argv[index], "-h") == 0 || strcmp(argv[index], "-help") == 0) - { - cc_print_help(); - - return kOk; - } - - if (strcmp(argv[index], "-dialect") == 0) - { - if (kCompilerBackend) - std::cout << kCompilerBackend->Language() << "\n"; - - return kOk; - } - - if (strcmp(argv[index], "-fmax-exceptions") == 0) - { - try - { - kErrorLimit = std::strtol(argv[index + 1], nullptr, 10); - } - // catch anything here - catch (...) - { - kErrorLimit = 0; - } - - skip = true; - - continue; - } - - std::string err = "Unknown command: "; - err += argv[index]; - - detail::print_error(err, "cc"); - - continue; - } - - kFileList.emplace_back(argv[index]); - - std::string srcFile = argv[index]; - - if (strstr(argv[index], kExt) == nullptr) - { - if (kState.fVerbose) - { - detail::print_error(srcFile + " is not a valid C source.\n", "cc"); - } - - return 1; - } - - if (kFactory.Compile(srcFile, kMachine) != kOk) - return -1; - } - - return kOk; -} - -// Last rev 8-1-24 diff --git a/Sources/power-cc.cxx b/Sources/power-cc.cxx new file mode 100644 index 0000000..41cb334 --- /dev/null +++ b/Sources/power-cc.cxx @@ -0,0 +1,1645 @@ +/* + * ======================================================== + * + * cc + * Copyright ZKA Technologies, all rights reserved. + * + * ======================================================== + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define kOk 0 + +/// @author Amlal El Mahrouss (amlel) +/// @file cc.cxx +/// @brief POWER C Compiler. + +///////////////////// + +/// ANSI ESCAPE CODES + +///////////////////// + +#define kBlank "\e[0;30m" +#define kRed "\e[0;31m" +#define kWhite "\e[0;97m" + +///////////////////////////////////// + +/// INTERNAL STRUCT OF THE C COMPILER + +///////////////////////////////////// + +namespace detail +{ + // \brief name to register struct. + struct CompilerRegisterMap final + { + std::string fName; + std::string fReg; + }; + + // \brief Map for C structs + // \author amlel + struct CompilerStructMap final + { + /// '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> fOffsets; + }; + + struct CompilerState final + { + std::vector fSyntaxTreeList; + std::vector kStackFrame; + std::vector kStructMap; + ParserKit::SyntaxLeafList* fSyntaxTree{nullptr}; + std::unique_ptr fOutputAssembly; + std::string fLastFile; + std::string fLastError; + bool fVerbose; + }; +} // namespace detail + +static detail::CompilerState kState; +static SizeType kErrorLimit = 100; +static std::string kIfFunction = ""; +static Int32 kAcceptableErrors = 0; + +namespace detail +{ + /// @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 + { + if (reason[0] == '\n') + reason.erase(0, 1); + + if (file.find(".pp") != std::string::npos) + file.erase(file.find(".pp"), 3); + + if (kState.fLastFile != file) + { + std::cout << kRed << "[ cc ] " << kWhite + << ((file == "cc") ? "internal compiler error " + : ("in file, " + file)) + << kBlank << std::endl; + std::cout << kRed << "[ cc ] " << kWhite << reason << kBlank << std::endl; + + kState.fLastFile = file; + } + else + { + std::cout << kRed << "[ cc ] [ " << kState.fLastFile << " ] " << kWhite + << reason << kBlank << std::endl; + } + + if (kAcceptableErrors > kErrorLimit) + std::exit(3); + + ++kAcceptableErrors; + } + + struct CompilerType final + { + std::string fName; + std::string fValue; + }; +} // namespace detail + +///////////////////////////////////////////////////////////////////////////////////////// + +// Target architecture. +static int kMachine = 0; + +///////////////////////////////////////// + +// REGISTERS ACCORDING TO USED ASSEMBLER + +///////////////////////////////////////// + +static size_t kRegisterCnt = kAsmRegisterLimit; +static size_t kStartUsable = 2; +static size_t kUsableLimit = 15; +static size_t kRegisterCounter = kStartUsable; +static std::string kRegisterPrefix = kAsmRegisterPrefix; + +///////////////////////////////////////// + +// COMPILER PARSING UTILITIES/STATES. + +///////////////////////////////////////// + +static std::vector kFileList; +static CompilerKit::AssemblyFactory kFactory; +static bool kInStruct = false; +static bool kOnWhileLoop = false; +static bool kOnForLoop = false; +static bool kInBraces = false; +static bool kIfFound = false; +static size_t kBracesCount = 0UL; + +/* @brief C compiler backend for C */ +class CompilerBackendCLang final : public ParserKit::CompilerBackend +{ +public: + explicit CompilerBackendCLang() = default; + ~CompilerBackendCLang() override = default; + + MPCC_COPY_DEFAULT(CompilerBackendCLang); + + std::string Check(const char* text, const char* file); + bool Compile(const std::string& text, const char* file) override; + + const char* Language() override + { + return "POWER C"; + } +}; + +static CompilerBackendCLang* kCompilerBackend = nullptr; +static std::vector kCompilerVariables; +static std::vector kCompilerFunctions; +static std::vector kCompilerTypes; + +namespace detail +{ + union number_cast final { + public: + number_cast(UInt64 _Raw) + : _Raw(_Raw) + { + } + + public: + char _Num[8]; + UInt64 _Raw; + }; + + union double_cast final { + public: + double_cast(float _Raw) + : _Raw(_Raw) + { + } + + public: + char _Sign; + char _Lh[8]; + char _Rh[23]; + + float _Raw; + }; +} // namespace detail + +///////////////////////////////////////////////////////////////////////////////////////// + +// @name Compile +// @brief Generate MASM from a C assignement. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerBackendCLang::Compile(const std::string& text, const char* file) +{ + std::string textBuffer = text; + + bool typeFound = false; + bool fnFound = false; + + // setup generator. + std::random_device rd; + + auto seed_data = std::array{}; + std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::mt19937 generator(seq); + + // start parsing + for (size_t text_index = 0; text_index < textBuffer.size(); ++text_index) + { + auto syntaxLeaf = ParserKit::SyntaxLeafList::SyntaxLeaf(); + + auto gen = uuids::uuid_random_generator{generator}; + uuids::uuid out = gen(); + + detail::number_cast time_off = (UInt64)out.as_bytes().data(); + + if (!typeFound) + { + auto substr = textBuffer.substr(text_index); + std::string match_type; + + for (size_t y = 0; y < substr.size(); ++y) + { + if (substr[y] == ' ') + { + while (match_type.find(' ') != std::string::npos) + { + match_type.erase(match_type.find(' ')); + } + + for (auto& clType : kCompilerTypes) + { + if (clType.fName == match_type) + { + match_type.clear(); + + std::string buf; + + buf += clType.fValue; + buf += ' '; + + if (substr.find('=') != std::string::npos) + { + break; + } + + if (textBuffer.find('(') != std::string::npos) + { + syntaxLeaf.fUserValue = buf; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + } + + typeFound = true; + break; + } + } + + break; + } + + match_type += substr[y]; + } + } + + if (textBuffer[text_index] == '{') + { + if (kInStruct) + { + continue; + } + + kInBraces = true; + ++kBracesCount; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + } + + // return keyword handler + if (textBuffer[text_index] == 'r') + { + std::string return_keyword; + return_keyword += "return"; + + std::size_t index = 0UL; + + std::string value; + + for (size_t return_index = text_index; return_index < textBuffer.size(); + ++return_index) + { + if (textBuffer[return_index] != return_keyword[index]) + { + for (size_t value_index = return_index; + value_index < textBuffer.size(); ++value_index) + { + if (textBuffer[value_index] == ';') + break; + + value += textBuffer[value_index]; + } + + break; + } + + ++index; + } + + if (index == return_keyword.size()) + { + if (!value.empty()) + { + if (value.find('(') != std::string::npos) + { + value.erase(value.find('(')); + } + + if (!isdigit(value[value.find('(') + 2])) + { + std::string tmp = value; + bool reg_to_reg = false; + + value.clear(); + + value += " import"; + value += tmp; + } + + syntaxLeaf.fUserValue = "\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("import") != std::string::npos) + value.erase(value.find("import"), strlen("import")); + + 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 = "__MPCC_IF_PROC_"; + kIfFunction += std::to_string(time_off._Raw); + + syntaxLeaf.fUserValue = + "\tcmpw " + "r10, r11"; + + syntaxLeaf.fUserValue += "\n\tbeq import " + kIfFunction + + " \ndword export .code64 " + kIfFunction + "\n"; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + kIfFound = true; + } + + // Parse expressions and instructions here. + // what does this mean? + // we encounter an assignment, or we reached the end of an expression. + if (textBuffer[text_index] == '=' || textBuffer[text_index] == ';') + { + if (fnFound) + continue; + if (kIfFound) + continue; + + if (textBuffer[text_index] == ';' && kInStruct) + continue; + + if (textBuffer.find("typedef ") != std::string::npos) + continue; + + if (textBuffer[text_index] == '=' && kInStruct) + { + detail::print_error( + "assignement of value 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 export .data64 "; + } + + int first_encountered = 0; + + std::string str_name; + + for (size_t text_index_2 = 0; text_index_2 < textBuffer.size(); + ++text_index_2) + { + if (textBuffer[text_index_2] == '\"') + { + ++text_index_2; + + // want to add this, so that the parser recognizes that this is a + // string. + substr += '"'; + + for (; text_index_2 < textBuffer.size(); ++text_index_2) + { + if (textBuffer[text_index_2] == '\"') + break; + + substr += textBuffer[text_index_2]; + } + } + + if (textBuffer[text_index_2] == '{' || textBuffer[text_index_2] == '}') + continue; + + if (textBuffer[text_index_2] == ';') + { + break; + } + + if (textBuffer[text_index_2] == ' ' || + textBuffer[text_index_2] == '\t') + { + if (first_encountered != 2) + { + if (textBuffer[text_index] != '=' && + substr.find("export .data64") == std::string::npos && + !kInStruct) + substr += "export .data64 "; + } + + ++first_encountered; + + continue; + } + + if (textBuffer[text_index_2] == '=') + { + if (!kInBraces) + { + substr.replace(substr.find("export .data64"), + strlen("export .data64"), "export .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"), "import "); + + if (substr.find("export .data64") != std::string::npos) + substr.erase(substr.find("export .data64"), strlen("export .data64")); + } + + auto var_to_find = + std::find_if(kCompilerVariables.cbegin(), kCompilerVariables.cend(), + [&](detail::CompilerType type) { + return type.fName.find(substr) != std::string::npos; + }); + + 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 += "export .code64 "; + + syntaxLeaf.fUserValue += substr; + syntaxLeaf.fUserValue += "\n"; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + fnFound = true; + } + + kCompilerFunctions.push_back(textBuffer); + } + + if (textBuffer[text_index] == '-' && textBuffer[text_index + 1] == '-') + { + textBuffer = textBuffer.replace(textBuffer.find("--"), strlen("--"), ""); + + for (int _text_i = 0; _text_i < textBuffer.size(); ++_text_i) + { + if (textBuffer[_text_i] == '\t' || textBuffer[_text_i] == ' ') + textBuffer.erase(_text_i, 1); + } + + syntaxLeaf.fUserValue += "dec "; + syntaxLeaf.fUserValue += textBuffer; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + break; + } + + if (textBuffer[text_index] == '}') + { + kRegisterCounter = kStartUsable; + + --kBracesCount; + + if (kBracesCount < 1) + { + kInBraces = false; + kBracesCount = 0; + } + + if (kIfFound) + kIfFound = false; + + if (kInStruct) + kInStruct = false; + + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + } + + syntaxLeaf.fUserValue.clear(); + } + + auto syntaxLeaf = ParserKit::SyntaxLeafList::SyntaxLeaf(); + syntaxLeaf.fUserValue = "\n"; + kState.fSyntaxTree->fLeafList.push_back(syntaxLeaf); + + return true; +} + +static bool kShouldHaveBraces = false; +static std::string kFnName; + +std::string CompilerBackendCLang::Check(const char* text, const char* file) +{ + std::string err_str; + std::string ln = text; + + if (ln.empty()) + { + return err_str; + } + + bool non_ascii_found = false; + + for (int i = 0; i < ln.size(); ++i) + { + if (isalnum(ln[i])) + { + non_ascii_found = true; + break; + } + } + + if (kShouldHaveBraces && ln.find('{') != std::string::npos) + { + kShouldHaveBraces = false; + } + + if (!non_ascii_found) + return err_str; + + size_t string_index = 1UL; + + if (ln.find('\'') != std::string::npos) + { + string_index = ln.find('\'') + 1; + + for (; string_index < ln.size(); ++string_index) + { + if (ln[string_index] == '\'') + { + if (ln[string_index + 1] != ';') + { + ln.erase(string_index, 1); + } + + return err_str; + } + } + } + else if (ln.find('"') != std::string::npos) + { + string_index = ln.find('"') + 1; + + for (; string_index < ln.size(); ++string_index) + { + if (ln[string_index] == '"') + { + if (ln[string_index + 1] != ';') + { + ln.erase(string_index, 1); + } + else + { + break; + } + } + } + } + else if (ln.find('"') == std::string::npos && + ln.find('\'') == std::string::npos) + { + std::vector 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 variables_list = { + {.fBegin = "static ", .fEnd = "="}, + {.fBegin = "=", .fEnd = ";"}, + {.fBegin = "if(", .fEnd = "="}, + {.fBegin = "if (", .fEnd = "="}, + {.fBegin = "if(", .fEnd = "<"}, + {.fBegin = "if (", .fEnd = "<"}, + {.fBegin = "if(", .fEnd = ">"}, + {.fBegin = "if (", .fEnd = ">"}, + {.fBegin = "if(", .fEnd = ")"}, + {.fBegin = "if (", .fEnd = ")"}, + + {.fBegin = "else(", .fEnd = "="}, + {.fBegin = "else (", .fEnd = "="}, + {.fBegin = "else(", .fEnd = "<"}, + {.fBegin = "else (", .fEnd = "<"}, + {.fBegin = "else(", .fEnd = ">"}, + {.fBegin = "else (", .fEnd = ">"}, + {.fBegin = "else(", .fEnd = ")"}, + {.fBegin = "else (", .fEnd = ")"}, + }; + + for (auto& variable : variables_list) + { + if (ln.find(variable.fBegin) != std::string::npos) + { + string_index = ln.find(variable.fBegin) + variable.fBegin.size(); + + while (ln[string_index] == ' ') + ++string_index; + + std::string keyword; + + for (; string_index < ln.size(); ++string_index) + { + if (ln[string_index] == variable.fEnd[0]) + { + std::string varname = ""; + + for (size_t index_keyword = ln.find(' '); + ln[index_keyword] != variable.fBegin[0]; ++index_keyword) + { + if (ln[index_keyword] == ' ') + { + continue; + } + + if (isdigit(ln[index_keyword])) + { + goto cc_next_loop; + } + + varname += ln[index_keyword]; + } + + if (varname.find(' ') != std::string::npos) + { + varname.erase(0, varname.find(' ')); + + if (variable.fBegin == "extern") + { + varname.erase(0, varname.find(' ')); + } + } + + if (kRegisterCounter == 5 || kRegisterCounter == 6) + ++kRegisterCounter; + + std::string reg = kAsmRegisterPrefix; + reg += std::to_string(kRegisterCounter); + + kCompilerVariables.push_back({.fValue = varname}); + goto cc_check_done; + } + + keyword.push_back(ln[string_index]); + } + + goto cc_next_loop; + + cc_check_done: + + // skip digit value. + if (isdigit(keyword[0]) || keyword[0] == '"') + { + goto cc_next_loop; + } + + while (keyword.find(' ') != std::string::npos) + keyword.erase(keyword.find(' '), 1); + + for (auto& var : kCompilerVariables) + { + if (var.fValue.find(keyword) != std::string::npos) + { + err_str.clear(); + goto cc_next; + } + } + + for (auto& fn : kCompilerFunctions) + { + if (fn.find(keyword[0]) != std::string::npos) + { + auto where_begin = fn.find(keyword[0]); + auto keyword_begin = 0UL; + auto failed = false; + + for (; where_begin < keyword.size(); ++where_begin) + { + if (fn[where_begin] == '(' && keyword[keyword_begin] == '(') + break; + + if (fn[where_begin] != keyword[keyword_begin]) + { + failed = true; + break; + } + + ++keyword_begin; + } + + if (!failed) + { + err_str.clear(); + goto cc_next; + } + else + { + continue; + } + } + } + + cc_error_value: + if (keyword.find("->") != std::string::npos) + return err_str; + + if (keyword.find(".") != std::string::npos) + return err_str; + + if (isalnum(keyword[0])) + err_str += "\nUndefined value: " + keyword; + + return err_str; + } + + cc_next_loop: + continue; + } + +cc_next: + + // extern does not declare anything, it imports a variable. + // so that's why it's not declare upper. + if (ParserKit::find_word(ln, "extern")) + { + auto substr = ln.substr(ln.find("extern") + strlen("extern")); + kCompilerVariables.push_back({.fValue = substr}); + } + + if (kShouldHaveBraces && ln.find('{') == std::string::npos) + { + err_str += "Missing '{' for function "; + err_str += kFnName; + err_str += "\n"; + + kShouldHaveBraces = false; + kFnName.clear(); + } + else if (kShouldHaveBraces && ln.find('{') != std::string::npos) + { + kShouldHaveBraces = false; + kFnName.clear(); + } + + bool type_not_found = true; + + if (ln.find('\'') != std::string::npos) + { + ln.replace(ln.find('\''), 3, "0"); + } + + auto first = ln.find('"'); + if (first != std::string::npos) + { + auto second = 0UL; + bool found_second_quote = false; + + for (size_t i = first + 1; i < ln.size(); ++i) + { + if (ln[i] == '\"') + { + found_second_quote = true; + second = i; + + break; + } + } + + if (!found_second_quote) + { + err_str += "Missing terminating \"."; + err_str += " here -> " + ln.substr(ln.find('"'), second); + } + } + + if (ln.find(')') != std::string::npos && ln.find(';') == std::string::npos) + { + if (ln.find('{') == std::string::npos) + { + kFnName = ln; + kShouldHaveBraces = true; + + goto skip_braces_check; + } + else if (ln.find('{') != std::string::npos) + { + kShouldHaveBraces = false; + } + } + +skip_braces_check: + + for (auto& key : kCompilerTypes) + { + if (ParserKit::find_word(ln, key.fName)) + { + if (isdigit(ln[ln.find(key.fName) + key.fName.size() + 1])) + { + err_str += "\nNumber cannot be set for "; + err_str += key.fName; + err_str += "'s name. here -> "; + err_str += ln; + } + + if (ln.find(key.fName) == 0 || ln[ln.find(key.fName) - 1] == ' ' || + ln[ln.find(key.fName) - 1] == '\t') + { + type_not_found = false; + + if (ln[ln.find(key.fName) + key.fName.size()] != ' ') + { + type_not_found = true; + + if (ln[ln.find(key.fName) + key.fName.size()] == '\t') + type_not_found = false; + + goto next; + } + else if (ln[ln.find(key.fName) + key.fName.size()] != '\t') + { + type_not_found = true; + + if (ln[ln.find(key.fName) + key.fName.size()] == ' ') + type_not_found = false; + } + } + + next: + + if (ln.find(';') == std::string::npos) + { + if (ln.find('(') != std::string::npos) + { + if (ln.find('=') == std::string::npos) + continue; + } + + err_str += "\nMissing ';', here -> "; + err_str += ln; + } + else + { + continue; + } + + if (ln.find('=') != std::string::npos) + { + if (ln.find('(') != std::string::npos) + { + if (ln.find(')') == std::string::npos) + { + err_str += "\nMissing ')', after '(' here -> "; + err_str += ln.substr(ln.find('(')); + } + } + } + } + } + + if (kInBraces && ln.find("struct") != std::string::npos && + ln.find("union") != std::string::npos && + ln.find("enum") != std::string::npos && + ln.find('=') != std::string::npos) + { + if (ln.find(';') == std::string::npos) + { + err_str += "\nMissing ';' after struct/union/enum declaration, here -> "; + err_str += ln; + } + } + + if (ln.find(';') != std::string::npos && + ln.find("for") == std::string::npos) + { + if (ln.find(';') + 1 != ln.size()) + { + for (int i = 0; i < ln.substr(ln.find(';') + 1).size(); ++i) + { + if ((ln.substr(ln.find(';') + 1)[i] != ' ') || + (ln.substr(ln.find(';') + 1)[i] != '\t')) + { + if (auto err = this->Check(ln.substr(ln.find(';') + 1).c_str(), file); + !err.empty()) + { + err_str += "\nUnexpected text after ';' -> "; + err_str += ln.substr(ln.find(';')); + err_str += err; + } + } + } + } + } + + if (ln.find('(') != std::string::npos) + { + if (ln.find(';') == std::string::npos && !ParserKit::find_word(ln, "|") && + !ParserKit::find_word(ln, "||") && !ParserKit::find_word(ln, "&") && + !ParserKit::find_word(ln, "&&") && !ParserKit::find_word(ln, "~")) + { + bool found_func = false; + size_t i = ln.find('('); + std::vector opens; + std::vector closes; + + for (; i < ln.size(); ++i) + { + if (ln[i] == ')') + { + closes.push_back(1); + } + + if (ln[i] == '(') + { + opens.push_back(1); + } + } + + if (closes.size() != opens.size()) + err_str += "Unterminated (), here -> " + ln; + + bool space_found = false; + + for (int i = 0; i < ln.size(); ++i) + { + if (ln[i] == ')' && !space_found) + { + space_found = true; + continue; + } + + if (space_found) + { + if (ln[i] == ' ' && isalnum(ln[i + 1])) + { + err_str += "\nBad function format here -> "; + err_str += ln; + } + } + } + } + + if (ln.find('(') < 1) + { + err_str += "\nMissing identifier before '(' here -> "; + err_str += ln; + } + else + { + if (type_not_found && ln.find(';') == std::string::npos && + ln.find("if") == std::string::npos && + ln.find("|") == std::string::npos && + ln.find("&") == std::string::npos && + ln.find("(") == std::string::npos && + ln.find(")") == std::string::npos) + { + err_str += "\n Missing ';' or type, here -> "; + err_str += ln; + } + } + + if (ln.find(')') == std::string::npos) + { + err_str += "\nMissing ')', after '(' here -> "; + err_str += ln.substr(ln.find('(')); + } + } + else + { + if (ln.find("for") != std::string::npos || + ln.find("while") != std::string::npos) + { + err_str += "\nMissing '(', after \"for\", here -> "; + err_str += ln; + } + } + + if (ln.find('}') != std::string::npos && !kInBraces) + { + if (!kInStruct && ln.find(';') == std::string::npos) + { + err_str += "\nMismatched '}', here -> "; + err_str += ln; + } + } + + if (!ln.empty()) + { + if (ln.find(';') == std::string::npos && + ln.find('{') == std::string::npos && + ln.find('}') == std::string::npos && + ln.find(')') == std::string::npos && + ln.find('(') == std::string::npos && + ln.find(',') == std::string::npos) + { + if (ln.size() <= 2) + return err_str; + + err_str += "\nMissing ';', here -> "; + err_str += ln; + } + } + + return err_str; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/** + * @brief C To Assembly mount-point. + */ + +///////////////////////////////////////////////////////////////////////////////////////// + +class AssemblyMountpointCLang final : public CompilerKit::AssemblyInterface +{ +public: + explicit AssemblyMountpointCLang() = default; + ~AssemblyMountpointCLang() override = default; + + MPCC_COPY_DEFAULT(AssemblyMountpointCLang); + + [[maybe_unused]] static Int32 Arch() noexcept + { + return CompilerKit::AssemblyFactory::kArchPowerPC; + } + + Int32 CompileToFormat(std::string& src, Int32 arch) override + { + if (arch != AssemblyMountpointCLang::Arch()) + return -1; + + if (kCompilerBackend == nullptr) + return -1; + + /* @brief copy contents wihtout extension */ + std::string src_file = src.data(); + std::ifstream src_fp = std::ifstream(src_file, std::ios::in); + std::string dest; + + for (auto& ch : src_file) + { + if (ch == '.') + { + break; + } + + dest += ch; + } + + /* According to PEF ABI. */ + std::vector exts = kAsmFileExts; + dest += exts[4]; + + kState.fOutputAssembly = std::make_unique(dest); + + auto fmt = CompilerKit::current_date(); + + (*kState.fOutputAssembly) << "# Path: " << src_file << "\n"; + (*kState.fOutputAssembly) + << "# Language: POWER Assembly (Generated from C)\n"; + (*kState.fOutputAssembly) << "# Date: " << fmt << "\n\n"; + + ParserKit::SyntaxLeafList syntax; + + kState.fSyntaxTreeList.push_back(syntax); + kState.fSyntaxTree = + &kState.fSyntaxTreeList[kState.fSyntaxTreeList.size() - 1]; + + std::string line_src; + + while (std::getline(src_fp, line_src)) + { + if (auto err = kCompilerBackend->Check(line_src.c_str(), src.data()); + err.empty()) + { + kCompilerBackend->Compile(line_src, src.data()); + } + else + { + detail::print_error(err, src.data()); + } + } + + if (kAcceptableErrors > 0) + return -1; + + std::vector keywords = {"ld", "stw", "add", "sub", "or"}; + + /// + /// Replace, optimize, fix assembly output. + /// + + for (auto& leaf : kState.fSyntaxTree->fLeafList) + { + std::vector access_keywords = {"->", "."}; + + for (auto& access_ident : access_keywords) + { + if (ParserKit::find_word(leaf.fUserValue, access_ident)) + { + for (auto& struc : kState.kStructMap) + { + /// TODO: + } + } + } + + for (auto& keyword : keywords) + { + if (ParserKit::find_word(leaf.fUserValue, keyword)) + { + std::size_t cnt = 0UL; + + for (auto& reg : kState.kStackFrame) + { + std::string needle; + + for (size_t i = 0; i < reg.fName.size(); i++) + { + if (reg.fName[i] == ' ') + { + ++i; + + for (; i < reg.fName.size(); i++) + { + if (reg.fName[i] == ',') + { + break; + } + + if (reg.fName[i] == ' ') + continue; + + needle += reg.fName[i]; + } + + break; + } + } + + if (ParserKit::find_word(leaf.fUserValue, needle)) + { + if (leaf.fUserValue.find("import ") != std::string::npos) + { + std::string range = "import "; + 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 kOk; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +#include + +#define kPrintF printf +#define kSplashCxx() \ + kPrintF(kWhite "cc, %s, (c) ZKA Technologies\n", kDistVersion) + +static void cc_print_help() +{ + kSplashCxx(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define kExt ".c" + +NDK_MODULE(NewOSCompilerCLangPowerPC) +{ + kCompilerTypes.push_back({.fName = "void", .fValue = "void"}); + kCompilerTypes.push_back({.fName = "char", .fValue = "byte"}); + kCompilerTypes.push_back({.fName = "short", .fValue = "hword"}); + kCompilerTypes.push_back({.fName = "int", .fValue = "dword"}); + kCompilerTypes.push_back({.fName = "long", .fValue = "qword"}); + kCompilerTypes.push_back({.fName = "*", .fValue = "offset"}); + + bool skip = false; + + kFactory.Mount(new AssemblyMountpointCLang()); + kMachine = CompilerKit::AssemblyFactory::kArchPowerPC; + kCompilerBackend = new CompilerBackendCLang(); + + for (auto index = 1UL; index < argc; ++index) + { + if (skip) + { + skip = false; + continue; + } + + if (argv[index][0] == '-') + { + if (strcmp(argv[index], "-v") == 0 || + strcmp(argv[index], "-version") == 0) + { + kSplashCxx(); + return kOk; + } + + if (strcmp(argv[index], "-verbose") == 0) + { + kState.fVerbose = true; + + continue; + } + + if (strcmp(argv[index], "-h") == 0 || strcmp(argv[index], "-help") == 0) + { + cc_print_help(); + + return kOk; + } + + if (strcmp(argv[index], "-dialect") == 0) + { + if (kCompilerBackend) + std::cout << kCompilerBackend->Language() << "\n"; + + return kOk; + } + + if (strcmp(argv[index], "-fmax-exceptions") == 0) + { + try + { + kErrorLimit = std::strtol(argv[index + 1], nullptr, 10); + } + // catch anything here + catch (...) + { + kErrorLimit = 0; + } + + skip = true; + + continue; + } + + std::string err = "Unknown command: "; + err += argv[index]; + + detail::print_error(err, "cc"); + + continue; + } + + kFileList.emplace_back(argv[index]); + + std::string srcFile = argv[index]; + + if (strstr(argv[index], kExt) == nullptr) + { + if (kState.fVerbose) + { + detail::print_error(srcFile + " is not a valid C source.\n", "cc"); + } + + return 1; + } + + if (kFactory.Compile(srcFile, kMachine) != kOk) + return -1; + } + + return kOk; +} + +// Last rev 8-1-24 diff --git a/Sources/ppcasm.cc b/Sources/ppcasm.cc deleted file mode 100644 index 5f015d5..0000000 --- a/Sources/ppcasm.cc +++ /dev/null @@ -1,976 +0,0 @@ -/* ------------------------------------------- - - Copyright ZKA Technologies - -------------------------------------------- */ - -///////////////////////////////////////////////////////////////////////////////////////// - -/// @file ppcasm.cxx -/// @author Amlal EL Mahrouss -/// @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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -///////////////////// - -// 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 = CompilerKit::kPefArchPowerPC; -static Boolean kOutputAsBinary = false; - -static UInt32 kErrorLimit = 10; -static UInt32 kAcceptableErrors = 0; - -static std::size_t kCounter = 1UL; - -static std::uintptr_t kOrigin = kPefBaseOrigin; -static std::vector> kOriginLabel; - -static bool kVerbose = false; - -static std::vector kBytes; - -static CompilerKit::AERecordHeader kCurrentRecord{ - .fName = "", .fKind = CompilerKit::kPefCode, .fSize = 0, .fOffset = 0}; - -static std::vector kRecords; -static std::vector kUndefinedSymbols; - -static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; -static const std::string kRelocSymbol = ":RuntimeSymbol:"; - -// \brief forward decl. -static bool asm_read_attributes(std::string &line); - -namespace detail { -void print_error(std::string reason, const std::string &file) noexcept { - if (reason[0] == '\n') reason.erase(0, 1); - - kStdErr << kRed << "[ ppcasm ] " << kWhite - << ((file == "ppcasm") ? "internal assembler error " - : ("in file, " + file)) - << kBlank << std::endl; - kStdErr << kRed << "[ ppcasm ] " << kWhite << reason << kBlank << std::endl; - - if (kAcceptableErrors > kErrorLimit) std::exit(3); - - ++kAcceptableErrors; -} - -void print_warning(std::string reason, const std::string &file) noexcept { - if (reason[0] == '\n') reason.erase(0, 1); - - if (!file.empty()) { - kStdOut << kYellow << "[ file ] " << kWhite << file << kBlank << std::endl; - } - - kStdOut << kYellow << "[ ppcasm ] " << kWhite << reason << kBlank - << std::endl; -} -} // namespace detail - -/// Do not move it on top! it uses the assembler detail namespace! -#include - -///////////////////////////////////////////////////////////////////////////////////////// - -/// @brief POWER assembler entrypoint, the program/module starts here. - -///////////////////////////////////////////////////////////////////////////////////////// - -NDK_MODULE(NewOSAssemblerPowerPC) { - for (size_t i = 1; i < argc; ++i) { - if (argv[i][0] == '/') { - if (strcmp(argv[i], "/version") == 0 || strcmp(argv[i], "/v") == 0) { - kStdOut << "ppcasm: POWER64 Assembler Driver.\nppcasm: " << kDistVersion << "\nppcasm: " - "Copyright (c) " - "ZKA Technologies.\n"; - return 0; - } else if (strcmp(argv[i], "/h") == 0) { - kStdOut << "ppcasm: POWER64 Assembler Driver.\nppcasm: Copyright (c) 2024 " - "ZKA Technologies.\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 << "ppcasm: ignore " << argv[i] << "\n"; - continue; - } - - if (!std::filesystem::exists(argv[i])) { - kStdOut << "ppcasm: can't open: " << argv[i] << std::endl; - goto asm_fail_exit; - } - - std::string object_output(argv[i]); - - for (auto &ext : kAsmFileExts) { - if (object_output.find(ext) != std::string::npos) { - object_output.erase(object_output.find(ext), std::strlen(ext)); - } - } - - object_output += 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 << "ppcasm: error: " << strerror(errno) << "\n"; - } - } - - std::string line; - - CompilerKit::AEHeader hdr{0}; - - memset(hdr.fPad, kAEInvalidOpcode, kAEPad); - - hdr.fMagic[0] = kAEMag0; - hdr.fMagic[1] = kAEMag1; - hdr.fSize = sizeof(CompilerKit::AEHeader); - hdr.fArch = kOutputArch; - - ///////////////////////////////////////////////////////////////////////////////////////// - - // COMPILATION LOOP - - ///////////////////////////////////////////////////////////////////////////////////////// - - CompilerKit::EncoderPowerPC asm64; - - while (std::getline(file_ptr, line)) { - if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) { - detail::print_error(ln, argv[i]); - continue; - } - - try { - asm_read_attributes(line); - asm64.WriteLine(line, argv[i]); - } catch (const std::exception &e) { - if (kVerbose) { - std::string what = e.what(); - detail::print_warning("exit because of: " + what, "ppcasm"); - } - - std::filesystem::remove(object_output); - goto asm_fail_exit; - } - } - - if (!kOutputAsBinary) { - if (kVerbose) { - kStdOut << "ppcasm: Writing object file...\n"; - } - - // this is the final step, write everything to the file. - - auto pos = file_ptr_out.tellp(); - - hdr.fCount = kRecords.size() + kUndefinedSymbols.size(); - - file_ptr_out << hdr; - - if (kRecords.empty()) { - kStdErr << "ppcasm: At least one record is needed to write an object " - "file.\nppcasm: Make one using `export .code64 foo_bar`.\n"; - - std::filesystem::remove(object_output); - return -1; - } - - kRecords[kRecords.size() - 1].fSize = kBytes.size(); - - std::size_t record_count = 0UL; - - for (auto &record_hdr : kRecords) { - record_hdr.fFlags |= CompilerKit::kKindRelocationAtRuntime; - record_hdr.fOffset = record_count; - ++record_count; - - file_ptr_out << record_hdr; - - if (kVerbose) - kStdOut << "ppcasm: Wrote record " << record_hdr.fName << "...\n"; - } - - // increment once again, so that we won't lie about the kUndefinedSymbols. - ++record_count; - - for (auto &sym : kUndefinedSymbols) { - CompilerKit::AERecordHeader undefined_sym{0}; - - if (kVerbose) - kStdOut << "ppcasm: Wrote symbol " << sym << " to file...\n"; - - undefined_sym.fKind = kAEInvalidOpcode; - undefined_sym.fSize = sym.size(); - undefined_sym.fOffset = record_count; - - ++record_count; - - memset(undefined_sym.fPad, kAEInvalidOpcode, 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 << "ppcasm: Write raw binary...\n"; - } - } - - // byte from byte, we write this. - for (auto &byte : kBytes) { - file_ptr_out.write(reinterpret_cast(&byte), sizeof(byte)); - } - - if (kVerbose) kStdOut << "ppcasm: Wrote file with program in it.\n"; - - file_ptr_out.flush(); - file_ptr_out.close(); - - if (kVerbose) kStdOut << "ppcasm: Exit succeeded.\n"; - - return 0; - } - -asm_fail_exit: - - if (kVerbose) kStdOut << "ppcasm: Exit failed.\n"; - - return MPCC_EXEC_ERROR; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief Check for attributes -// returns true if any was found. - -///////////////////////////////////////////////////////////////////////////////////////// - -static bool asm_read_attributes(std::string &line) { - // import is the opposite of export, it signals to the li - // that we need this symbol. - if (ParserKit::find_word(line, "import")) { - if (kOutputAsBinary) { - detail::print_error("Invalid import directive in flat binary mode.", - "ppcasm"); - throw std::runtime_error("invalid_import_bin"); - } - - auto name = line.substr(line.find("import") + strlen("import") + 1); - - if (name.size() == 0) { - detail::print_error("Invalid import", "ppcasm"); - throw std::runtime_error("invalid_import"); - } - - std::string result = std::to_string(name.size()); - result += kUndefinedSymbol; - - // mangle this - for (char &j : name) { - if (j == ' ' || j == ',') j = '$'; - } - - result += name; - - if (name.find(".code64") != std::string::npos) { - // data is treated as code. - kCurrentRecord.fKind = CompilerKit::kPefCode; - } else if (name.find(".data64") != std::string::npos) { - // no code will be executed from here. - kCurrentRecord.fKind = CompilerKit::kPefData; - } else if (name.find(".zero64") != std::string::npos) { - // this is a bss section. - kCurrentRecord.fKind = CompilerKit::kPefZero; - } - - // this is a special case for the start stub. - // we want this so that li can find it. - - if (name == kPefStart) { - kCurrentRecord.fKind = CompilerKit::kPefCode; - } - - // now we can tell the code size of the previous kCurrentRecord. - - if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); - - memset(kCurrentRecord.fName, 0, kAESymbolLen); - memcpy(kCurrentRecord.fName, result.c_str(), result.size()); - - ++kCounter; - - memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); - - kRecords.emplace_back(kCurrentRecord); - - return true; - } - // export is a special keyword used by ppcasm to tell the AE output stage to - // mark this section as a header. it currently supports .code64, .data64., - // .zero64 - else if (ParserKit::find_word(line, "export")) { - if (kOutputAsBinary) { - detail::print_error("Invalid export directive in flat binary mode.", - "ppcasm"); - throw std::runtime_error("invalid_export_bin"); - } - - auto name = line.substr(line.find("export") + strlen("export")); - - std::string name_copy = name; - - for (char &j : name) { - if (j == ' ') j = '$'; - } - - if (name.find(".code64") != std::string::npos) { - // data is treated as code. - - name_copy.erase(name_copy.find(".code64"), strlen(".code64")); - kCurrentRecord.fKind = CompilerKit::kPefCode; - } else if (name.find(".data64") != std::string::npos) { - // no code will be executed from here. - - name_copy.erase(name_copy.find(".data64"), strlen(".data64")); - kCurrentRecord.fKind = CompilerKit::kPefData; - } else if (name.find(".zero64") != std::string::npos) { - // this is a bss section. - - name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); - kCurrentRecord.fKind = CompilerKit::kPefZero; - } - - // this is a special case for the start stub. - // we want this so that li can find it. - - if (name == kPefStart) { - kCurrentRecord.fKind = CompilerKit::kPefCode; - } - - while (name_copy.find(" ") != std::string::npos) - name_copy.erase(name_copy.find(" "), 1); - - kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); - ++kOrigin; - - // now we can tell the code size of the previous kCurrentRecord. - - if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); - - memset(kCurrentRecord.fName, 0, kAESymbolLen); - memcpy(kCurrentRecord.fName, name.c_str(), name.size()); - - ++kCounter; - - memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); - - kRecords.emplace_back(kCurrentRecord); - - return true; - } - - return false; -} - -// \brief algorithms and helpers. - -namespace detail::algorithm { -// \brief authorize a brief set of characters. -static inline bool is_not_alnum_space(char c) { - return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || - (c == ',') || (c == '(') || (c == ')') || (c == '"') || - (c == '\'') || (c == '[') || (c == ']') || (c == '+') || - (c == '_') || (c == ':') || (c == '@') || (c == '.')); -} - -bool is_valid(const std::string &str) { - return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); -} -} // namespace detail::algorithm - -///////////////////////////////////////////////////////////////////////////////////////// - -// @brief Check for line (syntax check) - -///////////////////////////////////////////////////////////////////////////////////////// - -std::string CompilerKit::EncoderPowerPC::CheckLine(std::string &line, - const std::string &file) { - std::string err_str; - - if (line.empty() || ParserKit::find_word(line, "import") || - ParserKit::find_word(line, "export") || - line.find('#') != std::string::npos || ParserKit::find_word(line, ";")) { - if (line.find('#') != std::string::npos) { - line.erase(line.find('#')); - } else if (line.find(';') != std::string::npos) { - line.erase(line.find(';')); - } else { - /// does the line contains valid input? - if (!detail::algorithm::is_valid(line)) { - err_str = "Line contains non alphanumeric characters.\nhere -> "; - err_str += line; - } - } - - return err_str; - } - - if (!detail::algorithm::is_valid(line)) { - err_str = "Line contains non alphanumeric characters.\nhere -> "; - err_str += line; - - return err_str; - } - - // check for a valid instruction format. - - if (line.find(',') != std::string::npos) { - if (line.find(',') + 1 == line.size()) { - err_str += "\nInstruction lacks right register, here -> "; - err_str += line.substr(line.find(',')); - - return err_str; - } else { - bool nothing_on_right = true; - - if (line.find(',') + 1 > line.size()) { - err_str += "\nInstruction not complete, here -> "; - err_str += line; - - return err_str; - } - - auto substr = line.substr(line.find(',') + 1); - - for (auto &ch : substr) { - if (ch != ' ' && ch != '\t') { - nothing_on_right = false; - } - } - - // this means we found nothing after that ',' . - if (nothing_on_right) { - err_str += "\nInstruction not complete, here -> "; - err_str += line; - - return err_str; - } - } - } - - // these do take an argument. - std::vector operands_inst = {"stw", "li"}; - - // these don't. - std::vector filter_inst = {"blr", "bl", "sc"}; - - for (auto &opcodePPC : kOpcodesPowerPC) { - if (ParserKit::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 (ParserKit::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 CompilerKit::EncoderPowerPC::WriteNumber(const std::size_t &pos, - std::string &jump_label) { - if (!isdigit(jump_label[pos])) return false; - - switch (jump_label[pos + 1]) { - case 'x': { - if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); - !res) { - if (errno != 0) { - detail::print_error("invalid hex number: " + jump_label, "ppcasm"); - throw std::runtime_error("invalid_hex"); - } - } - - CompilerKit::NumberCast64 num( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); - - for (char &i : num.number) { - kBytes.push_back(i); - } - - if (kVerbose) { - kStdOut << "ppcasm: found a base 16 number here: " - << jump_label.substr(pos) << "\n"; - } - - return true; - } - case 'b': { - if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); - !res) { - if (errno != 0) { - detail::print_error("invalid binary number: " + jump_label, "ppcasm"); - throw std::runtime_error("invalid_bin"); - } - } - - CompilerKit::NumberCast64 num( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); - - if (kVerbose) { - kStdOut << "ppcasm: found a base 2 number here: " - << jump_label.substr(pos) << "\n"; - } - - for (char &i : num.number) { - kBytes.push_back(i); - } - - return true; - } - case 'o': { - if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); - !res) { - if (errno != 0) { - detail::print_error("invalid octal number: " + jump_label, "ppcasm"); - throw std::runtime_error("invalid_octal"); - } - } - - CompilerKit::NumberCast64 num( - strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); - - if (kVerbose) { - kStdOut << "ppcasm: found a base 8 number here: " - << jump_label.substr(pos) << "\n"; - } - - for (char &i : num.number) { - kBytes.push_back(i); - } - - return true; - } - default: { - break; - } - } - - /* check for errno and stuff like that */ - if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { - if (errno != 0) { - return false; - } - } - - CompilerKit::NumberCast64 num( - strtol(jump_label.substr(pos).c_str(), nullptr, 10)); - - for (char &i : num.number) { - kBytes.push_back(i); - } - - if (kVerbose) { - kStdOut << "ppcasm: found a base 10 number here: " << jump_label.substr(pos) - << "\n"; - } - - return true; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -/// @brief Read and write an instruction to the output array. - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CompilerKit::EncoderPowerPC::WriteLine(std::string &line, - const std::string &file) { - if (ParserKit::find_word(line, "export")) return true; - if (!detail::algorithm::is_valid(line)) return true; - - for (auto &opcodePPC : kOpcodesPowerPC) { - // strict check here - if (ParserKit::find_word(line, opcodePPC.name)) { - std::string name(opcodePPC.name); - std::string jump_label, cpy_jump_label; - std::vector 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]; - detail::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) { - detail::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) { - detail::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) { - detail::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) { - detail::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) { - detail::print_error("Too much registers. -> " + line, file); - throw std::runtime_error("too_much_regs"); - } - - if (kVerbose) { - kStdOut << "ppcasm: Found register: " << register_syntax - << "\n"; - kStdOut << "ppcasm: 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) { - detail::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) { - detail::print_error( - "Unrecognized register found.\ntip: each ppcasm register " - "starts with 'r'.\nline: " + - line, - file); - - throw std::runtime_error("not_a_register"); - } - } - - if (found_some_count < 1 && name[0] != 'l' && name[0] != 's') { - detail::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/Sources/ppcasm.cxx b/Sources/ppcasm.cxx new file mode 100644 index 0000000..01e8124 --- /dev/null +++ b/Sources/ppcasm.cxx @@ -0,0 +1,976 @@ +/* ------------------------------------------- + + Copyright ZKA Technologies + +------------------------------------------- */ + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @file ppcasm.cxx +/// @author Amlal EL Mahrouss +/// @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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///////////////////// + +// 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 = CompilerKit::kPefArchPowerPC; +static Boolean kOutputAsBinary = false; + +static UInt32 kErrorLimit = 10; +static UInt32 kAcceptableErrors = 0; + +static std::size_t kCounter = 1UL; + +static std::uintptr_t kOrigin = kPefBaseOrigin; +static std::vector> kOriginLabel; + +static bool kVerbose = false; + +static std::vector kBytes; + +static CompilerKit::AERecordHeader kCurrentRecord{ + .fName = "", .fKind = CompilerKit::kPefCode, .fSize = 0, .fOffset = 0}; + +static std::vector kRecords; +static std::vector kUndefinedSymbols; + +static const std::string kUndefinedSymbol = ":UndefinedSymbol:"; +static const std::string kRelocSymbol = ":RuntimeSymbol:"; + +// \brief forward decl. +static bool asm_read_attributes(std::string &line); + +namespace detail { +void print_error(std::string reason, const std::string &file) noexcept { + if (reason[0] == '\n') reason.erase(0, 1); + + kStdErr << kRed << "[ ppcasm ] " << kWhite + << ((file == "ppcasm") ? "internal assembler error " + : ("in file, " + file)) + << kBlank << std::endl; + kStdErr << kRed << "[ ppcasm ] " << kWhite << reason << kBlank << std::endl; + + if (kAcceptableErrors > kErrorLimit) std::exit(3); + + ++kAcceptableErrors; +} + +void print_warning(std::string reason, const std::string &file) noexcept { + if (reason[0] == '\n') reason.erase(0, 1); + + if (!file.empty()) { + kStdOut << kYellow << "[ file ] " << kWhite << file << kBlank << std::endl; + } + + kStdOut << kYellow << "[ ppcasm ] " << kWhite << reason << kBlank + << std::endl; +} +} // namespace detail + +/// Do not move it on top! it uses the assembler detail namespace! +#include + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @brief POWER assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +NDK_MODULE(NewOSAssemblerPowerPC) { + for (size_t i = 1; i < argc; ++i) { + if (argv[i][0] == '/') { + if (strcmp(argv[i], "/version") == 0 || strcmp(argv[i], "/v") == 0) { + kStdOut << "ppcasm: POWER64 Assembler Driver.\nppcasm: " << kDistVersion << "\nppcasm: " + "Copyright (c) " + "ZKA Technologies.\n"; + return 0; + } else if (strcmp(argv[i], "/h") == 0) { + kStdOut << "ppcasm: POWER64 Assembler Driver.\nppcasm: Copyright (c) 2024 " + "ZKA Technologies.\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 << "ppcasm: ignore " << argv[i] << "\n"; + continue; + } + + if (!std::filesystem::exists(argv[i])) { + kStdOut << "ppcasm: can't open: " << argv[i] << std::endl; + goto asm_fail_exit; + } + + std::string object_output(argv[i]); + + for (auto &ext : kAsmFileExts) { + if (object_output.find(ext) != std::string::npos) { + object_output.erase(object_output.find(ext), std::strlen(ext)); + } + } + + object_output += 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 << "ppcasm: error: " << strerror(errno) << "\n"; + } + } + + std::string line; + + CompilerKit::AEHeader hdr{0}; + + memset(hdr.fPad, kAEInvalidOpcode, kAEPad); + + hdr.fMagic[0] = kAEMag0; + hdr.fMagic[1] = kAEMag1; + hdr.fSize = sizeof(CompilerKit::AEHeader); + hdr.fArch = kOutputArch; + + ///////////////////////////////////////////////////////////////////////////////////////// + + // COMPILATION LOOP + + ///////////////////////////////////////////////////////////////////////////////////////// + + CompilerKit::EncoderPowerPC asm64; + + while (std::getline(file_ptr, line)) { + if (auto ln = asm64.CheckLine(line, argv[i]); !ln.empty()) { + detail::print_error(ln, argv[i]); + continue; + } + + try { + asm_read_attributes(line); + asm64.WriteLine(line, argv[i]); + } catch (const std::exception &e) { + if (kVerbose) { + std::string what = e.what(); + detail::print_warning("exit because of: " + what, "ppcasm"); + } + + std::filesystem::remove(object_output); + goto asm_fail_exit; + } + } + + if (!kOutputAsBinary) { + if (kVerbose) { + kStdOut << "ppcasm: Writing object file...\n"; + } + + // this is the final step, write everything to the file. + + auto pos = file_ptr_out.tellp(); + + hdr.fCount = kRecords.size() + kUndefinedSymbols.size(); + + file_ptr_out << hdr; + + if (kRecords.empty()) { + kStdErr << "ppcasm: At least one record is needed to write an object " + "file.\nppcasm: Make one using `export .code64 foo_bar`.\n"; + + std::filesystem::remove(object_output); + return -1; + } + + kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + std::size_t record_count = 0UL; + + for (auto &record_hdr : kRecords) { + record_hdr.fFlags |= CompilerKit::kKindRelocationAtRuntime; + record_hdr.fOffset = record_count; + ++record_count; + + file_ptr_out << record_hdr; + + if (kVerbose) + kStdOut << "ppcasm: Wrote record " << record_hdr.fName << "...\n"; + } + + // increment once again, so that we won't lie about the kUndefinedSymbols. + ++record_count; + + for (auto &sym : kUndefinedSymbols) { + CompilerKit::AERecordHeader undefined_sym{0}; + + if (kVerbose) + kStdOut << "ppcasm: Wrote symbol " << sym << " to file...\n"; + + undefined_sym.fKind = kAEInvalidOpcode; + undefined_sym.fSize = sym.size(); + undefined_sym.fOffset = record_count; + + ++record_count; + + memset(undefined_sym.fPad, kAEInvalidOpcode, 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 << "ppcasm: Write raw binary...\n"; + } + } + + // byte from byte, we write this. + for (auto &byte : kBytes) { + file_ptr_out.write(reinterpret_cast(&byte), sizeof(byte)); + } + + if (kVerbose) kStdOut << "ppcasm: Wrote file with program in it.\n"; + + file_ptr_out.flush(); + file_ptr_out.close(); + + if (kVerbose) kStdOut << "ppcasm: Exit succeeded.\n"; + + return 0; + } + +asm_fail_exit: + + if (kVerbose) kStdOut << "ppcasm: Exit failed.\n"; + + return MPCC_EXEC_ERROR; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for attributes +// returns true if any was found. + +///////////////////////////////////////////////////////////////////////////////////////// + +static bool asm_read_attributes(std::string &line) { + // import is the opposite of export, it signals to the li + // that we need this symbol. + if (ParserKit::find_word(line, "import")) { + if (kOutputAsBinary) { + detail::print_error("Invalid import directive in flat binary mode.", + "ppcasm"); + throw std::runtime_error("invalid_import_bin"); + } + + auto name = line.substr(line.find("import") + strlen("import") + 1); + + if (name.size() == 0) { + detail::print_error("Invalid import", "ppcasm"); + throw std::runtime_error("invalid_import"); + } + + std::string result = std::to_string(name.size()); + result += kUndefinedSymbol; + + // mangle this + for (char &j : name) { + if (j == ' ' || j == ',') j = '$'; + } + + result += name; + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + kCurrentRecord.fKind = CompilerKit::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + kCurrentRecord.fKind = CompilerKit::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + kCurrentRecord.fKind = CompilerKit::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that li can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = CompilerKit::kPefCode; + } + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, result.c_str(), result.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + // export is a special keyword used by ppcasm to tell the AE output stage to + // mark this section as a header. it currently supports .code64, .data64., + // .zero64 + else if (ParserKit::find_word(line, "export")) { + if (kOutputAsBinary) { + detail::print_error("Invalid export directive in flat binary mode.", + "ppcasm"); + throw std::runtime_error("invalid_export_bin"); + } + + auto name = line.substr(line.find("export") + strlen("export")); + + std::string name_copy = name; + + for (char &j : name) { + if (j == ' ') j = '$'; + } + + if (name.find(".code64") != std::string::npos) { + // data is treated as code. + + name_copy.erase(name_copy.find(".code64"), strlen(".code64")); + kCurrentRecord.fKind = CompilerKit::kPefCode; + } else if (name.find(".data64") != std::string::npos) { + // no code will be executed from here. + + name_copy.erase(name_copy.find(".data64"), strlen(".data64")); + kCurrentRecord.fKind = CompilerKit::kPefData; + } else if (name.find(".zero64") != std::string::npos) { + // this is a bss section. + + name_copy.erase(name_copy.find(".zero64"), strlen(".zero64")); + kCurrentRecord.fKind = CompilerKit::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that li can find it. + + if (name == kPefStart) { + kCurrentRecord.fKind = CompilerKit::kPefCode; + } + + while (name_copy.find(" ") != std::string::npos) + name_copy.erase(name_copy.find(" "), 1); + + kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); + ++kOrigin; + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, name.c_str(), name.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace detail::algorithm { +// \brief authorize a brief set of characters. +static inline bool is_not_alnum_space(char c) { + return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || + (c == ',') || (c == '(') || (c == ')') || (c == '"') || + (c == '\'') || (c == '[') || (c == ']') || (c == '+') || + (c == '_') || (c == ':') || (c == '@') || (c == '.')); +} + +bool is_valid(const std::string &str) { + return std::find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); +} +} // namespace detail::algorithm + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string CompilerKit::EncoderPowerPC::CheckLine(std::string &line, + const std::string &file) { + std::string err_str; + + if (line.empty() || ParserKit::find_word(line, "import") || + ParserKit::find_word(line, "export") || + line.find('#') != std::string::npos || ParserKit::find_word(line, ";")) { + if (line.find('#') != std::string::npos) { + line.erase(line.find('#')); + } else if (line.find(';') != std::string::npos) { + line.erase(line.find(';')); + } else { + /// does the line contains valid input? + if (!detail::algorithm::is_valid(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + } + } + + return err_str; + } + + if (!detail::algorithm::is_valid(line)) { + err_str = "Line contains non alphanumeric characters.\nhere -> "; + err_str += line; + + return err_str; + } + + // check for a valid instruction format. + + if (line.find(',') != std::string::npos) { + if (line.find(',') + 1 == line.size()) { + err_str += "\nInstruction lacks right register, here -> "; + err_str += line.substr(line.find(',')); + + return err_str; + } else { + bool nothing_on_right = true; + + if (line.find(',') + 1 > line.size()) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + + auto substr = line.substr(line.find(',') + 1); + + for (auto &ch : substr) { + if (ch != ' ' && ch != '\t') { + nothing_on_right = false; + } + } + + // this means we found nothing after that ',' . + if (nothing_on_right) { + err_str += "\nInstruction not complete, here -> "; + err_str += line; + + return err_str; + } + } + } + + // these do take an argument. + std::vector operands_inst = {"stw", "li"}; + + // these don't. + std::vector filter_inst = {"blr", "bl", "sc"}; + + for (auto &opcodePPC : kOpcodesPowerPC) { + if (ParserKit::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 (ParserKit::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 CompilerKit::EncoderPowerPC::WriteNumber(const std::size_t &pos, + std::string &jump_label) { + if (!isdigit(jump_label[pos])) return false; + + switch (jump_label[pos + 1]) { + case 'x': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16); + !res) { + if (errno != 0) { + detail::print_error("invalid hex number: " + jump_label, "ppcasm"); + throw std::runtime_error("invalid_hex"); + } + } + + CompilerKit::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 16)); + + for (char &i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "ppcasm: found a base 16 number here: " + << jump_label.substr(pos) << "\n"; + } + + return true; + } + case 'b': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2); + !res) { + if (errno != 0) { + detail::print_error("invalid binary number: " + jump_label, "ppcasm"); + throw std::runtime_error("invalid_bin"); + } + } + + CompilerKit::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 2)); + + if (kVerbose) { + kStdOut << "ppcasm: found a base 2 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char &i : num.number) { + kBytes.push_back(i); + } + + return true; + } + case 'o': { + if (auto res = strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7); + !res) { + if (errno != 0) { + detail::print_error("invalid octal number: " + jump_label, "ppcasm"); + throw std::runtime_error("invalid_octal"); + } + } + + CompilerKit::NumberCast64 num( + strtol(jump_label.substr(pos + 2).c_str(), nullptr, 7)); + + if (kVerbose) { + kStdOut << "ppcasm: found a base 8 number here: " + << jump_label.substr(pos) << "\n"; + } + + for (char &i : num.number) { + kBytes.push_back(i); + } + + return true; + } + default: { + break; + } + } + + /* check for errno and stuff like that */ + if (auto res = strtol(jump_label.substr(pos).c_str(), nullptr, 10); !res) { + if (errno != 0) { + return false; + } + } + + CompilerKit::NumberCast64 num( + strtol(jump_label.substr(pos).c_str(), nullptr, 10)); + + for (char &i : num.number) { + kBytes.push_back(i); + } + + if (kVerbose) { + kStdOut << "ppcasm: found a base 10 number here: " << jump_label.substr(pos) + << "\n"; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/// @brief Read and write an instruction to the output array. + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CompilerKit::EncoderPowerPC::WriteLine(std::string &line, + const std::string &file) { + if (ParserKit::find_word(line, "export")) return true; + if (!detail::algorithm::is_valid(line)) return true; + + for (auto &opcodePPC : kOpcodesPowerPC) { + // strict check here + if (ParserKit::find_word(line, opcodePPC.name)) { + std::string name(opcodePPC.name); + std::string jump_label, cpy_jump_label; + std::vector 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]; + detail::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) { + detail::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) { + detail::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) { + detail::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) { + detail::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) { + detail::print_error("Too much registers. -> " + line, file); + throw std::runtime_error("too_much_regs"); + } + + if (kVerbose) { + kStdOut << "ppcasm: Found register: " << register_syntax + << "\n"; + kStdOut << "ppcasm: 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) { + detail::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) { + detail::print_error( + "Unrecognized register found.\ntip: each ppcasm register " + "starts with 'r'.\nline: " + + line, + file); + + throw std::runtime_error("not_a_register"); + } + } + + if (found_some_count < 1 && name[0] != 'l' && name[0] != 's') { + detail::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/codetools.files b/codetools.files index 211a290..d895485 100644 --- a/codetools.files +++ b/codetools.files @@ -1,29 +1,29 @@ .clang-format -Comm/AsmKit/AsmKit.hpp -Comm/AsmKit/CPU/32x0.hpp -Comm/AsmKit/CPU/64x0.hpp -Comm/AsmKit/CPU/amd64.hpp -Comm/AsmKit/CPU/arm64.hpp -Comm/AsmKit/CPU/ppc.hpp -Comm/CompilerKit.hpp -Comm/Defines.hpp -Comm/ParserKit.hpp -Comm/Public/SDK/CRT/__mpcc_alloca.hxx -Comm/Public/SDK/CRT/__mpcc_defines.hxx -Comm/Public/SDK/CRT/__mpcc_exception.hxx -Comm/Public/SDK/CRT/__mpcc_hint.hxx -Comm/Public/SDK/CRT/__mpcc_malloc.hxx -Comm/Public/SDK/CRT/__mpcc_power.inc -Comm/StdKit/AE.hpp -Comm/StdKit/ELF.hpp -Comm/StdKit/ErrorID.hpp -Comm/StdKit/ErrorOr.hpp -Comm/StdKit/PEF.hpp -Comm/StdKit/Ref.hpp -Comm/StdKit/String.hpp -Comm/StdKit/XCOFF.hxx -Comm/UUID.hpp -Comm/Version.hxx +Headers/AsmKit/AsmKit.hpp +Headers/AsmKit/CPU/32x0.hpp +Headers/AsmKit/CPU/64x0.hpp +Headers/AsmKit/CPU/amd64.hpp +Headers/AsmKit/CPU/arm64.hpp +Headers/AsmKit/CPU/ppc.hpp +Headers/Macros.hpp +Headers/Defines.hpp +Headers/ParserKit.hpp +Headers/Public/SDK/CRT/__mpcc_alloca.hxx +Headers/Public/SDK/CRT/__mpcc_defines.hxx +Headers/Public/SDK/CRT/__mpcc_exception.hxx +Headers/Public/SDK/CRT/__mpcc_hint.hxx +Headers/Public/SDK/CRT/__mpcc_malloc.hxx +Headers/Public/SDK/CRT/__mpcc_power.inc +Headers/StdKit/AE.hpp +Headers/StdKit/ELF.hpp +Headers/StdKit/ErrorID.hpp +Headers/StdKit/ErrorOr.hpp +Headers/StdKit/PEF.hpp +Headers/StdKit/Ref.hpp +Headers/StdKit/String.hpp +Headers/StdKit/XCOFF.hxx +Headers/UUID.hpp +Headers/Version.hxx Doc/asm-specs.txt Doc/havp.txt Doc/notice.txt @@ -33,29 +33,29 @@ Examples/Example64k.s Examples/ExampleAMD64.asm Examples/ExampleAMD64_Return.asm Examples/ExampleCDialect.S -Examples/ExampleCDialect.cc +Examples/ExampleCDialect.cxx Examples/ExampleCPlusPlus.cxx Examples/ExampleCPlusPlus.s Examples/ExamplePowerPC.S MailMap ReadMe.md -Sources/32asm.cc -Sources/64asm.cc -Sources/64x0-cc.cc -Sources/AssemblyFactory.cc +Sources/32asm.cxx +Sources/64asm.cxx +Sources/64x0-cc.cxx +Sources/AssemblyFactory.cxx Sources/Detail/ReadMe.md Sources/Detail/asmutils.hxx Sources/Detail/compilerutils.hxx -Sources/String.cc -Sources/bpp.cc -Sources/coff2ae.cc +Sources/String.cxx +Sources/bpp.cxx +Sources/coff2ae.cxx Sources/compile_flags.txt -Sources/cplusplus.cc -Sources/elf2ae.cc -Sources/i64asm.cc -Sources/link.cc -Sources/power-cc.cc -Sources/ppcasm.cc +Sources/cplusplus.cxx +Sources/elf2ae.cxx +Sources/i64asm.cxx +Sources/link.cxx +Sources/power-cc.cxx +Sources/ppcasm.cxx posix.make run_format.sh win64.make diff --git a/codetools.includes b/codetools.includes index a9a0fb5..cb0237d 100644 --- a/codetools.includes +++ b/codetools.includes @@ -1,2 +1,2 @@ -./Comm +./Headers ./ diff --git a/posix.make b/posix.make index 8a17a58..9a11054 100644 --- a/posix.make +++ b/posix.make @@ -7,42 +7,42 @@ # ======================================================== # -COMMON_INC=-I./Comm -I./ -I./Sources/Detail +COMMON_INC=-I./Headers -I./ -I./Sources/Detail LINK_CC=clang++ -std=c++20 -LINK_SRC=Sources/link.cc +LINK_SRC=Sources/link.cxx LINK_OUTPUT=Output/link LINK_ALT_OUTPUT=Output/64link LINK_ALT_3_OUTPUT=Output/i64link LINK_ALT_2_OUTPUT=Output/32link LINK_ALT_4_OUTPUT=Output/ppclink -PP_SRC=Sources/bpp.cc +PP_SRC=Sources/bpp.cxx PP_OUTPUT=Output/bpp -SRC_COMMON=Sources/String.cc Sources/AssemblyFactory.cc +SRC_COMMON=Sources/String.cxx Sources/AssemblyFactory.cxx # C++ Compiler (AMD64) -AMD64_CXX_SRC=Sources/cplusplus.cc $(SRC_COMMON) +AMD64_CXX_SRC=Sources/cplusplus.cxx $(SRC_COMMON) AMD64_CXX_OUTPUT=Output/cplusplus # C Compiler (POWER) -64X0_CC_SRC=Sources/64x0-cc.cc $(SRC_COMMON) +64X0_CC_SRC=Sources/64x0-cc.cxx $(SRC_COMMON) 64X0_CC_OUTPUT=Output/64x0-cc # C Compiler (Our own RISC) -PPC_CC_SRC=Sources/power-cc.cc $(SRC_COMMON) +PPC_CC_SRC=Sources/power-cc.cxx $(SRC_COMMON) PPC_CC_OUTPUT=Output/power-cc # 64x0 Assembler (Our Own RISC) -ASM_SRC=Sources/64asm.cc $(SRC_COMMON) +ASM_SRC=Sources/64asm.cxx $(SRC_COMMON) ASM_OUTPUT=Output/64asm # AMD64 Assembler (Intel CISC) -IASM_SRC=Sources/i64asm.cc $(SRC_COMMON) +IASM_SRC=Sources/i64asm.cxx $(SRC_COMMON) IASM_OUTPUT=Output/i64asm # Power4 Assembler (IBM RISC) -PPCASM_SRC=Sources/ppcasm.cc $(SRC_COMMON) +PPCASM_SRC=Sources/ppcasm.cxx $(SRC_COMMON) PPCASM_OUTPUT=Output/ppcasm .PHONY: all diff --git a/win64.make b/win64.make index 37d762a..70d9381 100644 --- a/win64.make +++ b/win64.make @@ -7,41 +7,41 @@ # ======================================================== # -COMMON_INC=-I./Comm -I./ -I./Sources/Detail +COMMON_INC=-I./Headers -I./ -I./Sources/Detail LINK_CC=clang++ -std=c++20 -Xlinker -s -LINK_SRC=Sources/link.cc +LINK_SRC=Sources/link.cxx LINK_OUTPUT=Output/link.exe LINK_ALT_OUTPUT=Output/64link.exe LINK_ALT_3_OUTPUT=Output/i64link.exe LINK_ALT_2_OUTPUT=Output/32link.exe -PP_SRC=Sources/bpp.cc +PP_SRC=Sources/bpp.cxx PP_OUTPUT=Output/bpp.exe -SRC_COMMON=Sources/String.cc Sources/AssemblyFactory.cc +SRC_COMMON=Sources/String.cxx Sources/AssemblyFactory.cxx # C++ Compiler (AMD64) -AMD64_CXX_SRC=Sources/cplusplus.cc $(SRC_COMMON) +AMD64_CXX_SRC=Sources/cplusplus.cxx $(SRC_COMMON) AMD64_CXX_OUTPUT=Output/cplusplus.exe # C Compiler (POWER) -64X0_CC_SRC=Sources/64x0-cc.cc $(SRC_COMMON) +64X0_CC_SRC=Sources/64x0-cc.cxx $(SRC_COMMON) 64X0_CC_OUTPUT=Output/64x0-cc.exe # C Compiler -PPC_CC_SRC=Sources/power-cc.cc $(SRC_COMMON) +PPC_CC_SRC=Sources/power-cc.cxx $(SRC_COMMON) PPC_CC_OUTPUT=Output/power-cc.exe # 64x0 Assembler -ASM_SRC=Sources/64asm.cc $(SRC_COMMON) +ASM_SRC=Sources/64asm.cxx $(SRC_COMMON) ASM_OUTPUT=Output/64asm.exe # AMD64 Assembler -IASM_SRC=Sources/i64asm.cc $(SRC_COMMON) +IASM_SRC=Sources/i64asm.cxx $(SRC_COMMON) IASM_OUTPUT=Output/i64asm.exe # POWER Assembler -PPCASM_SRC=Sources/ppcasm.cc $(SRC_COMMON) +PPCASM_SRC=Sources/ppcasm.cxx $(SRC_COMMON) PPCASM_OUTPUT=Output/ppcasm.exe .PHONY: all -- cgit v1.2.3 From 13b84ff69d821029da3c2318a35a294d8045d7f9 Mon Sep 17 00:00:00 2001 From: Amlal Date: Wed, 17 Jul 2024 18:39:40 +0200 Subject: [IMP] NDK instead of MPCC. Signed-off-by: Amlal --- Examples/ExampleCDialect.S | 2 +- Examples/ExampleCPlusPlus.s | 4 ++-- Headers/Public/SDK/CRT/__mpcc_exception.hxx | 4 ++-- Headers/Public/SDK/CRT/__mpcc_power.inc | 2 +- Headers/StdKit/AE.hpp | 2 +- Sources/cplusplus.cxx | 2 +- posix.make | 2 +- win64.make | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) (limited to 'Examples/ExampleCDialect.S') diff --git a/Examples/ExampleCDialect.S b/Examples/ExampleCDialect.S index 1eb2f82..c657cbb 100644 --- a/Examples/ExampleCDialect.S +++ b/Examples/ExampleCDialect.S @@ -1,5 +1,5 @@ ; Path: Examples/ExampleCDialect.cxx -; Language: MPCC assembly. (Generated from C++) +; Language: NDK assembly. (Generated from C++) ; Date: 2024-5-14 #bits 64 diff --git a/Examples/ExampleCPlusPlus.s b/Examples/ExampleCPlusPlus.s index 0414858..7522eca 100644 --- a/Examples/ExampleCPlusPlus.s +++ b/Examples/ExampleCPlusPlus.s @@ -1,5 +1,5 @@ ; Path: Examples/ExampleCPlusPlus.cxx -; Language: MPCC assembly. (Generated from C++) +; Language: NDK assembly. (Generated from C++) ; Date: 2024-5-14 #bits 64 @@ -15,5 +15,5 @@ mov r10, r8 mov r11, 0 mov r12, 0 -mov rax, 0 +mov rax, 0 ret diff --git a/Headers/Public/SDK/CRT/__mpcc_exception.hxx b/Headers/Public/SDK/CRT/__mpcc_exception.hxx index 4a9f84a..dac42ad 100644 --- a/Headers/Public/SDK/CRT/__mpcc_exception.hxx +++ b/Headers/Public/SDK/CRT/__mpcc_exception.hxx @@ -16,12 +16,12 @@ namespace std { inline void __throw_general(void) { - throw std::runtime_error("MPCC C++ Runtime error."); + throw std::runtime_error("NDK C++ Runtime error."); } inline void __throw_domain_error(const char* error) { - std::cout << "MPCC C++: Domain error: " << error << "\r"; + std::cout << "NDK C++: Domain error: " << error << "\r"; __throw_general(); } } // namespace std diff --git a/Headers/Public/SDK/CRT/__mpcc_power.inc b/Headers/Public/SDK/CRT/__mpcc_power.inc index 9e4928c..90b1364 100644 --- a/Headers/Public/SDK/CRT/__mpcc_power.inc +++ b/Headers/Public/SDK/CRT/__mpcc_power.inc @@ -1,5 +1,5 @@ # Path: SDK/__mpcc_power.inc -# Language: MPCC POWER Assembly support for GNU. +# Language: NDK POWER Assembly support for GNU. # Build Date: 2024-6-4 %ifdef __CODETOOLS__ diff --git a/Headers/StdKit/AE.hpp b/Headers/StdKit/AE.hpp index baa4249..cf64516 100644 --- a/Headers/StdKit/AE.hpp +++ b/Headers/StdKit/AE.hpp @@ -1,7 +1,7 @@ /* * ======================================================== * - * MPCC + * NDK * Copyright ZKA Technologies, all rights reserved. * * ======================================================== diff --git a/Sources/cplusplus.cxx b/Sources/cplusplus.cxx index 3eb6e9b..e1044b9 100644 --- a/Sources/cplusplus.cxx +++ b/Sources/cplusplus.cxx @@ -669,7 +669,7 @@ public: if (dest.empty()) { - dest = "CXX-MPCC-"; + dest = "CXX-NDK-"; std::random_device rd; auto seed_data = std::array {}; diff --git a/posix.make b/posix.make index 9a11054..0b44aed 100644 --- a/posix.make +++ b/posix.make @@ -1,7 +1,7 @@ # # ======================================================== # - # MPCC + # NDK # Copyright ZKA Technologies, all rights reserved. # # ======================================================== diff --git a/win64.make b/win64.make index 70d9381..ff73f42 100644 --- a/win64.make +++ b/win64.make @@ -1,7 +1,7 @@ # # ======================================================== # - # MPCC + # NDK # Copyright ZKA Technologies, all rights reserved. # # ======================================================== -- cgit v1.2.3 From af93068ec107e8d8f5873399e86e4001cb1c6b40 Mon Sep 17 00:00:00 2001 From: Amlal Date: Sun, 21 Jul 2024 09:47:44 +0200 Subject: [RM] Examples/ added to gitignore, improved Ref class. Signed-off-by: Amlal --- .gitignore | 2 ++ Examples/Example64k.s | 3 -- Examples/ExampleAMD64.asm | 6 ---- Examples/ExampleAMD64_Return.asm | 6 ---- Examples/ExampleCDialect.S | 14 --------- Examples/ExampleCDialect.cc | 7 ----- Examples/ExampleCPlusPlus.cxx | 12 -------- Examples/ExampleCPlusPlus.s | 19 ------------- Examples/ExamplePowerPC.S | 8 ------ Examples/ExamplePowerPC.S.pp | 11 -------- Examples/ExamplePowerPC.dmp | 5 ---- Headers/StdKit/Ref.hpp | 26 ++++++++++------- Sources/cplusplus.cxx | 9 +++--- codetools.10x | 48 ------------------------------- codetools.cflags | 1 - codetools.config | 2 -- codetools.creator | 1 - codetools.cxxflags | 1 - codetools.files | 61 ---------------------------------------- codetools.includes | 2 -- 20 files changed, 23 insertions(+), 221 deletions(-) delete mode 100644 Examples/Example64k.s delete mode 100644 Examples/ExampleAMD64.asm delete mode 100644 Examples/ExampleAMD64_Return.asm delete mode 100644 Examples/ExampleCDialect.S delete mode 100644 Examples/ExampleCDialect.cc delete mode 100644 Examples/ExampleCPlusPlus.cxx delete mode 100644 Examples/ExampleCPlusPlus.s delete mode 100644 Examples/ExamplePowerPC.S delete mode 100644 Examples/ExamplePowerPC.S.pp delete mode 100644 Examples/ExamplePowerPC.dmp delete mode 100644 codetools.10x delete mode 100644 codetools.cflags delete mode 100644 codetools.config delete mode 100644 codetools.creator delete mode 100644 codetools.cxxflags delete mode 100644 codetools.files delete mode 100644 codetools.includes (limited to 'Examples/ExampleCDialect.S') diff --git a/.gitignore b/.gitignore index a181dc3..22939a1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ local.properties *.creator.user +Examples/ + # NewOS/MP-UX executable *.exec *.bin diff --git a/Examples/Example64k.s b/Examples/Example64k.s deleted file mode 100644 index b56003a..0000000 --- a/Examples/Example64k.s +++ /dev/null @@ -1,3 +0,0 @@ -mv r0, r3 -lda r19, 0x10000 -jlr diff --git a/Examples/ExampleAMD64.asm b/Examples/ExampleAMD64.asm deleted file mode 100644 index 781934c..0000000 --- a/Examples/ExampleAMD64.asm +++ /dev/null @@ -1,6 +0,0 @@ -#bits 64 - -export .code64 __ImageStart - -mov rax, 17 -ret diff --git a/Examples/ExampleAMD64_Return.asm b/Examples/ExampleAMD64_Return.asm deleted file mode 100644 index 0ecbaf7..0000000 --- a/Examples/ExampleAMD64_Return.asm +++ /dev/null @@ -1,6 +0,0 @@ -#bits 64 - -export .code64 __RaiseInterrupt - -int 13 -ret diff --git a/Examples/ExampleCDialect.S b/Examples/ExampleCDialect.S deleted file mode 100644 index c657cbb..0000000 --- a/Examples/ExampleCDialect.S +++ /dev/null @@ -1,14 +0,0 @@ -; Path: Examples/ExampleCDialect.cxx -; Language: NDK assembly. (Generated from C++) -; Date: 2024-5-14 - -#bits 64 - -#org 0x1000000 - -export .code64 __MPCC_int___ImageStart -mov r8, 0 - -mov r9, 36 -mov rax, r8 -ret diff --git a/Examples/ExampleCDialect.cc b/Examples/ExampleCDialect.cc deleted file mode 100644 index b55dd5f..0000000 --- a/Examples/ExampleCDialect.cc +++ /dev/null @@ -1,7 +0,0 @@ -int __ImageStart(int argc, char const* argv[]) -{ - int *foo; - *foo = 36; - - return *foo; -} diff --git a/Examples/ExampleCPlusPlus.cxx b/Examples/ExampleCPlusPlus.cxx deleted file mode 100644 index 3335e1e..0000000 --- a/Examples/ExampleCPlusPlus.cxx +++ /dev/null @@ -1,12 +0,0 @@ -int main(int argc, char const* argv[]) -{ - { - bool bar = false; - bar = 1; - - bool bar2 = bar; - bar2 = false; - } - - return 0; -} diff --git a/Examples/ExampleCPlusPlus.s b/Examples/ExampleCPlusPlus.s deleted file mode 100644 index 7522eca..0000000 --- a/Examples/ExampleCPlusPlus.s +++ /dev/null @@ -1,19 +0,0 @@ -; Path: Examples/ExampleCPlusPlus.cxx -; Language: NDK assembly. (Generated from C++) -; Date: 2024-5-14 - -#bits 64 - -#org 0x1000000 - -export .code64 __MPCC_int_main -mov r8, 0 -mov r9, 0 - -mov r10, 1 -mov r10, r8 -mov r11, 0 - -mov r12, 0 -mov rax, 0 -ret diff --git a/Examples/ExamplePowerPC.S b/Examples/ExamplePowerPC.S deleted file mode 100644 index b9de796..0000000 --- a/Examples/ExamplePowerPC.S +++ /dev/null @@ -1,8 +0,0 @@ -; you you can never say! - -b 0x1000 -mflr r21 -mtlr r21 -li r3, 0 -cmpw r10, r11 -stw r7, r5+36 diff --git a/Examples/ExamplePowerPC.S.pp b/Examples/ExamplePowerPC.S.pp deleted file mode 100644 index 5eeb07f..0000000 --- a/Examples/ExamplePowerPC.S.pp +++ /dev/null @@ -1,11 +0,0 @@ -# Path: Examples/ExamplePowerPC.S.pp -# Language: POWER Assembly -# Build Date: 2024-6-4 - -bl 0x1000 - - -li r1, 0x500 -mr r2, r1 -sc -blr diff --git a/Examples/ExamplePowerPC.dmp b/Examples/ExamplePowerPC.dmp deleted file mode 100644 index 5986d03..0000000 --- a/Examples/ExamplePowerPC.dmp +++ /dev/null @@ -1,5 +0,0 @@ -0x0000000000000000: 00 10 00 48 b 0x1000 -0x0000000000000004: 00 05 20 38 li r1, 0x500 -0x0000000000000008: 78 13 21 7C or r1, r1, r2 -0x000000000000000c: 02 00 00 44 sc -0x0000000000000010: 20 00 80 4E blr \ No newline at end of file diff --git a/Headers/StdKit/Ref.hpp b/Headers/StdKit/Ref.hpp index e655ccb..0f9c0e3 100644 --- a/Headers/StdKit/Ref.hpp +++ b/Headers/StdKit/Ref.hpp @@ -12,18 +12,25 @@ namespace CompilerKit { - // @author ZKA Technologies - // @brief Reference class, refers to a pointer of data in static memory. + // @author Amlal + // @brief Reference holder class, refers to a pointer of data in static memory. template class Ref final { public: explicit Ref() = default; - ~Ref() = default; + + ~Ref() + { + if (m_Strong) + { + (*m_Class).~T(); + } + } public: explicit Ref(T cls, const bool& strong = false) - : m_Class(cls), m_Strong(strong) + : m_Class(&cls), m_Strong(strong) { } @@ -41,12 +48,12 @@ namespace CompilerKit T& Leak() { - return m_Class; + return *m_Class; } T operator*() { - return m_Class; + return *m_Class; } bool IsStrong() const @@ -56,11 +63,11 @@ namespace CompilerKit operator bool() { - return m_Class; + return *m_Class; } private: - T m_Class; + T* m_Class; bool m_Strong{false}; }; @@ -68,8 +75,7 @@ namespace CompilerKit class NonNullRef final { public: - NonNullRef() = delete; - NonNullRef(nullPtr) = delete; + explicit NonNullRef() = delete; explicit NonNullRef(T* ref) : m_Ref(ref, true) diff --git a/Sources/cplusplus.cxx b/Sources/cplusplus.cxx index e1044b9..5b5b8bc 100644 --- a/Sources/cplusplus.cxx +++ b/Sources/cplusplus.cxx @@ -9,10 +9,13 @@ /// bugs: 0 +#include "StdKit/Ref.hpp" #define __PK_USE_STRUCT_INSTEAD__ 1 #define kPrintF printf +#define kOk (0) + #define kSplashCxx() \ kPrintF(kWhite "%s\n", "Zeta C++ Compiler Driver, (c) 2024 Zeta Electronics, all rights reserved.") @@ -20,8 +23,8 @@ kPrintF(kWhite "%s\n", "Zeta C++ Compiler Driver, (c) 2024 Zeta Electronics, all #include #include -#include #include +#include #include #include #include @@ -30,10 +33,8 @@ kPrintF(kWhite "%s\n", "Zeta C++ Compiler Driver, (c) 2024 Zeta Electronics, all #include #include -#define kOk 0 - /* ZKA Technologies C++ driver */ -/* This is part of ZECC C++ compiler. */ +/* This is part of NDK. */ /* (c) ZKA Technologies */ /// @author Amlal El Mahrouss (amlel) diff --git a/codetools.10x b/codetools.10x deleted file mode 100644 index 2961860..0000000 --- a/codetools.10x +++ /dev/null @@ -1,48 +0,0 @@ - - - - *.c,*.cc,*.cpp,*.c++,*.cp,*.cxx,*.h,*.hh,*.hpp,*.h++,*.hp,*.hxx,*.inl,*.cs,*.rs,*.java,*.jav,*.js,*.jsc,*.jsx,*.json,*.cls,*.py,*.rpy,*.php,*.php3,*.phl,*.phtml,*.rhtml,*.tpl,*.phps,*.lua,*.html,*.html5,*.htm,*.xml,*.xaml,*.css,*.ssi,*.haml,*.yaml,*.bat,*.wbf,*.wbt,*.txt,*.cmake,*.make,*.makefile,*.mak,*.mk,*.sh,*.bash,*.csv,*.asp,*.pl,*.mac,*.ws,*.vbs,*.perl,*.src,*.rss,*.inc,*.f,*.go,*.prl,*.plx,*.rb,*.lsp,*.lpx,*.ps1,*.command,*.cbl,*.cob,*.qs,*.wxs,*.ph,*.msc,*.glsl,*.hlsl,*.fx,*.vert,*.tesc,*.tese,*.geom,*.frag,*.comp,*.pssl,*.scons,*.cu,*.jai, - - true - true - true - false - false - false - - - - - - - - - - - - - false - - Debug - Release - - - x64 - Win32 - - - C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\include - C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.40.33807\ATLMFC\include - C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\VS\include - C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt - C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\um - C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\shared - C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\winrt - C:\Program Files (x86)\Windows Kits\10\\include\10.0.22621.0\\cppwinrt - C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um - - - - - - diff --git a/codetools.cflags b/codetools.cflags deleted file mode 100644 index 68d5165..0000000 --- a/codetools.cflags +++ /dev/null @@ -1 +0,0 @@ --std=c17 \ No newline at end of file diff --git a/codetools.config b/codetools.config deleted file mode 100644 index e0284f4..0000000 --- a/codetools.config +++ /dev/null @@ -1,2 +0,0 @@ -// Add predefined macros for your project here. For example: -// #define THE_ANSWER 42 diff --git a/codetools.creator b/codetools.creator deleted file mode 100644 index e94cbbd..0000000 --- a/codetools.creator +++ /dev/null @@ -1 +0,0 @@ -[General] diff --git a/codetools.cxxflags b/codetools.cxxflags deleted file mode 100644 index e23b2ae..0000000 --- a/codetools.cxxflags +++ /dev/null @@ -1 +0,0 @@ --std=c++20 diff --git a/codetools.files b/codetools.files deleted file mode 100644 index d895485..0000000 --- a/codetools.files +++ /dev/null @@ -1,61 +0,0 @@ -.clang-format -Headers/AsmKit/AsmKit.hpp -Headers/AsmKit/CPU/32x0.hpp -Headers/AsmKit/CPU/64x0.hpp -Headers/AsmKit/CPU/amd64.hpp -Headers/AsmKit/CPU/arm64.hpp -Headers/AsmKit/CPU/ppc.hpp -Headers/Macros.hpp -Headers/Defines.hpp -Headers/ParserKit.hpp -Headers/Public/SDK/CRT/__mpcc_alloca.hxx -Headers/Public/SDK/CRT/__mpcc_defines.hxx -Headers/Public/SDK/CRT/__mpcc_exception.hxx -Headers/Public/SDK/CRT/__mpcc_hint.hxx -Headers/Public/SDK/CRT/__mpcc_malloc.hxx -Headers/Public/SDK/CRT/__mpcc_power.inc -Headers/StdKit/AE.hpp -Headers/StdKit/ELF.hpp -Headers/StdKit/ErrorID.hpp -Headers/StdKit/ErrorOr.hpp -Headers/StdKit/PEF.hpp -Headers/StdKit/Ref.hpp -Headers/StdKit/String.hpp -Headers/StdKit/XCOFF.hxx -Headers/UUID.hpp -Headers/Version.hxx -Doc/asm-specs.txt -Doc/havp.txt -Doc/notice.txt -Doc/vnrp.txt -Doxyfile -Examples/Example64k.s -Examples/ExampleAMD64.asm -Examples/ExampleAMD64_Return.asm -Examples/ExampleCDialect.S -Examples/ExampleCDialect.cxx -Examples/ExampleCPlusPlus.cxx -Examples/ExampleCPlusPlus.s -Examples/ExamplePowerPC.S -MailMap -ReadMe.md -Sources/32asm.cxx -Sources/64asm.cxx -Sources/64x0-cc.cxx -Sources/AssemblyFactory.cxx -Sources/Detail/ReadMe.md -Sources/Detail/asmutils.hxx -Sources/Detail/compilerutils.hxx -Sources/String.cxx -Sources/bpp.cxx -Sources/coff2ae.cxx -Sources/compile_flags.txt -Sources/cplusplus.cxx -Sources/elf2ae.cxx -Sources/i64asm.cxx -Sources/link.cxx -Sources/power-cc.cxx -Sources/ppcasm.cxx -posix.make -run_format.sh -win64.make diff --git a/codetools.includes b/codetools.includes deleted file mode 100644 index cb0237d..0000000 --- a/codetools.includes +++ /dev/null @@ -1,2 +0,0 @@ -./Headers -./ -- cgit v1.2.3