diff options
| author | Amlal El Mahrouss <amlal.elmahrouss@icloud.com> | 2024-01-27 22:08:08 +0100 |
|---|---|---|
| committer | Amlal El Mahrouss <amlal.elmahrouss@icloud.com> | 2024-01-27 22:08:08 +0100 |
| commit | 5b49ce381c840a14b2e5a493d471bd2e378e5db6 (patch) | |
| tree | 8478b62244d24e8507a08733bf65919b59e4ceb1 | |
| parent | f37be20a2ac03d78faa30c1d1adcdd0dbd1f473e (diff) | |
Toolchain: Work in progress AMD64 support.
- We use the Mahrouss Logic x86 Standard:
- Register is prefixed with 'r' and hols it's id.
- Register Dest first, Source second, example: mov rd, rs
- #code_<isa>_<features>, which will let select what
instructions to use.
Signed-off-by: Amlal El Mahrouss <amlal.elmahrouss@icloud.com>
| -rw-r--r-- | Private/CompilerKit/AsmKit/Arch/32x0.hpp | 2 | ||||
| -rw-r--r-- | Private/CompilerKit/AsmKit/Arch/64x0.hpp | 50 | ||||
| -rw-r--r-- | Private/CompilerKit/AsmKit/Arch/amd64.hpp | 69 | ||||
| -rw-r--r-- | Private/CompilerKit/AsmKit/AsmKit.hpp | 18 | ||||
| l--------- | Private/External/AE.h (renamed from Private/External/ObjFormat.h) | 0 | ||||
| -rw-r--r-- | Private/Toolchain/.gitignore | 1 | ||||
| -rw-r--r-- | Private/Toolchain/64asm.cc | 25 | ||||
| -rw-r--r-- | Private/Toolchain/bin/Source/hello_amd64.masm | 510 | ||||
| -rw-r--r-- | Private/Toolchain/i64asm.cc | 875 | ||||
| -rw-r--r-- | Private/Toolchain/makefile | 4 |
10 files changed, 1517 insertions, 37 deletions
diff --git a/Private/CompilerKit/AsmKit/Arch/32x0.hpp b/Private/CompilerKit/AsmKit/Arch/32x0.hpp index 28bf754..f9be82b 100644 --- a/Private/CompilerKit/AsmKit/Arch/32x0.hpp +++ b/Private/CompilerKit/AsmKit/Arch/32x0.hpp @@ -31,7 +31,7 @@ struct CpuCode32x0 { - const char fName[16]; + const char fName[32]; char fOpcode; char fSize; char fFunct3; diff --git a/Private/CompilerKit/AsmKit/Arch/64x0.hpp b/Private/CompilerKit/AsmKit/Arch/64x0.hpp index fde40fa..3052021 100644 --- a/Private/CompilerKit/AsmKit/Arch/64x0.hpp +++ b/Private/CompilerKit/AsmKit/Arch/64x0.hpp @@ -30,37 +30,37 @@ typedef uint8_t e64k_num_t; struct CpuCode64x0 { - const e64k_character_t fName[16]; + const e64k_character_t fName[32]; e64k_num_t fOpcode; e64k_num_t fFunct3; e64k_num_t fFunct7; }; inline std::vector<CpuCode64x0> kOpcodes64x0 = { - kAsmOpcodeDecl("nop", 0b0000000, 0b0000000, kAsmNoArgs) // no-operation. - kAsmOpcodeDecl("np", 0b0000000, 0b0000000, kAsmNoArgs) // no-operation. - kAsmOpcodeDecl("jlr", 0b1110011, 0b0000111, kAsmJump) // jump to linked return register - kAsmOpcodeDecl("jrl", 0b1110011, 0b0001111, kAsmJump) // jump from return register. - kAsmOpcodeDecl("mv", 0b0100011, 0b101, kAsmRegToReg) - kAsmOpcodeDecl("bg", 0b1100111, 0b111, kAsmRegToReg) - kAsmOpcodeDecl("bl", 0b1100111, 0b011, kAsmRegToReg) - kAsmOpcodeDecl("beq", 0b1100111, 0b000, kAsmRegToReg) - kAsmOpcodeDecl("bne", 0b1100111, 0b001, kAsmRegToReg) - kAsmOpcodeDecl("bge", 0b1100111, 0b101, kAsmRegToReg) - kAsmOpcodeDecl("ble", 0b1100111, 0b100, kAsmRegToReg) - kAsmOpcodeDecl("stw", 0b0001111, 0b100, kAsmImmediate) - kAsmOpcodeDecl("ldw", 0b0001111, 0b100, kAsmImmediate) - kAsmOpcodeDecl("lda", 0b0001111, 0b101, kAsmImmediate) - kAsmOpcodeDecl("sta", 0b0001111, 0b001, kAsmImmediate) - // add/sub without carry flag - kAsmOpcodeDecl("add", 0b0101011, 0b100, kAsmImmediate) - kAsmOpcodeDecl("dec", 0b0101011, 0b101, kAsmImmediate) - // add/sub with carry flag - kAsmOpcodeDecl("addc", 0b0101011, 0b110, kAsmImmediate) - kAsmOpcodeDecl("decc", 0b0101011, 0b111, kAsmImmediate) - kAsmOpcodeDecl("int", 0b1110011, 0b00, kAsmSyscall) - kAsmOpcodeDecl("pha", 0b1110011, 0b00, kAsmNoArgs) - kAsmOpcodeDecl("pla", 0b1110011, 0b01, kAsmNoArgs) + kAsmOpcodeDecl("nop", 0b0000000, 0b0000000, kAsmNoArgs) // no-operation. + kAsmOpcodeDecl("np", 0b0000000, 0b0000000, kAsmNoArgs) // no-operation. + kAsmOpcodeDecl("jlr", 0b1110011, 0b0000111, kAsmJump) // jump to linked return register + kAsmOpcodeDecl("jrl", 0b1110011, 0b0001111, kAsmJump) // jump from return register. + kAsmOpcodeDecl("mv", 0b0100011, 0b101, kAsmRegToReg) + kAsmOpcodeDecl("bg", 0b1100111, 0b111, kAsmRegToReg) + kAsmOpcodeDecl("bl", 0b1100111, 0b011, kAsmRegToReg) + kAsmOpcodeDecl("beq", 0b1100111, 0b000, kAsmRegToReg) + kAsmOpcodeDecl("bne", 0b1100111, 0b001, kAsmRegToReg) + kAsmOpcodeDecl("bge", 0b1100111, 0b101, kAsmRegToReg) + kAsmOpcodeDecl("ble", 0b1100111, 0b100, kAsmRegToReg) + kAsmOpcodeDecl("stw", 0b0001111, 0b100, kAsmImmediate) + kAsmOpcodeDecl("ldw", 0b0001111, 0b100, kAsmImmediate) + kAsmOpcodeDecl("lda", 0b0001111, 0b101, kAsmImmediate) + kAsmOpcodeDecl("sta", 0b0001111, 0b001, kAsmImmediate) + // add/sub without carry flag + kAsmOpcodeDecl("add", 0b0101011, 0b100, kAsmImmediate) + kAsmOpcodeDecl("dec", 0b0101011, 0b101, kAsmImmediate) + // add/sub with carry flag + kAsmOpcodeDecl("addc", 0b0101011, 0b110, kAsmImmediate) + kAsmOpcodeDecl("decc", 0b0101011, 0b111, kAsmImmediate) + kAsmOpcodeDecl("int", 0b1110011, 0b00, kAsmSyscall) + kAsmOpcodeDecl("pha", 0b1110011, 0b00, kAsmNoArgs) + kAsmOpcodeDecl("pla", 0b1110011, 0b01, kAsmNoArgs) }; // \brief 64x0 register prefix diff --git a/Private/CompilerKit/AsmKit/Arch/amd64.hpp b/Private/CompilerKit/AsmKit/Arch/amd64.hpp new file mode 100644 index 0000000..78007b1 --- /dev/null +++ b/Private/CompilerKit/AsmKit/Arch/amd64.hpp @@ -0,0 +1,69 @@ +/* + * ======================================================== + * + * MPCC + * Copyright 2024, Mahrouss Logic, all rights reserved. + * + * ======================================================== + */ + +#pragma once + +#include <CompilerKit/Defines.hpp> + +// @brief 64x0 support. +// @file Arch/64x0.hpp + +#define kAsmOpcodeDecl(__NAME, __OPCODE) \ + { .fName = __NAME, .fOpcode = __OPCODE }, + + + + +typedef char e64k_character_t; +typedef uint8_t e64_byte_t; +typedef uint16_t e64_hword_t; +typedef uint32_t e64k_word_t; + +struct CpuCodeAMD64 +{ + std::string fName; + e64_byte_t fPrefixBytes[4]; + e64_hword_t fOpcode; + e64_hword_t fModReg; + e64k_word_t fDisplacment; + e64k_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<CpuCodeAMD64> kOpcodesAMD64 = { + kAsmOpcodeDecl("int", kAsmIntOpcode) + kAsmOpcodeDecl("int", kasmIntOpcodeAlt) + kAsmOpcodeDecl("into", 0xCE) + kAsmOpcodeDecl("iret", 0xCF) +}; + +// \brief 64x0 register prefix +// example: r32, r0 +// r32 -> sp +// r0 -> hw zero + +#define kAsmFloatZeroRegister -1 +#define kAsmZeroRegister -1 + +#define kAsmRegisterPrefix "r" +#define kAsmRegisterLimit 16 +#define kAsmPcRegister 8 +#define kAsmCrRegister -1 +#define kAsmSpRegister 9 + +/* return address register */ +#define kAsmRetRegister 0 diff --git a/Private/CompilerKit/AsmKit/AsmKit.hpp b/Private/CompilerKit/AsmKit/AsmKit.hpp index a159c05..e85b5d6 100644 --- a/Private/CompilerKit/AsmKit/AsmKit.hpp +++ b/Private/CompilerKit/AsmKit/AsmKit.hpp @@ -75,6 +75,24 @@ namespace CompilerKit }; +#ifdef __ASM_NEED_AMD64__ + + class PlatformAssemblerAMD64 final : public PlatformAssembler + { + public: + explicit PlatformAssemblerAMD64() = default; + ~PlatformAssemblerAMD64() = default; + + CXXKIT_COPY_DEFAULT(PlatformAssemblerAMD64); + + 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_AMD64__ + #ifdef __ASM_NEED_64x0__ class PlatformAssembler64x0 final : public PlatformAssembler diff --git a/Private/External/ObjFormat.h b/Private/External/AE.h index 6781ba9..6781ba9 120000 --- a/Private/External/ObjFormat.h +++ b/Private/External/AE.h diff --git a/Private/Toolchain/.gitignore b/Private/Toolchain/.gitignore index 323cdad..9e925a1 100644 --- a/Private/Toolchain/.gitignore +++ b/Private/Toolchain/.gitignore @@ -11,6 +11,7 @@ bin/mkcdfs bin/ccplus bin/cppfront bin/bccl +bin/i64asm bin/Assembly.* diff --git a/Private/Toolchain/64asm.cc b/Private/Toolchain/64asm.cc index 4b9e25f..3bd0eda 100644 --- a/Private/Toolchain/64asm.cc +++ b/Private/Toolchain/64asm.cc @@ -307,13 +307,16 @@ MPCC_MODULE(MPUXAssembler64000) } // byte from byte, we write this. - for (auto &byte : kBytes) + for (auto& byte : kBytes) { - file_ptr_out.write(reinterpret_cast<const char *>(&byte), sizeof(byte)); + for (size_t i = 0; i < sizeof(byte); i++) + { + file_ptr_out << reinterpret_cast<const char*>(&byte)[i]; + } } if (kVerbose) - kStdOut << "64asm: Wrote file with program in it."; + kStdOut << "64asm: Wrote file with program in it.\n"; file_ptr_out.flush(); file_ptr_out.close(); @@ -347,7 +350,7 @@ static bool asm_read_attributes(std::string &line) { if (kOutputAsBinary) { - detail::print_error("invalid import directive in flat binary mode.", "64asm"); + detail::print_error("Invalid import directive in flat binary mode.", "64asm"); throw std::runtime_error("invalid_import_bin"); } @@ -412,7 +415,7 @@ static bool asm_read_attributes(std::string &line) { if (kOutputAsBinary) { - detail::print_error("invalid export directive in flat binary mode.", "64asm"); + detail::print_error("Invalid export directive in flat binary mode.", "64asm"); throw std::runtime_error("invalid_export_bin"); } @@ -550,7 +553,7 @@ std::string CompilerKit::PlatformAssembler64x0::CheckLine(std::string &line, con { if (line.find(',') + 1 == line.size()) { - err_str += "\ninstruction lacks right register, here -> "; + err_str += "\nInstruction lacks right register, here -> "; err_str += line.substr(line.find(',')); return err_str; @@ -561,7 +564,7 @@ std::string CompilerKit::PlatformAssembler64x0::CheckLine(std::string &line, con if (line.find(',') + 1 > line.size()) { - err_str += "\ninstruction not complete, here -> "; + err_str += "\nInstruction not complete, here -> "; err_str += line; return err_str; @@ -581,7 +584,7 @@ std::string CompilerKit::PlatformAssembler64x0::CheckLine(std::string &line, con // this means we found nothing after that ',' . if (nothing_on_right) { - err_str += "\ninstruction not complete, here -> "; + err_str += "\nInstruction not complete, here -> "; err_str += line; return err_str; @@ -607,7 +610,7 @@ std::string CompilerKit::PlatformAssembler64x0::CheckLine(std::string &line, con // if only the instruction was found. if (line == op) { - err_str += "\nmalformed "; + err_str += "\nMalformed "; err_str += op; err_str += " instruction, here -> "; err_str += line; @@ -622,7 +625,7 @@ std::string CompilerKit::PlatformAssembler64x0::CheckLine(std::string &line, con { if (!isspace(line[line.find(opcode64x0.fName) + strlen(opcode64x0.fName)])) { - err_str += "\nmissing space between "; + err_str += "\nMissing space between "; err_str += opcode64x0.fName; err_str += " and operands.\nhere -> "; err_str += line; @@ -634,7 +637,7 @@ std::string CompilerKit::PlatformAssembler64x0::CheckLine(std::string &line, con } } - err_str += "unrecognized instruction and operands: " + line; + err_str += "Unrecognized instruction and operands: " + line; return err_str; } diff --git a/Private/Toolchain/bin/Source/hello_amd64.masm b/Private/Toolchain/bin/Source/hello_amd64.masm new file mode 100644 index 0000000..4d3c349 --- /dev/null +++ b/Private/Toolchain/bin/Source/hello_amd64.masm @@ -0,0 +1,510 @@ +org 0x7c00 + +nop + +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 +db 0x0 + +db 0xAA +db 0x55
\ No newline at end of file diff --git a/Private/Toolchain/i64asm.cc b/Private/Toolchain/i64asm.cc new file mode 100644 index 0000000..039cbfc --- /dev/null +++ b/Private/Toolchain/i64asm.cc @@ -0,0 +1,875 @@ +/* + * ======================================================== + * + * i64asm + * Copyright 2024, Mahrouss Logic, all rights reserved. + * + * ======================================================== + */ + +/// bugs: 0 + +///////////////////////////////////////////////////////////////////////////////////////// + +// @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. + +///////////////////////////////////////////////////////////////////////////////////////// + +#define __ASM_NEED_AMD64__ 1 + +#include <CompilerKit/AsmKit/Arch/amd64.hpp> +#include <CompilerKit/ParserKit.hpp> +#include <CompilerKit/StdKit/PEF.hpp> +#include <CompilerKit/StdKit/AE.hpp> +#include <filesystem> +#include <iostream> +#include <fstream> + +///////////////////// + +// 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; + +static std::size_t kCounter = 1UL; + +static std::uintptr_t kOrigin = kPefBaseOrigin; +static std::vector<std::pair<std::string, std::uintptr_t>> kOriginLabel; + +static bool kVerbose = false; + +static std::vector<e64_byte_t> kBytes; + +static CompilerKit::AERecordHeader kCurrentRecord{ + .fName = "", + .fKind = CompilerKit::kPefCode, + .fSize = 0, + .fOffset = 0}; + +static std::vector<CompilerKit::AERecordHeader> kRecords; +static std::vector<std::string> kUndefinedSymbols; + +static const std::string kUndefinedSymbol = ":ld:"; +static const std::string kRelocSymbol = ":mld:"; + +// \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; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief AMD64 assembler entrypoint, the program/module starts here. + +///////////////////////////////////////////////////////////////////////////////////////// + +MPCC_MODULE(MPUXAssemblerAMD64) +{ + //////////////// 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 (e64_hword_t i = 0; i < kJumpLimit; i++) + { + CpuCodeAMD64 code{.fName = opcodes_jump[i], .fOpcode = static_cast<e64_hword_t>(kAsmJumpOpcode + i)}; + kOpcodesAMD64.push_back(code); + } + + CpuCodeAMD64 code{.fName = "jcxz", .fOpcode = 0xE3}; + kOpcodesAMD64.push_back(code); + + for (e64_hword_t i = kJumpLimitStandard; i < kJumpLimitStandardLimit; i++) + { + CpuCodeAMD64 code{.fName = "jmp", .fOpcode = i}; + kOpcodesAMD64.push_back(code); + } + + CpuCodeAMD64 lahf{.fName = "lahf", .fOpcode = 0x9F}; + kOpcodesAMD64.push_back(lahf); + + CpuCodeAMD64 lds{.fName = "lds", .fOpcode = 0xC5}; + kOpcodesAMD64.push_back(lds); + + CpuCodeAMD64 lea{.fName = "lea", .fOpcode = 0x8D}; + kOpcodesAMD64.push_back(lea); + + for (e64_hword_t i = 0xA0; i < 0xA3; i++) + { + CpuCodeAMD64 mov{.fName = "mov", .fOpcode = i}; + kOpcodesAMD64.push_back(mov); + } + + CpuCodeAMD64 mov{.fName = "nop", .fOpcode = 0x90}; + kOpcodesAMD64.push_back(mov); + + //////////////// 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.\ni64asm: v1.10\ni64asm: Copyright (c) 2024 Mahrouss Logic.\n"; + return 0; + } + else if (strcmp(argv[i], "-h") == 0) + { + kStdOut << "i64asm: AMD64 Assembler.\ni64asm: 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 << "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 += 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::PlatformAssemblerAMD64 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"); + } + + std::filesystem::remove(object_output); + 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 .text 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 << "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 = kBytes.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 : kBytes) + { + for (size_t i = 0; i < sizeof(byte); i++) + { + file_ptr_out << reinterpret_cast<const char *>(&byte)[i]; + } + } + + 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 import directive in flat binary mode.", "i64asm"); + throw std::runtime_error("invalid_import_bin"); + } + + auto name = line.substr(line.find("import ") + strlen("import ")); + + std::string result = std::to_string(name.size()); + result += kUndefinedSymbol; + + // mangle this + for (char &j : name) + { + if (j == ' ' || + j == ',') + j = '$'; + } + + result += name; + + if (name.find(".text") != std::string::npos) + { + // data is treated as code. + kCurrentRecord.fKind = CompilerKit::kPefCode; + } + else if (name.find(".data") != std::string::npos) + { + // no code will be executed from here. + kCurrentRecord.fKind = CompilerKit::kPefData; + } + else if (name.find(".page_zero") != std::string::npos) + { + // this is a bss section. + kCurrentRecord.fKind = CompilerKit::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == "__start") + { + 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 i64asm to tell the AE output stage to mark this section as a header. + // it currently supports .text, .data., page_zero + else if (ParserKit::find_word(line, "export ")) + { + if (kOutputAsBinary) + { + detail::print_error("invalid export directive in flat binary mode.", "i64asm"); + 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(".text") != std::string::npos) + { + // data is treated as code. + + name_copy.erase(name_copy.find(".text"), strlen(".text")); + kCurrentRecord.fKind = CompilerKit::kPefCode; + } + else if (name.find(".data") != std::string::npos) + { + // no code will be executed from here. + + name_copy.erase(name_copy.find(".data"), strlen(".data")); + kCurrentRecord.fKind = CompilerKit::kPefData; + } + else if (name.find(".page_zero") != std::string::npos) + { + // this is a bss section. + + name_copy.erase(name_copy.find(".page_zero"), strlen(".page_zero")); + kCurrentRecord.fKind = CompilerKit::kPefZero; + } + + // this is a special case for the start stub. + // we want this so that ld can find it. + + if (name == "__start") + { + kCurrentRecord.fKind = CompilerKit::kPefCode; + } + + while (name_copy.find(" ") != std::string::npos) + name_copy.erase(name_copy.find(" "), 1); + + kOriginLabel.push_back(std::make_pair(name_copy, kOrigin)); + ++kOrigin; + + // now we can tell the code size of the previous kCurrentRecord. + + if (!kRecords.empty()) + kRecords[kRecords.size() - 1].fSize = kBytes.size(); + + memset(kCurrentRecord.fName, 0, kAESymbolLen); + memcpy(kCurrentRecord.fName, name.c_str(), name.size()); + + ++kCounter; + + memset(kCurrentRecord.fPad, kAEInvalidOpcode, kAEPad); + + kRecords.emplace_back(kCurrentRecord); + + return true; + } + + return false; +} + +// \brief algorithms and helpers. + +namespace detail::algorithm +{ + // \brief authorize a brief set of characters. + static inline bool is_not_alnum_space(char c) + { + return !(isalpha(c) || isdigit(c) || (c == ' ') || (c == '\t') || (c == ',') || + (c == '(') || (c == ')') || (c == '"') || (c == '\'') || (c == '[') || (c == ']') || (c == '+') || (c == '_') || (c == ':') || (c == '@') || (c == '.')); + } + + bool is_valid(const std::string &str) + { + return find_if(str.begin(), str.end(), is_not_alnum_space) == str.end(); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// @brief Check for line (syntax check) + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string CompilerKit::PlatformAssemblerAMD64::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, "#") || + 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; + } + } + } + + return err_str; +} + +bool CompilerKit::PlatformAssemblerAMD64::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 = strtoq(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::NumberCast num(strtoq(jump_label.substr(pos + 2).c_str(), + nullptr, 16)); + + for (char &i : num.number) + { + kBytes.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 = strtoq(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::NumberCast num(strtoq(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) + { + kBytes.push_back(i); + } + + return true; + } + case 'o': + { + if (auto res = strtoq(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::NumberCast num(strtoq(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) + { + kBytes.push_back(i); + } + + return true; + } + default: + { + break; + } + } + + /* check for errno and stuff like that */ + if (auto res = strtoq(jump_label.substr(pos).c_str(), + nullptr, 10); + !res) + { + if (errno != 0) + { + return false; + } + } + + CompilerKit::NumberCast num(strtoq(jump_label.substr(pos).c_str(), + nullptr, 10)); + + for (char &i : num.number) + { + kBytes.push_back(i); + } + + 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::PlatformAssemblerAMD64::WriteLine(std::string &line, const std::string &file) +{ + if (ParserKit::find_word(line, "export ")) + return true; + + for (auto &opcodeAMD64 : kOpcodesAMD64) + { + // strict check here + if (ParserKit::find_word(line, opcodeAMD64.fName) && + detail::algorithm::is_valid(line)) + { + std::string name(opcodeAMD64.fName); + + kBytes.emplace_back(opcodeAMD64.fOpcode); + + if (name == "mov") + { + 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 = strtoq( + 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"; + } + } + } + } + } + } + + if (line.find("db") != std::string::npos) + { + this->WriteNumber(line.find("db") + strlen("db") + 1, line); + } + 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 << "Origin: " << kOrigin << std::endl; + } + } + } + } + } + + return true; +} + +// Last rev 13-1-24
\ No newline at end of file diff --git a/Private/Toolchain/makefile b/Private/Toolchain/makefile index e04a40b..8cbd608 100644 --- a/Private/Toolchain/makefile +++ b/Private/Toolchain/makefile @@ -31,6 +31,9 @@ CXX_OUTPUT=bin/ccplus ASM_SRC=64asm.cc ../CompilerKit/StdKit/*.cc ../CompilerKit/AsmKit/*.cc ASM_OUTPUT=bin/64asm +IASM_SRC=i64asm.cc ../CompilerKit/StdKit/*.cc ../CompilerKit/AsmKit/*.cc +IASM_OUTPUT=bin/i64asm + .PHONY: all all: cl @echo "[make] done build." @@ -40,6 +43,7 @@ cl: ld $(LINK_CC) $(COMMON_INC) $(PP_SRC) -o $(PP_OUTPUT) $(LINK_CC) $(COMMON_INC) $(CC_SRC) -o $(CC_OUTPUT) $(LINK_CC) $(COMMON_INC) $(CXX_SRC) -o $(CXX_OUTPUT) + $(LINK_CC) $(COMMON_INC) $(IASM_SRC) -o $(IASM_OUTPUT) $(LINK_CC) $(COMMON_INC) $(ASM_SRC) -o $(ASM_OUTPUT) .PHONY: ld |
