(wip) Instructions unit test (complete)
Some checks are pending
Build-StoryEditor / build_linux (push) Waiting to run
Build-StoryEditor / build_win32 (push) Waiting to run

This commit is contained in:
Anthony Rabine 2025-10-18 14:23:58 +02:00
parent 9db4bae9fd
commit ee6958a729
4 changed files with 725 additions and 74 deletions

View file

@ -210,8 +210,8 @@ bool Assembler::CompileMnemonicArguments(std::shared_ptr<Instr> instr)
case OP_PUSH:
case OP_SKIPZ:
case OP_SKIPNZ:
case OP_CALL:
case OP_JUMPR:
case OP_NOT:
GET_REG(instr->args[0], ra);
instr->compiledArgs.push_back(ra);
break;
@ -226,7 +226,6 @@ bool Assembler::CompileMnemonicArguments(std::shared_ptr<Instr> instr)
case OP_AND:
case OP_OR:
case OP_XOR:
case OP_NOT:
GET_REG(instr->args[0], ra);
GET_REG(instr->args[1], rb);
instr->compiledArgs.push_back(ra);
@ -245,11 +244,11 @@ bool Assembler::CompileMnemonicArguments(std::shared_ptr<Instr> instr)
leu32_put(instr->compiledArgs, op);
break;
}
case OP_CALL:
case OP_JUMP:
// Reserve 2 bytes for address, it will be filled at the end
// Reserve 4 bytes for address, it will be filled at the end
instr->useLabel = true;
instr->compiledArgs.push_back(0);
instr->compiledArgs.push_back(0);
instr->compiledArgs.reserve(4);
break;
case OP_STORE: // store @r4, r1, 2
CHIP32_CHECK(instr, instr->args[0].at(0) == '@', "Missing @ sign before register")
@ -558,9 +557,9 @@ Position of Data in RAM
{
if (instr->useLabel && (instr->args.size() > 0))
{
// label is the first argument for jump, second position for LCONS and LOAD
// label is the first argument for JUMP and CALL, second position for LCONS and LOAD
uint16_t argsIndex = 1;
if (instr->code.opcode == OP_JUMP) {
if ((instr->code.opcode == OP_JUMP) || (instr->code.opcode == OP_CALL)) {
argsIndex = 0;
}
std::string label = instr->args[argsIndex];

View file

@ -43,7 +43,7 @@ public:
m_syscallHandler = std::bind(&Machine::HandleSyscall, this,
std::placeholders::_1,
std::placeholders::_2);
ram.resize(1024);
ram.resize(2048);
}

View file

@ -35,6 +35,23 @@ THE SOFTWARE.
#define _NEXT_BYTE ctx->rom.mem[++ctx->registers[PC]]
static inline uint32_t deserialize_le32(const uint8_t *ptr)
{
return (uint32_t)ptr[0] |
((uint32_t)ptr[1] << 8) |
((uint32_t)ptr[2] << 16) |
((uint32_t)ptr[3] << 24);
}
static inline void serialize_le32(uint8_t *ptr, uint32_t value)
{
ptr[0] = (uint8_t)(value & 0xFF);
ptr[1] = (uint8_t)((value >> 8) & 0xFF);
ptr[2] = (uint8_t)((value >> 16) & 0xFF);
ptr[3] = (uint8_t)((value >> 24) & 0xFF);
}
static inline uint16_t _NEXT_SHORT (chip32_ctx_t *ctx)
{
ctx->registers[PC] += 2;
@ -45,8 +62,7 @@ static inline uint32_t _NEXT_INT (chip32_ctx_t *ctx)
{
ctx->registers[PC] += 4;
return ctx->rom.mem[ctx->registers[PC] - 3] | ctx->rom.mem[ctx->registers[PC] - 2] << 8 |
ctx->rom.mem[ctx->registers[PC] - 1] << 16 | ctx->rom.mem[ctx->registers[PC]] << 24;
return deserialize_le32(&ctx->rom.mem[ctx->registers[PC] - 3]);
}
#define _CHECK_SKIP if (skip) continue;
@ -88,11 +104,11 @@ static const uint16_t OpCodesSize = sizeof(OpCodes) / sizeof(OpCodes[0]);
static void push(chip32_ctx_t *ctx, uint32_t val) {
ctx->registers[SP] -= 4;
ctx->ram.mem[ctx->registers[SP]] = val;
serialize_le32(&ctx->ram.mem[ctx->registers[SP]], val);
}
static uint32_t pop(chip32_ctx_t *ctx) {
uint32_t val = ctx->ram.mem[ctx->registers[SP]];
uint32_t val = deserialize_le32(&ctx->ram.mem[ctx->registers[SP]]);
ctx->registers[SP] += 4;
return val;
}
@ -190,7 +206,7 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
}
case OP_CALL:
{
ctx->registers[RA] = ctx->registers[PC] + 2; // set return address to next instruction after CALL
ctx->registers[RA] = ctx->registers[PC] + 4; // set return address to next instruction after CALL (+4 is for address size)
const uint8_t reg = _NEXT_BYTE;
_CHECK_REGISTER_VALID(reg)
ctx->registers[PC] = ctx->registers[reg] - 1;
@ -319,7 +335,14 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
const uint8_t reg2 = _NEXT_BYTE;
_CHECK_REGISTER_VALID(reg1)
_CHECK_REGISTER_VALID(reg2)
if (ctx->registers[reg2] != 0)
{
ctx->registers[reg1] = ctx->registers[reg1] / ctx->registers[reg2];
}
else
{
ctx->registers[reg1] = 0;
}
break;
}
case OP_SHL:

View file

@ -1,87 +1,716 @@
/*
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.
*/
// ===================================================================
// test_vm.cpp - Tests exhaustifs de toutes les instructions Chip32
// ===================================================================
#include <iostream>
#include <catch2/catch_test_macros.hpp>
#include "chip32_machine.h" // Inclure chip32_machine.h au lieu de assembler et vm
/*
Purpose: test all opcodes
*/
// Suppression des fonctions et classes de configuration de la VM
// qui sont maintenant encapsulées dans Chip32::Machine.
#include "chip32_machine.h"
class VmTestContext
{
public:
VmTestContext() {
// La RAM est allouée et initialisée à l'intérieur de QuickExecute
// de la classe Machine. On peut laisser le constructeur vide.
}
VmTestContext() {}
void Execute(const std::string &assemblyCode)
{
// Utiliser la méthode QuickExecute de Machine pour Parse, Build et Run
machine.QuickExecute(assemblyCode);
// Vérification de base: le parsing et le build doivent réussir
REQUIRE( machine.parseResult == true );
REQUIRE( machine.buildResult == true );
// Vérification que l'exécution a fini normalement (HALT)
REQUIRE( machine.runResult == VM_FINISHED );
REQUIRE(machine.parseResult == true);
REQUIRE(machine.buildResult == true);
REQUIRE(machine.runResult == VM_FINISHED);
}
Chip32::Machine machine; // Instance de la Machine à utiliser pour les tests
Chip32::Machine machine;
};
// ===================================================================
// ARITHMETIC OPERATIONS
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "MUL", "[vm]") {
static const std::string test1 = R"(
TEST_CASE_METHOD(VmTestContext, "ADD - Addition", "[vm][arithmetic][add]") {
static const std::string test = R"(
lcons r0, 10
lcons r1, 32
add r0, r1
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}
TEST_CASE_METHOD(VmTestContext, "ADDI - Addition immediate (if supported)", "[vm][arithmetic][addi]") {
// Note: Vérifier si ADDI est implémenté dans votre VM
static const std::string test = R"(
lcons r0, 10
addi r0, 32
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}
TEST_CASE_METHOD(VmTestContext, "SUB - Subtraction", "[vm][arithmetic][sub]") {
static const std::string test = R"(
lcons r0, 50
lcons r1, 8
sub r0, r1
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}
TEST_CASE_METHOD(VmTestContext, "SUBI - Subtraction immediate (if supported)", "[vm][arithmetic][subi]") {
static const std::string test = R"(
lcons r0, 50
subi r0, 8
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}
TEST_CASE_METHOD(VmTestContext, "MUL - Multiplication", "[vm][arithmetic][mul]") {
static const std::string test = R"(
lcons r0, 37
lcons r1, 0x695
mul r0, r1
halt
)";
Execute(test1);
// Accéder directement aux registres de la Machine pour vérifier le résultat
uint32_t result = machine.ctx.registers[R0];
REQUIRE (result == 37 * 0x695);
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 37 * 0x695);
}
TEST_CASE_METHOD(VmTestContext, "DIV", "[vm]") {
static const std::string test1 = R"(
lcons r0, 37
lcons r1, 8
TEST_CASE_METHOD(VmTestContext, "DIV - Division", "[vm][arithmetic][div]") {
static const std::string test = R"(
lcons r0, 84
lcons r1, 2
div r0, r1
halt
)";
Execute(test1);
// Accéder directement aux registres de la Machine pour vérifier le résultat
uint32_t result = machine.ctx.registers[R0];
REQUIRE (result == (int)(37/8));
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}
TEST_CASE_METHOD(VmTestContext, "DIV - Division by zero protection", "[vm][arithmetic][div]") {
static const std::string test = R"(
lcons r0, 42
lcons r1, 0
div r0, r1
halt
)";
Execute(test);
// Vérifier que la VM ne crash pas (comportement peut varier)
}
// ===================================================================
// BITWISE OPERATIONS
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "AND - Bitwise AND", "[vm][bitwise][and]") {
static const std::string test = R"(
lcons r0, 0xFF
lcons r1, 0x0F
and r0, r1
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0x0F);
}
TEST_CASE_METHOD(VmTestContext, "OR - Bitwise OR", "[vm][bitwise][or]") {
static const std::string test = R"(
lcons r0, 0xF0
lcons r1, 0x0F
or r0, r1
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0xFF);
}
TEST_CASE_METHOD(VmTestContext, "XOR - Bitwise XOR", "[vm][bitwise][xor]") {
static const std::string test = R"(
lcons r0, 0xFF
lcons r1, 0xAA
xor r0, r1
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0x55);
}
TEST_CASE_METHOD(VmTestContext, "NOT - Bitwise NOT", "[vm][bitwise][not]") {
static const std::string test = R"(
lcons r0, 0x00000000
not r0
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0xFFFFFFFF);
}
TEST_CASE_METHOD(VmTestContext, "SHIFTL - Shift left", "[vm][bitwise][shiftl]") {
static const std::string test = R"(
lcons r0, 0x01
lcons r1, 4
shiftl r0, r1
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0x10);
}
TEST_CASE_METHOD(VmTestContext, "SHIFTR - Shift right (logical)", "[vm][bitwise][shiftr]") {
static const std::string test = R"(
lcons r0, 0x80
lcons r1, 4
shiftr r0, r1
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0x08);
}
TEST_CASE_METHOD(VmTestContext, "ISHIFTR - Shift right (arithmetic)", "[vm][bitwise][ishiftr]") {
static const std::string test = R"(
lcons r0, 0xFFFFFF80
lcons r1, 2
ishiftr r0, r1
halt
)";
Execute(test);
// Shift arithmétique conserve le signe
REQUIRE((int32_t)machine.ctx.registers[R0] == -32);
}
// ===================================================================
// DATA MOVEMENT
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "MOV - Move register", "[vm][data][mov]") {
static const std::string test = R"(
lcons r0, 42
mov r1, r0
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R1] == 42);
}
TEST_CASE_METHOD(VmTestContext, "LCONS - Load constant", "[vm][data][lcons]") {
static const std::string test = R"(
lcons r0, 0x12345678
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0x12345678);
}
// ===================================================================
// MEMORY OPERATIONS
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "STORE and LOAD - Memory operations", "[vm][memory]") {
static const std::string test = R"(
$myVar DV32, 0
.main:
lcons r0, 42
lcons r1, $myVar
store @r1, r0, 4
lcons r2, 0
load r2, @r1, 4
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R2] == 42);
}
TEST_CASE_METHOD(VmTestContext, "LOAD - Direct address", "[vm][memory][load]") {
static const std::string test = R"(
$value DV32, 123
.main:
load r0, $value, 4
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 123);
}
// ===================================================================
// STACK OPERATIONS
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "PUSH and POP", "[vm][stack]") {
static const std::string test = R"(
lcons r0, 42
lcons r1, 100
push r0
push r1
lcons r0, 0
lcons r1, 0
pop r1
pop r0
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
REQUIRE(machine.ctx.registers[R1] == 100);
}
TEST_CASE_METHOD(VmTestContext, "PUSH/POP - Multiple values", "[vm][stack]") {
static const std::string test = R"(
lcons r0, 1
lcons r1, 2
lcons r2, 3
lcons r3, 4
push r0
push r1
push r2
push r3
lcons r0, 0
lcons r1, 0
lcons r2, 0
lcons r3, 0
pop r3
pop r2
pop r1
pop r0
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 1);
REQUIRE(machine.ctx.registers[R1] == 2);
REQUIRE(machine.ctx.registers[R2] == 3);
REQUIRE(machine.ctx.registers[R3] == 4);
}
// ===================================================================
// CONTROL FLOW - JUMP
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "JUMP - Unconditional jump", "[vm][control][jump]") {
static const std::string test = R"(
lcons r0, 0
jump .target
lcons r0, 99
.target:
lcons r0, 42
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}
TEST_CASE_METHOD(VmTestContext, "JUMPR - Jump to register", "[vm][control][jumpr]") {
static const std::string test = R"(
lcons r0, .target
jumpr r0
lcons r1, 99
.target:
lcons r1, 42
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R1] == 42);
}
// ===================================================================
// CONTROL FLOW - SKIP
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "SKIPZ - Skip if zero (condition true)", "[vm][control][skipz]") {
static const std::string test = R"(
lcons r0, 0
skipz r0
jump .non_zero
lcons r1, 42
halt
.non_zero:
lcons r1, 99
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R1] == 42);
}
TEST_CASE_METHOD(VmTestContext, "SKIPZ - Skip if zero (condition false)", "[vm][control][skipz]") {
static const std::string test = R"(
lcons r0, 1
skipz r0
jump .non_zero
lcons r1, 42
halt
.non_zero:
lcons r1, 99
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R1] == 99);
}
TEST_CASE_METHOD(VmTestContext, "SKIPNZ - Skip if not zero (condition true)", "[vm][control][skipnz]") {
static const std::string test = R"(
lcons r0, 1
skipnz r0
jump .it_is_zero
lcons r1, 42
halt
.it_is_zero:
lcons r1, 99
halt
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R1] == 42);
}
TEST_CASE_METHOD(VmTestContext, "SKIPNZ - Skip if not zero (condition false)", "[vm][control][skipnz]") {
static const std::string test = R"(
lcons r0, 0
skipnz r0
jump .it_is_zero
lcons r1, 42
halt
.it_is_zero:
lcons r1, 99
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R1] == 99);
}
TEST_CASE_METHOD(VmTestContext, "SKIPZ - Multiple instructions", "[vm][control][skipz]") {
static const std::string test = R"(
lcons r0, 0
lcons r1, 0
skipz r0
lcons r1, 10
add r1, r0
halt
)";
Execute(test);
// Skip lcons, donc r1 reste 0, puis add 0 + 0 = 0
REQUIRE(machine.ctx.registers[R1] == 0);
}
// ===================================================================
// COMPARISONS
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "EQ - Equal (true)", "[vm][comparison][eq]") {
static const std::string test = R"(
lcons r1, 42
lcons r2, 42
eq r0, r1, r2
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 1);
}
TEST_CASE_METHOD(VmTestContext, "EQ - Equal (false)", "[vm][comparison][eq]") {
static const std::string test = R"(
lcons r1, 42
lcons r2, 10
eq r0, r1, r2
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0);
}
TEST_CASE_METHOD(VmTestContext, "GT - Greater than (true)", "[vm][comparison][gt]") {
static const std::string test = R"(
lcons r1, 50
lcons r2, 10
gt r0, r1, r2
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 1);
}
TEST_CASE_METHOD(VmTestContext, "GT - Greater than (false)", "[vm][comparison][gt]") {
static const std::string test = R"(
lcons r1, 10
lcons r2, 50
gt r0, r1, r2
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0);
}
TEST_CASE_METHOD(VmTestContext, "GT - Greater than (equal)", "[vm][comparison][gt]") {
static const std::string test = R"(
lcons r1, 42
lcons r2, 42
gt r0, r1, r2
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0);
}
TEST_CASE_METHOD(VmTestContext, "LT - Less than (true)", "[vm][comparison][lt]") {
static const std::string test = R"(
lcons r1, 10
lcons r2, 50
lt r0, r1, r2
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 1);
}
TEST_CASE_METHOD(VmTestContext, "LT - Less than (false)", "[vm][comparison][lt]") {
static const std::string test = R"(
lcons r1, 50
lcons r2, 10
lt r0, r1, r2
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0);
}
TEST_CASE_METHOD(VmTestContext, "LT - Less than (equal)", "[vm][comparison][lt]") {
static const std::string test = R"(
lcons r1, 42
lcons r2, 42
lt r0, r1, r2
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 0);
}
// ===================================================================
// FUNCTION CALLS
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "CALL and RET - Function call", "[vm][function]") {
static const std::string test = R"(
lcons r0, 10
call .myFunction
lcons r1, 100
halt
.myFunction:
lcons r0, 42
ret
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
REQUIRE(machine.ctx.registers[R1] == 100);
}
TEST_CASE_METHOD(VmTestContext, "CALL - Nested function calls", "[vm][function]") {
static const std::string test = R"(
lcons r0, 0
call .func1
halt
.func1:
lcons r0, 1
call .func2
ret
.func2:
lcons r0, 42
ret
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}
// ===================================================================
// SPECIAL INSTRUCTIONS
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "NOP - No operation", "[vm][special][nop]") {
static const std::string test = R"(
lcons r0, 42
nop
nop
nop
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}
TEST_CASE_METHOD(VmTestContext, "HALT - Program termination", "[vm][special][halt]") {
static const std::string test = R"(
lcons r0, 42
halt
lcons r0, 99
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}
// ===================================================================
// COMPLEX SCENARIOS
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "Complex - Factorial calculation", "[vm][complex]") {
static const std::string test = R"(
; Calculate 5! = 120
lcons r0, 5 ; n
lcons r1, 1 ; result
.loop:
mul r1, r0
lcons r2, 1
sub r0, r2
skipz r0
jump .loop
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R1] == 120);
}
TEST_CASE_METHOD(VmTestContext, "Complex - Fibonacci calculation", "[vm][complex]") {
static const std::string test = R"(
; Calculate 10th Fibonacci number = 55
lcons r0, 0 ; fib(n-2)
lcons r1, 1 ; fib(n-1)
lcons r2, 10 ; counter
.loop:
lcons r3, 1
sub r2, r3
skipz r2
jump .continue
jump .done
.continue:
mov r3, r1
add r1, r0
mov r0, r3
jump .loop
.done:
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R1] == 55);
}
TEST_CASE_METHOD(VmTestContext, "Complex - Array sum", "[vm][complex][array]") {
static const std::string test = R"(
$array DV32, 10, 20, 30, 40, 50
.main:
lcons r0, 0 ; sum
lcons r1, $array ; pointer
lcons r2, 5 ; count
.loop:
load r3, @r1, 4
add r0, r3
lcons r4, 4
add r1, r4
lcons r4, 1
sub r2, r4
skipz r2
jump .loop
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 150);
}
TEST_CASE_METHOD(VmTestContext, "Complex - Conditional branches", "[vm][complex][branch]") {
static const std::string test = R"(
lcons r0, 10
lcons r1, 5
gt r2, r0, r1
skipz r2
jump .greater
lcons r3, 1
jump .end
.greater:
lcons r3, 2
.end:
halt
)";
Execute(test);
REQUIRE(machine.ctx.registers[R3] == 2);
}
// ===================================================================
// EDGE CASES
// ===================================================================
TEST_CASE_METHOD(VmTestContext, "Edge - Maximum positive integer", "[vm][edge]") {
static const std::string test = R"(
lcons r0, 0x7FFFFFFF
lcons r1, 1
add r0, r1
halt
)";
Execute(test);
// Overflow vers valeur négative
REQUIRE((int32_t)machine.ctx.registers[R0] < 0);
}
TEST_CASE_METHOD(VmTestContext, "Edge - All registers", "[vm][edge][registers]") {
static const std::string test = R"(
lcons r0, 0
lcons r1, 1
lcons r2, 2
lcons r3, 3
lcons r4, 4
lcons r5, 5
lcons r6, 6
lcons r7, 7
lcons r8, 8
lcons r9, 9
halt
)";
Execute(test);
for (int i = 0; i < 10; i++) {
REQUIRE(machine.ctx.registers[i] == i);
}
}
TEST_CASE_METHOD(VmTestContext, "Edge - Deep nested calls", "[vm][edge][stack]") {
static const std::string test = R"(
call .level1
halt
.level1:
call .level2
ret
.level2:
call .level3
ret
.level3:
lcons r0, 42
ret
)";
Execute(test);
REQUIRE(machine.ctx.registers[R0] == 42);
}