diff --git a/core/chip32/chip32_assembler.cpp b/core/chip32/chip32_assembler.cpp index f2826a0..7794d67 100644 --- a/core/chip32/chip32_assembler.cpp +++ b/core/chip32/chip32_assembler.cpp @@ -54,7 +54,7 @@ static const uint32_t NbRegs = sizeof(AllRegs) / sizeof(AllRegs[0]); // Keep same order than the opcodes list!! static const std::string Mnemonics[] = { - "nop", "halt", "syscall", "lcons", "mov", "push", "pop", "store", "load", "add", "sub", "mul", "div", + "nop", "halt", "syscall", "lcons", "mov", "push", "pop", "store", "load", "add", "addi", "sub", "subi", "mul", "div", "shiftl", "shiftr", "ishiftr", "and", "or", "xor", "not", "call", "ret", "jump", "jumpr", "skipz", "skipnz", "eq", "gt", "lt" }; @@ -104,38 +104,8 @@ static inline void leu16_put(std::vector &container, uint16_t data m_lastError.message = error; \ return false; } \ -std::vector Split(std::string line) -{ - std::vector result; - std::istringstream iss(line); - std::string token; - while (std::getline(iss, token, ' ')) { - // Vérifier si le jeton contient une virgule - size_t comma_pos = token.find(","); - if (comma_pos != std::string::npos) { - // Diviser le jeton en deux parties séparées par la virgule - std::string first_token = token.substr(0, comma_pos); - std::string second_token = token.substr(comma_pos + 1); - // Ajouter chaque partie au vecteur de résultats - if (!first_token.empty()) { - result.push_back(first_token); - } - if (!second_token.empty()) { - result.push_back(second_token); - } - } else { - // Ajouter le jeton entier au vecteur de résultats - if (!token.empty()) { - result.push_back(token); - } - } - } - - return result; -} - -uint32_t convertStringToLong(const std::string& str) { +static uint32_t convertStringToLong(const std::string& str) { char* end; if (str.compare(0, 2, "0x") == 0 || str.compare(0, 2, "0X") == 0) { return static_cast(strtol(str.c_str(), &end, 16)); @@ -163,6 +133,38 @@ bool Assembler::GetRegister(const std::string ®Name, uint8_t ®) return false; } +std::vector Assembler::Split(const std::string &line) +{ + std::vector result; + std::string current; + bool inQuotes = false; + + for (char c : line) { + if (c == '"') { + // Si on rencontre un guillemet, on change l'état + inQuotes = !inQuotes; + current += c; + } + else if ((c == ' ' || c == ',') && !inQuotes) { + // Si on rencontre un espace ou une virgule en dehors des guillemets + if (!current.empty()) { + result.push_back(current); + current.clear(); + } + } else { + // Sinon, on ajoute le caractère au "current" + current += c; + } + } + + // Ajout du dernier morceau, s'il existe + if (!current.empty()) { + result.push_back(current); + } + + return result; +} + bool Assembler::GetRegisterName(uint8_t reg, std::string ®Name) { for (uint32_t i = 0; i < NbRegs; i++) @@ -227,6 +229,19 @@ bool Assembler::CompileMnemonicArguments(Instr &instr) instr.compiledArgs.push_back(ra); instr.compiledArgs.push_back(rb); break; + case OP_ADDI: + case OP_SUBI: + { + GET_REG(instr.args[0], ra); + instr.compiledArgs.push_back(ra); + + uint32_t op = convertStringToLong(instr.args[1]); + if (op > 255) { + return false; + } + leu32_put(instr.compiledArgs, op); + break; + } case OP_JUMP: // Reserve 2 bytes for address, it will be filled at the end instr.useLabel = true; @@ -243,14 +258,37 @@ bool Assembler::CompileMnemonicArguments(Instr &instr) instr.compiledArgs.push_back(static_cast(strtol(instr.args[2].c_str(), NULL, 0))); break; case OP_LOAD: - CHIP32_CHECK(instr, instr.args[1].at(0) == '@', "Missing @ sign before register") - instr.args[1].erase(0, 1); - GET_REG(instr.args[0], ra); - GET_REG(instr.args[1], rb); - instr.compiledArgs.push_back(ra); - instr.compiledArgs.push_back(rb); - instr.compiledArgs.push_back(static_cast(strtol(instr.args[2].c_str(), NULL, 0))); + { + // We allow two forms of writing: + // - load r0, @R1, 4 ; the address is located in a register + // - load r0, $variable, 4 ; we use the variable address to get the value + // + char prefix = instr.args[1].at(0); + + // Register based + if (prefix == '@') + { + + instr.args[1].erase(0, 1); + GET_REG(instr.args[0], ra); + GET_REG(instr.args[1], rb); + instr.compiledArgs.push_back(ra); + instr.compiledArgs.push_back(rb); + instr.compiledArgs.push_back(static_cast(strtol(instr.args[2].c_str(), NULL, 0))); + } + // Variable based + else if (prefix == '$') + { + instr.useLabel = true; + leu32_put(instr.compiledArgs, 0); // reserve 4 bytes + } + else + { + CHIP32_CHECK(instr, false, "Load source address must be @reg or $variable"); + } + break; + } case OP_CMP_EQ: case OP_CMP_GT: case OP_CMP_LT: @@ -415,7 +453,7 @@ bool Assembler::Parse(const std::string &data) if (nbArgsSuccess) { - CHIP32_CHECK(instr, CompileMnemonicArguments(instr) == true, "Compile failure"); + CHIP32_CHECK(instr, CompileMnemonicArguments(instr) == true, "Compile failure, mnemonic or arguments"); instr.addr = code_addr; code_addr += 1 + instr.compiledArgs.size(); m_instructions.push_back(instr); @@ -478,16 +516,20 @@ bool Assembler::Parse(const std::string &data) { if (instr.useLabel && (instr.args.size() > 0)) { - // label is the first argument for jump, second position for LCONS - uint16_t argsIndex = instr.code.opcode == OP_LCONS ? 1 : 0; + // label is the first argument for jump, second position for LCONS and LOAD + uint16_t argsIndex = 1; + if (instr.code.opcode == OP_JUMP) { + argsIndex = 0; + } std::string label = instr.args[argsIndex]; CHIP32_CHECK(instr, m_labels.count(label) > 0, "label not found: " + label); uint16_t addr = m_labels[label].addr; std::cout << "LABEL: " << label << " , addr: " << addr << std::endl; instr.compiledArgs[argsIndex] = addr & 0xFF; instr.compiledArgs[argsIndex+1] = (addr >> 8U) & 0xFF; - if (instr.code.opcode == OP_LCONS) { + if ((instr.code.opcode == OP_LCONS) || (instr.code.opcode == OP_LOAD)) { // We precise if the address is from RAM or ROM + // For that, the MSB is set to 1 (for RAM) instr.compiledArgs[argsIndex+3] = m_labels[label].isRamData ? 0x80 : 0; } } diff --git a/core/chip32/chip32_assembler.h b/core/chip32/chip32_assembler.h index 8bc54e6..33d3093 100644 --- a/core/chip32/chip32_assembler.h +++ b/core/chip32/chip32_assembler.h @@ -89,8 +89,13 @@ public: struct Error { std::string message; - int line; - std::string ToString() const { return "Error line " + std::to_string(line) + ", " + message; } + int line{-1}; + std::string ToString() const { + if (line < 0) + return "No error"; + else + return "Error line " + std::to_string(line) + ", " + message; + } }; // Separated parser to allow only code check @@ -103,6 +108,8 @@ public: m_instructions.clear(); } + static std::vector Split(const std::string &line); + std::vector::const_iterator Begin() { return m_instructions.begin(); } std::vector::const_iterator End() { return m_instructions.end(); } diff --git a/core/chip32/chip32_macros.h b/core/chip32/chip32_macros.h new file mode 100644 index 0000000..e12af80 --- /dev/null +++ b/core/chip32/chip32_macros.h @@ -0,0 +1,195 @@ +/* +The MIT License + +Copyright (c) 2022 Anthony Rabine + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "chip32_assembler.h" + +namespace Chip32 +{ + +struct Macro { + int param_count; + std::vector body; +}; + +class ScriptProcessor { +public: + void process(const std::string &text) { + + resultAsm.clear(); + + std::stringstream data_stream(text); + std::string line; + + while(std::getline(data_stream, line)) + { + line = trim(line); + parseLine(line); + } + } + + // Return the expanded assembly file + // without any macro directives + std::string GetResult() const { + return resultAsm; + } + + void generate_assembly() { + + std::stringstream out; + + for (const std::string& line : text_section) { + out << line << "\n"; + } + + for (const std::string& line : data_section) { + out << line << "\n"; + } + resultAsm = out.str(); + } + +private: + std::stack conditionStack; + std::stack loopStack; + std::string resultAsm; + Macro* current_macro = nullptr; + + + std::unordered_map macros; + std::vector text_section; + std::vector data_section; + bool in_data_section = false; + bool in_macro_section = false; + + + int labelCounter = 0; + int printCounter = 0; + + std::string trim(const std::string& str) { + size_t first = str.find_first_not_of(" \t"); + if (first == std::string::npos) return ""; + size_t last = str.find_last_not_of(" \t"); + return str.substr(first, (last - first + 1)); + } + + + std::string expand_macro(const std::string& line, const std::vector& args) { + std::string expanded = line; + for (size_t i = 0; i < args.size(); ++i) { + std::string placeholder = "%" + std::to_string(i + 1); + expanded = std::regex_replace(expanded, std::regex(placeholder), args[i]); + } + return expanded; + } + + void createLabel() + { + int label = labelCounter++; + loopStack.push(label); + std::cout << "L" << label << "_START:" << std::endl; + } + + void parseLine(std::string& line) { + line = std::regex_replace(line, std::regex("^ +| +$"), ""); // Trim spaces + if (line.empty() || line[0] == ';') return; + + if ((line.find("%macro") == 0)) { + + if (!in_macro_section) { + std::cerr << "Error: Macros must be located in macro section\n"; + exit(1); + } + + std::istringstream ss(line); + std::string _, name; + int param_count; + ss >> _ >> name >> param_count; + macros[name] = { param_count, {} }; + current_macro = ¯os[name]; + } else if (line.find("%endmacro") == 0) { + current_macro = nullptr; + } else if (current_macro) { + current_macro->body.push_back(line); + } else if (line.find("%section_macro") == 0) { + in_macro_section = true; + + } else if (line.find("%section_text") == 0) { + + in_macro_section = false; + in_data_section = false; + } else if (line.find("%section_data") == 0) { + + in_macro_section = false; + in_data_section = true; + } else { + std::vector args = Assembler::Split(line); + + if (args.size() > 0) { + std::string name = args[0]; + args.erase(args.begin()); + + if ((name.find("DC") == 0 || name.find("DV") == 0) && !in_data_section) { + std::cerr << "Error: Data declarations (DC/DV) must be inside section .data\n"; + exit(1); + } + + if (macros.find(name) != macros.end()) { + Macro& macro = macros[name]; + + if (args.size() != macro.param_count) { + std::cerr << "Error: Macro " << name << " expects " << macro.param_count << " arguments, got " << args.size() << "\n"; + return; + } + for (const std::string& body_line : macro.body) { + text_section.push_back(expand_macro(body_line, args)); + } + } else { + if (in_data_section) { + data_section.push_back(line); + } else { + text_section.push_back(line); + } + } + } + else + { + std::cerr << "Macro problem with this line\n"; + exit(1); + } + } + + } + +}; + +} // namespace Chip32 diff --git a/core/chip32/chip32_vm.c b/core/chip32/chip32_vm.c index 58306f3..a0727d9 100644 --- a/core/chip32/chip32_vm.c +++ b/core/chip32/chip32_vm.c @@ -65,14 +65,14 @@ static inline uint32_t _NEXT_INT (chip32_ctx_t *ctx) #define _CHECK_REGISTER_VALID(r) \ if (r >= REGISTER_COUNT) \ return VM_ERR_INVALID_REGISTER; + #define _CHECK_CAN_PUSH(n) \ - if (ctx->registers[SP] - (n * sizeof(uint32_t)) > ctx->ram.addr) \ + if (ctx->registers[SP] - (n * sizeof(uint32_t)) < 0) \ return VM_ERR_STACK_OVERFLOW; + #define _CHECK_CAN_POP(n) \ - if (ctx->registers[SP] + (n * sizeof(uint32_t)) > (ctx->ram.addr + ctx->ram.size)) \ - return VM_ERR_STACK_UNDERFLOW; \ - if (ctx->registers[SP] < ctx->prog_size) \ - return VM_ERR_STACK_OVERFLOW; + if ((ctx->registers[SP] + (n * sizeof(uint32_t))) > (ctx->ram.size)) \ + return VM_ERR_STACK_UNDERFLOW; #else #define _CHECK_ROM_ADDR_VALID(a) #define _CHECK_BYTES_AVAIL(n) @@ -266,6 +266,14 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx) ctx->registers[reg1] = ctx->registers[reg1] + ctx->registers[reg2]; break; } + case OP_ADDI: + { + const uint8_t reg1 = _NEXT_BYTE; + const uint8_t val = _NEXT_BYTE; + _CHECK_REGISTER_VALID(reg1) + ctx->registers[reg1] = ctx->registers[reg1] + val; + break; + } case OP_SUB: { const uint8_t reg1 = _NEXT_BYTE; @@ -275,6 +283,14 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx) ctx->registers[reg1] = ctx->registers[reg1] - ctx->registers[reg2]; break; } + case OP_SUBI: + { + const uint8_t reg1 = _NEXT_BYTE; + const uint8_t val = _NEXT_BYTE; + _CHECK_REGISTER_VALID(reg1) + ctx->registers[reg1] = ctx->registers[reg1] - val; + break; + } case OP_MUL: { const uint8_t reg1 = _NEXT_BYTE; diff --git a/core/chip32/chip32_vm.h b/core/chip32/chip32_vm.h index 110290f..cc45842 100644 --- a/core/chip32/chip32_vm.h +++ b/core/chip32/chip32_vm.h @@ -59,31 +59,33 @@ typedef enum // arithmetic: OP_ADD = 9, ///< sum and store in first reg, e.g.: add r0, r2 - OP_SUB = 10, ///< subtract and store in first reg, e.g.: sub r0, r2 - OP_MUL = 11, ///< multiply and store in first reg, e.g.: mul r0, r2 - OP_DIV = 12, ///< divide and store in first reg, remain in second, e.g.: div r0, r2 + OP_ADDI = 10, ///< Add immediate value (8 bits max), e.g.: addi r4, 2 ; r4 = r4 + 2 + OP_SUB = 11, ///< subtract and store in first reg, e.g.: sub r0, r2 + OP_SUBI = 12, ///< substract immediate value (8 bits max), e.g.: subi r2, 8 ; r2 = r2 - 8 + OP_MUL = 13, ///< multiply and store in first reg, e.g.: mul r0, r2 + OP_DIV = 14, ///< divide and store in first reg, remain in second, e.g.: div r0, r2 - OP_SHL = 13, ///< logical shift left, e.g.: shl r0, r1 - OP_SHR = 14, ///< logical shift right, e.g.: shr r0, r1 - OP_ISHR = 15, ///< arithmetic shift right (for signed values), e.g.: ishr r0, r1 + OP_SHL = 15, ///< logical shift left, e.g.: shl r0, r1 + OP_SHR = 16, ///< logical shift right, e.g.: shr r0, r1 + OP_ISHR = 17, ///< arithmetic shift right (for signed values), e.g.: ishr r0, r1 - OP_AND = 16, ///< and two registers and store result in the first one, e.g.: and r0, r1 - OP_OR = 17, ///< or two registers and store result in the first one, e.g.: or r0, r1 - OP_XOR = 18, ///< xor two registers and store result in the first one, e.g.: xor r0, r1 - OP_NOT = 19, ///< not a register and store result, e.g.: not r0 + OP_AND = 18, ///< and two registers and store result in the first one, e.g.: and r0, r1 + OP_OR = 19, ///< or two registers and store result in the first one, e.g.: or r0, r1 + OP_XOR = 20, ///< xor two registers and store result in the first one, e.g.: xor r0, r1 + OP_NOT = 21, ///< not a register and store result, e.g.: not r0 // branching/functions - OP_CALL = 20, ///< set register RA to the next instruction and jump to subroutine, e.g.: call 0x10 0x00 - OP_RET = 21, ///< return to the address of last callee (RA), e.g.: ret - OP_JUMP = 22, ///< jump to address (can use label or address), e.g.: jump .my_label - OP_JUMPR = 23, ///< jump to address contained in a register, e.g.: jumpr t9 - OP_SKIPZ = 24, ///< skip next instruction if zero, e.g.: skipz r0 - OP_SKIPNZ = 25, ///< skip next instruction if not zero, e.g.: skipnz r2 + OP_CALL = 22, ///< set register RA to the next instruction and jump to subroutine, e.g.: call 0x10 0x00 + OP_RET = 23, ///< return to the address of last callee (RA), e.g.: ret + OP_JUMP = 24, ///< jump to address (can use label or address), e.g.: jump .my_label + OP_JUMPR = 25, ///< jump to address contained in a register, e.g.: jumpr t9 + OP_SKIPZ = 26, ///< skip next instruction if zero, e.g.: skipz r0 + OP_SKIPNZ = 27, ///< skip next instruction if not zero, e.g.: skipnz r2 // Comparison - OP_CMP_EQ = 26, ///< compare two registers for equality, result in first e.g.: cmp_eq r4, r0, r1 (r4 = (r0 == r1 ? 1 : 0) - OP_CMP_GT = 27, ///< compare if first register is greater than the second, result in first e.g.: cmp_gt r4, r0, r1 - OP_CMP_LT = 28, ///< compare if first register is less than the second, result in first e.g.: cmp_lt r4, r0, r1 + OP_CMP_EQ = 28, ///< compare two registers for equality, result in first e.g.: cmp_eq r4, r0, r1 (r4 = (r0 == r1 ? 1 : 0) + OP_CMP_GT = 29, ///< compare if first register is greater than the second, result in first e.g.: cmp_gt r4, r0, r1 + OP_CMP_LT = 30, ///< compare if first register is less than the second, result in first e.g.: cmp_lt r4, r0, r1 INSTRUCTION_COUNT } chip32_instruction_t; @@ -91,7 +93,7 @@ typedef enum /* -| name | number | type | preserved | +| name | number | type | preserved on function call | |-------|--------|----------------------------------|-----------| | r0-r9 | 0-9 | general-purpose | N | | t0-t9 | 10-19 | temporary registers | Y | @@ -157,7 +159,8 @@ typedef struct { #define OPCODES_LIST { { OP_NOP, 0, 0 }, { OP_HALT, 0, 0 }, { OP_SYSCALL, 1, 1 }, { OP_LCONS, 2, 5 }, \ { OP_MOV, 2, 2 }, { OP_PUSH, 1, 1 }, {OP_POP, 1, 1 }, \ -{ OP_STORE, 3, 3 }, { OP_LOAD, 3, 3 }, { OP_ADD, 2, 2 }, { OP_SUB, 2, 2 }, { OP_MUL, 2, 2 }, \ +{ OP_STORE, 3, 3 }, { OP_LOAD, 3, 3 }, \ +{ OP_ADD, 2, 2 }, { OP_ADDI, 2, 2 }, { OP_SUB, 2, 2 }, { OP_SUBI, 2, 2 }, { OP_MUL, 2, 2 }, \ { OP_DIV, 2, 2 }, { OP_SHL, 2, 2 }, { OP_SHR, 2, 2 }, { OP_ISHR, 2, 2 }, { OP_AND, 2, 2 }, \ { OP_OR, 2, 2 }, { OP_XOR, 2, 2 }, { OP_NOT, 1, 1 }, { OP_CALL, 1, 1 }, { OP_RET, 0, 0 }, \ { OP_JUMP, 1, 2 }, { OP_JUMPR, 1, 1 }, { OP_SKIPZ, 1, 1 }, { OP_SKIPNZ, 1, 1 }, \ diff --git a/core/chip32/tests/test_parser.cpp b/core/chip32/tests/test_parser.cpp index 075faf8..f0ea324 100644 --- a/core/chip32/tests/test_parser.cpp +++ b/core/chip32/tests/test_parser.cpp @@ -23,9 +23,11 @@ THE SOFTWARE. */ #include +#include #include "catch.hpp" #include "chip32_assembler.h" +#include "chip32_macros.h" /* Purpose: grammar, ram usage and macros, rom code generation @@ -65,14 +67,80 @@ mov R0,R2 ; copy R2 into R0 (NO blank space between , and R2) halt )"; +#include +#include + + +int get_from_memory(chip32_ctx_t *ctx, uint32_t addr, char *text) +{ + int valid = 0; + + // Test if address is valid + + bool isRam = addr & 0x80000000; + addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing + if (isRam) { + strcpy(&text[0], (const char *)&ctx->ram.mem[addr]); + } else { + strcpy(&text[0], (const char *)&ctx->rom.mem[addr]); + } + + return valid; +} + static uint8_t story_player_syscall(chip32_ctx_t *ctx, uint8_t code) { uint8_t retCode = SYSCALL_RET_OK; + char working_buf[100] = {0}; + + // Printf + if (code == 4) + { + // In R0: string with escaped characters + // R1: Number of arguments + // R2, R3 ... arguments + + // Integers: stored in registers by values + // Strings: first character address in register + + get_from_memory(ctx, ctx->registers[R0], working_buf); + int arg_count = ctx->registers[R1]; + + switch(arg_count){ + case 0: + puts(working_buf); + break; + case 1: + printf(working_buf, ctx->registers[R2]); + puts(""); + break; + case 2: + printf(working_buf, ctx->registers[R2], ctx->registers[R3]); + puts(""); + break; + case 3: + printf(working_buf, ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]); + puts(""); + break; + default: + break; + } + + } + // WAIT (sleep) + else if (code == 5) + { + std::this_thread::sleep_for(std::chrono::milliseconds(ctx->registers[R0])); + } return retCode; } + + + + TEST_CASE( "Check various indentations and typos" ) { std::vector program; @@ -82,7 +150,7 @@ TEST_CASE( "Check various indentations and typos" ) { bool parseResult = assembler.Parse(test1); - std::cout << assembler.GetLastError().ToString(); + std::cout << assembler.GetLastError().ToString() << std::endl; REQUIRE( parseResult == true ); @@ -90,7 +158,7 @@ TEST_CASE( "Check various indentations and typos" ) { result.Print(); hexdump(program.data(), program.size()); - static chip32_ctx_t chip32_ctx; + chip32_ctx_t chip32_ctx; chip32_ctx.stack_size = 512; @@ -99,7 +167,7 @@ TEST_CASE( "Check various indentations and typos" ) { chip32_ctx.rom.size = program.size(); chip32_ctx.ram.mem = data; - chip32_ctx.ram.addr = 40 *1024, + chip32_ctx.ram.addr = 40 *1024; chip32_ctx.ram.size = sizeof(data); chip32_ctx.syscall = story_player_syscall; @@ -108,3 +176,151 @@ TEST_CASE( "Check various indentations and typos" ) { chip32_result_t runResult = chip32_run(&chip32_ctx); REQUIRE( runResult == VM_FINISHED ); } + +// ==================================================================================================== +static const std::string testPrintf = R"( + +; ======================================================== +; We test the printf system call +; ======================================================== +jump .entry + +$printHello DC8 "La réponse est %d" +$answer DC32 42 + +$counter DV32 10 + +.entry: + + ; prepapre loop + + lcons t0, 1000 + lcons t1, 4 + .while R1 > 0 + .print "La valeur est: %d", $counter + + mov r0, t0 ; wait time in ms for argument + syscall 5 ; wait call + + .endwhile + + + halt + + + +)"; + + +static const std::string testMacro1 = R"( + +%section_macro + +%macro incr 1 + push t0 + lcons t0, 1 + add %1, t0 + pop t0 +%endmacro + + +%macro print 2 + lcons r0, %1 ; string text + lcons r1, 1 ; number of arguments + mov r2, %2 + syscall 4 +%endmacro + +%macro LOOP_START 3 + lcons %2, %3 ; Initialise le compteur de boucle (registre spécifié) + %1_loop: ; Étiquette de début de boucle +%endmacro + + +%macro LOOP_END 2 + subi %2, 1 ; Décrémente le registre spécifié + skipz %2 + jump %1_loop ; Saute si le registre n'est pas zéro +%endmacro + +%section_text + + lcons R3, 4 + incr R3 + + LOOP_START .myLoop, r6, 5 + print $printHello, r3 + LOOP_END .myLoop, r6 + halt + +%section_data + +$printHello DC8 "Answer is %d" + +)"; + +TEST_CASE( "Check assembly macro language part 1" ) +{ + + Chip32::ScriptProcessor processor; + processor.process(testMacro1); + + processor.generate_assembly(); + + std::string resultAsm = processor.GetResult(); + + std::cout << "-----------------------------------------------------" << std::endl; + std::cout << resultAsm << std::endl; + std::cout << "-----------------------------------------------------" << std::endl; + +/* + const std::string& output_filename + std::ofstream out(output_filename); + if (!out) { + std::cerr << "Error creating file: " << output_filename << "\n"; + return; + } + + out.close(); +*/ + + + std::vector program; + Chip32::Assembler assembler; + Chip32::Result result; + uint8_t data[8*1024]; + + bool parseResult = assembler.Parse(resultAsm); + + std::cout << assembler.GetLastError().ToString() << std::endl; + + REQUIRE( parseResult == true ); + + + REQUIRE( assembler.BuildBinary(program, result) == true); + result.Print(); + hexdump(program.data(), program.size()); + + chip32_ctx_t chip32_ctx; + + chip32_ctx.stack_size = 512; + + chip32_ctx.rom.mem = program.data(); + chip32_ctx.rom.addr = 0; + chip32_ctx.rom.size = program.size(); + + chip32_ctx.ram.mem = data; + chip32_ctx.ram.addr = 40 *1024; + chip32_ctx.ram.size = sizeof(data); + + chip32_ctx.syscall = story_player_syscall; + + chip32_initialize(&chip32_ctx); + chip32_result_t runResult = chip32_run(&chip32_ctx); + + REQUIRE( runResult == VM_FINISHED ); + + REQUIRE( chip32_ctx.registers[R3] == 5); + + +}