Big update on machine class, fix addressing lcons, load etc.
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-16 00:11:01 +02:00
parent c6da4b891a
commit 0c3809657e
16 changed files with 568 additions and 18486 deletions

3
.gitignore vendored
View file

@ -86,4 +86,5 @@ story-editor/flatpak_build/
story-editor/AppDir/
story-player/android/build/reports/problems/problems-report.html
core/tests/build
core/tests/build
core/story-manager/tests/build

2
.vscode/launch.json vendored
View file

@ -73,7 +73,7 @@
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/core/tests/build/core_tests", // Remplacez par le chemin de votre exécutable
"args": ["[vm]"],
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/core/tests/build",
"environment": [],

View file

@ -24,7 +24,6 @@ THE SOFTWARE.
#include "chip32_assembler.h"
#include "chip32_binary_format.h"
#include <sstream>
#include <vector>
@ -37,6 +36,7 @@ THE SOFTWARE.
namespace Chip32
{
// =============================================================================
// GLOBAL UTILITY FUNCTIONS
// =============================================================================
@ -263,15 +263,14 @@ bool Assembler::CompileMnemonicArguments(Instr &instr)
{
// 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
// - load r0, $variable, 2 ; 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);
instr.args[1].erase(0, 1); // delete @ character
GET_REG(instr.args[0], ra);
GET_REG(instr.args[1], rb);
instr.compiledArgs.push_back(ra);
@ -282,7 +281,10 @@ bool Assembler::CompileMnemonicArguments(Instr &instr)
else if (prefix == '$')
{
instr.useLabel = true;
GET_REG(instr.args[0], ra);
instr.compiledArgs.push_back(ra | 0x80); // Flag this register with a bit to indicate an immediate address is following
leu32_put(instr.compiledArgs, 0); // reserve 4 bytes
instr.compiledArgs.push_back(static_cast<uint32_t>(strtol(instr.args[2].c_str(), NULL, 0)));
}
else
{
@ -357,203 +359,6 @@ bool Assembler::CompileConstantArgument(Instr &instr, const std::string &a)
return true;
}
bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
{
program.clear();
result = { 0, 0, 0};
// ========================================================================
// PHASE 1: Créer les sections temporaires
// ========================================================================
std::vector<uint8_t> constSection; // DC - ROM constants only
std::vector<uint8_t> codeSection; // Executable code
std::vector<uint8_t> initDataSection; // DV values + DZ zeros (RAM init)
uint32_t bssSize = 0; // Total RAM size (DV + DZ)
uint32_t entryPoint = 0; // Entry point (.main)
// Map to track DV init data: RAM address -> init data
std::map<uint16_t, std::vector<uint8_t>> dvInitData;
// ========================================================================
// PHASE 2: Première passe - identifier les variables DV et leurs données
// ========================================================================
// On doit d'abord construire une map des données d'initialisation DV
for (size_t idx = 0; idx < m_instructions.size(); idx++)
{
const auto &instr = m_instructions[idx];
// Détecter une variable DV (le marqueur final)
if (instr.isRamData && !instr.isZeroData && instr.mnemonic[0] == '$')
{
uint16_t ramAddr = instr.addr;
uint16_t romInitAddr = instr.romInitAddr;
uint16_t dataLen = instr.dataLen;
// Collecter les données d'init depuis les instructions précédentes
std::vector<uint8_t> initData;
// Chercher en arrière les instructions ROM data pour cette variable
for (size_t j = 0; j < idx; j++)
{
const auto &dataInstr = m_instructions[j];
// Ces instructions ont isRomData=true et leur addr correspond à romInitAddr
if (dataInstr.isRomData && !dataInstr.isRamData &&
dataInstr.addr >= romInitAddr &&
dataInstr.addr < romInitAddr + dataLen)
{
// Ces données appartiennent à cette variable DV
initData.insert(initData.end(),
dataInstr.compiledArgs.begin(),
dataInstr.compiledArgs.end());
}
}
dvInitData[ramAddr] = initData;
}
}
// ========================================================================
// PHASE 3: Deuxième passe - dispatcher dans les bonnes sections
// ========================================================================
// Track which ROM data belongs to DV variables
std::set<uint16_t> dvRomAddresses;
for (const auto &instr : m_instructions)
{
if (instr.isRamData && !instr.isZeroData && instr.mnemonic[0] == '$')
{
for (uint16_t addr = instr.romInitAddr;
addr < instr.romInitAddr + instr.dataLen;
addr++)
{
dvRomAddresses.insert(addr);
}
}
}
for (const auto &i : m_instructions)
{
// ====================================================================
// RAM VARIABLES (DV et DZ) - marqueurs uniquement
// ====================================================================
if (i.isRamData)
{
bssSize += i.dataLen;
// Les données seront traitées dans la phase 4
}
// ====================================================================
// ROM CODE (Instructions exécutables)
// ====================================================================
else if (i.isRomCode())
{
// Détecter le point d'entrée AVANT d'ajouter les données
if (i.isLabel && i.mnemonic == ".main")
{
entryPoint = static_cast<uint32_t>(codeSection.size());
}
// Ajouter l'opcode
codeSection.push_back(i.code.opcode);
// Ajouter les arguments compilés
std::copy(i.compiledArgs.begin(),
i.compiledArgs.end(),
std::back_inserter(codeSection));
}
// ====================================================================
// ROM DATA - Distinguer DC (vraies constantes) de DV init data
// ====================================================================
else if (i.isRomData && !i.isRamData)
{
// Vérifier si cette donnée appartient à une variable DV
bool isDvInitData = (dvRomAddresses.find(i.addr) != dvRomAddresses.end());
if (!isDvInitData)
{
// C'est une vraie constante DC - va dans constSection
std::copy(i.compiledArgs.begin(),
i.compiledArgs.end(),
std::back_inserter(constSection));
result.constantsSize += i.compiledArgs.size();
}
// Sinon, c'est une donnée DV, elle sera traitée dans la phase 4
}
}
// ========================================================================
// PHASE 4: Construire initDataSection (DV + DZ dans l'ordre RAM)
// ========================================================================
if (bssSize > 0)
{
initDataSection.resize(bssSize, 0); // Tout à zéro par défaut (pour DZ)
// Copier les données DV aux bonnes positions RAM
for (const auto &pair : dvInitData)
{
uint16_t ramAddr = pair.first;
const std::vector<uint8_t> &data = pair.second;
// Copier les données d'init à l'adresse RAM correspondante
for (size_t i = 0; i < data.size() && (ramAddr + i) < bssSize; i++)
{
initDataSection[ramAddr + i] = data[i];
}
}
// Les zones DZ sont déjà à zéro grâce au resize()
}
// ========================================================================
// PHASE 5: Créer le header binaire
// ========================================================================
chip32_binary_header_t header;
chip32_binary_header_init(&header);
header.const_size = static_cast<uint32_t>(constSection.size());
header.bss_size = bssSize;
header.code_size = static_cast<uint32_t>(codeSection.size());
header.entry_point = entryPoint;
header.init_data_size = static_cast<uint32_t>(initDataSection.size());
if (initDataSection.size() > 0)
{
header.flags |= CHIP32_FLAG_HAS_INIT_DATA;
}
// ========================================================================
// PHASE 6: Assembler le binaire final
// ========================================================================
uint32_t totalSize = chip32_binary_calculate_size(&header);
program.resize(totalSize);
uint32_t bytesWritten = chip32_binary_write(
&header,
constSection.empty() ? nullptr : constSection.data(),
codeSection.empty() ? nullptr : codeSection.data(),
initDataSection.empty() ? nullptr : initDataSection.data(),
program.data(),
static_cast<uint32_t>(program.size())
);
if (bytesWritten == 0 || bytesWritten != totalSize)
{
// Erreur lors de l'écriture
program.clear();
return false;
}
// ========================================================================
// PHASE 7: Remplir les statistiques
// ========================================================================
result.ramUsageSize = bssSize;
result.romUsageSize = header.const_size + header.code_size;
result.constantsSize = header.const_size;
return true;
}
bool Assembler::Parse(const std::string &data)
{
std::stringstream data_stream(data);
@ -561,7 +366,8 @@ bool Assembler::Parse(const std::string &data)
Clear();
int code_addr = 0;
int ram_addr = 0;
int dz_ram_addr = 0; // For DZ
int dv_ram_addr = 0; // For DV
int lineNum = 0;
while(std::getline(data_stream, line))
{
@ -682,38 +488,20 @@ bool Assembler::Parse(const std::string &data)
// =======================================================================================
else if (!instr.isZeroData) // DV
{
// DV behaves like DC for data storage, but marks the location as RAM
// The initial values are stored in ROM and must be copied to RAM at startup
// DV behaves like DC for data storage
instr.addr = ram_addr;
instr.addr = dv_ram_addr;
m_labels[opcode] = instr; // RAM address for this variable
// Store the ROM address where initial data starts
instr.romInitAddr = code_addr;
// Process all initial values (like DC)
for (unsigned int i = 2; i < lineParts.size(); i++)
{
Instr dataInstr = instr; // Copy instruction info
CHIP32_CHECK(dataInstr, CompileConstantArgument(dataInstr, lineParts[i]),
CHIP32_CHECK(instr, CompileConstantArgument(instr, lineParts[i]),
"Compile argument error, stopping.");
// This instruction contains ROM data for initialization
dataInstr.addr = code_addr;
dataInstr.isRomData = true; // Store in ROM for init data
dataInstr.isRamData = false; // But this is just init data, not the variable
m_instructions.push_back(dataInstr);
code_addr += dataInstr.compiledArgs.size();
instr.dataLen += dataInstr.compiledArgs.size();
m_instructions.push_back(instr);
dv_ram_addr += instr.compiledArgs.size();
instr.addr = dv_ram_addr;
}
// Now add the RAM variable marker
instr.addr = ram_addr;
instr.isRomData = false;
instr.isRamData = true;
ram_addr += instr.dataLen;
m_instructions.push_back(instr);
}
// =======================================================================================
// DZ - Zero-initialized RAM zones (no data in ROM, just reserve space)
@ -724,22 +512,39 @@ bool Assembler::Parse(const std::string &data)
CHIP32_CHECK(instr, lineParts.size() == 3,
"DZ directive requires exactly one argument (number of elements)");
instr.addr = ram_addr;
instr.addr = dz_ram_addr;
// Calculate size in bytes: num_elements * (type_size / 8)
uint32_t numElements = static_cast<uint32_t>(strtol(lineParts[2].c_str(), NULL, 0));
instr.dataLen = static_cast<uint16_t>(numElements * (instr.dataTypeSize / 8));
ram_addr += instr.dataLen;
dz_ram_addr += instr.dataLen;
m_labels[opcode] = instr;
m_instructions.push_back(instr);
}
}
}
// Now that the RAM DV sier is known, compute DZ real data location after DV
/*
Position of Data in RAM
----------------------------
| DV |
----------------------------
| DZ |
----------------------------
*/
// 2. Second pass: replace all label or RAM data by the real address in memory
for (auto &instr : m_instructions)
{
if (instr.isZeroData)
{
instr.addr += dv_ram_addr;
}
if (instr.useLabel && (instr.args.size() > 0))
{
// label is the first argument for jump, second position for LCONS and LOAD
@ -749,15 +554,17 @@ bool Assembler::Parse(const std::string &data)
}
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;
uint32_t addr = m_labels[label].addr;
std::cout << "LABEL: " << label << " , addr: " << addr << std::endl;
if (m_labels[label].isRamData)
{
addr |= CHIP32_RAM_OFFSET;
}
instr.compiledArgs[argsIndex] = addr & 0xFF;
instr.compiledArgs[argsIndex+1] = (addr >> 8U) & 0xFF;
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;
}
instr.compiledArgs[argsIndex+2] = (addr >> 16U) & 0xFF;
instr.compiledArgs[argsIndex+3] = (addr >> 24U) & 0xFF;
}
}

View file

@ -56,8 +56,7 @@ struct Instr {
bool isRamData{false}; //!< True if RAM variable (DV or DZ)
bool isZeroData{false}; //!< True if zero-initialized RAM (DZ only)
uint16_t addr{0}; //!< Instruction/data address (ROM for DC, RAM for DV/DZ)
uint16_t romInitAddr{0};//!< For DV: ROM address where initial data is stored
uint32_t addr{0}; //!< Instruction/data address (ROM for DC, RAM for DV/DZ)
bool isRomCode() const { return !(isLabel || isRomData || isRamData); }
};
@ -68,23 +67,6 @@ struct RegNames
std::string name;
};
struct Result
{
int ramUsageSize{0};
int romUsageSize{0};
int constantsSize{0};
void Print()
{
std::cout << "RAM usage: " << ramUsageSize << " bytes\n"
<< "IMAGE size: " << romUsageSize << " bytes\n"
<< " -> ROM DATA: " << constantsSize << " bytes\n"
<< " -> ROM CODE: " << romUsageSize - constantsSize << "\n"
<< std::endl;
}
};
class Assembler
{
public:
@ -102,8 +84,6 @@ public:
// Separated parser to allow only code check
bool Parse(const std::string &data);
// Generate the executable binary after the parse pass
bool BuildBinary(std::vector<uint8_t> &program, Result &result);
void Clear() {
m_labels.clear();
@ -112,8 +92,8 @@ public:
static std::vector<std::string> Split(const std::string &line);
std::vector<Instr>::const_iterator Begin() { return m_instructions.begin(); }
std::vector<Instr>::const_iterator End() { return m_instructions.end(); }
std::vector<Instr>::const_iterator begin() { return m_instructions.begin(); }
std::vector<Instr>::const_iterator end() { return m_instructions.end(); }
// Returns the register number from the name
bool GetRegister(const std::string &regName, uint8_t &reg);

View file

@ -18,17 +18,16 @@ chip32_binary_error_t chip32_binary_load(
uint32_t binary_size,
uint8_t* ram,
uint32_t ram_size,
chip32_binary_stats_t *out_stats
chip32_binary_header_t *header
)
{
memset(ctx, 0, sizeof(ctx));
// Initialize VM
chip32_initialize(ctx);
ctx->stack_size = 512; // Can be changed outside this function
// Clear RAM
memset(ram, 0, ram_size);
chip32_binary_header_t header;
if (!binary || !ctx) {
return CHIP32_BIN_ERR_NULL_POINTER;
}
@ -39,23 +38,23 @@ chip32_binary_error_t chip32_binary_load(
}
// Copy header
memcpy(&header, binary, sizeof(chip32_binary_header_t));
memcpy(header, binary, sizeof(chip32_binary_header_t));
// Verify magic number
if (header.magic != CHIP32_MAGIC) {
if (header->magic != CHIP32_MAGIC) {
return CHIP32_BIN_ERR_INVALID_MAGIC;
}
// Check version
if (header.version > CHIP32_VERSION) {
if (header->version > CHIP32_VERSION) {
return CHIP32_BIN_ERR_UNSUPPORTED_VERSION;
}
// Calculate expected size
uint32_t expected_size = sizeof(chip32_binary_header_t) +
header.const_size +
header.code_size +
header.init_data_size;
header->const_size +
header->code_size +
header->data_size;
if (binary_size != expected_size) {
return CHIP32_BIN_ERR_SIZE_MISMATCH;
@ -76,27 +75,13 @@ chip32_binary_error_t chip32_binary_load(
ctx->ram.size = ram_size;
// Set entry point (DATA size + entry point offset in CODE)
ctx->registers[PC] = header.entry_point;
ctx->registers[PC] = header->entry_point;
// Stack pointer at the end of the RAM
ctx->registers[SP] = ctx->ram.size;
// Load data initialized values
const uint8_t *data = binary + header.code_size;
memcpy(ram, data, header.init_data_size);
out_stats->const_size = header.const_size;
out_stats->bss_size = header.bss_size;
out_stats->code_size = header.code_size;
out_stats->init_data_size = header.init_data_size;
out_stats->total_file_size = sizeof(chip32_binary_header_t) +
header.const_size +
header.code_size +
header.init_data_size;
out_stats->total_rom_size = header.const_size +
header.code_size;
out_stats->total_ram_size = header.bss_size;
const uint8_t *data = binary + offset + header->const_size + header->code_size;
memcpy(ram, data, header->data_size);
return CHIP32_BIN_OK;
}
@ -146,12 +131,12 @@ uint32_t chip32_binary_calculate_size(const chip32_binary_header_t* header)
return sizeof(chip32_binary_header_t) +
header->const_size +
header->code_size +
header->init_data_size;
header->data_size;
}
uint32_t chip32_binary_write(
const chip32_binary_header_t* header,
const uint8_t* data_section,
const uint8_t* const_section,
const uint8_t* code_section,
const uint8_t* init_data_section,
uint8_t* out_buffer,
@ -169,18 +154,16 @@ uint32_t chip32_binary_write(
return 0; // Buffer too small
}
uint32_t offset = 0;
// Write header
memcpy(out_buffer + offset, header, sizeof(chip32_binary_header_t));
offset += sizeof(chip32_binary_header_t);
memcpy(out_buffer, header, sizeof(chip32_binary_header_t));
uint32_t offset = sizeof(chip32_binary_header_t);
// Write DATA section
// Write CONST section
if (header->const_size > 0) {
if (!data_section) {
if (!const_section) {
return 0; // Data expected but NULL pointer
}
memcpy(out_buffer + offset, data_section, header->const_size);
memcpy(out_buffer + offset, const_section, header->const_size);
offset += header->const_size;
}
@ -194,12 +177,12 @@ uint32_t chip32_binary_write(
}
// Write INIT DATA section
if (header->init_data_size > 0) {
if (header->data_size > 0) {
if (!init_data_section) {
return 0; // Init data expected but NULL pointer
}
memcpy(out_buffer + offset, init_data_section, header->init_data_size);
offset += header->init_data_size;
memcpy(out_buffer + offset, init_data_section, header->data_size);
offset += header->data_size;
}
return offset;
@ -210,6 +193,26 @@ uint32_t chip32_binary_write(
// DEBUG/UTILITY FUNCTIONS
// ============================================================================
void chip32_binary_build_stats(const chip32_binary_header_t *header, chip32_binary_stats_t* out_stats)
{
out_stats->const_size = header->const_size;
out_stats->bss_size = header->bss_size;
out_stats->code_size = header->code_size;
out_stats->data_size = header->data_size;
out_stats->total_file_size = sizeof(chip32_binary_header_t) +
header->const_size +
header->code_size +
header->data_size;
out_stats->total_rom_size = header->const_size +
header->code_size;
out_stats->total_ram_size = header->bss_size;
}
void chip32_binary_print_header(const chip32_binary_header_t* header)
{
if (!header) {
@ -229,11 +232,11 @@ void chip32_binary_print_header(const chip32_binary_header_t* header)
printf(" (has init data)");
}
printf("\n");
printf("DATA section: %u bytes (ROM constants)\n", header->const_size);
printf("CONST section: %u bytes (ROM constants)\n", header->const_size);
printf("BSS section: %u bytes (Total RAM: DV+DZ)\n", header->bss_size);
printf("CODE section: %u bytes\n", header->code_size);
printf("Entry point: 0x%08X\n", header->entry_point);
printf("Init data: %u bytes (DV values + DZ zeros)\n", header->init_data_size);
printf("Init data: %u bytes (DV values + DZ zeros)\n", header->data_size);
printf("\n");
}
@ -244,13 +247,13 @@ void chip32_binary_print_stats(const chip32_binary_stats_t* stats)
}
printf("=== Chip32 Binary Statistics ===\n");
printf("DATA section: %u bytes (ROM, initialized)\n", stats->const_size);
printf("BSS section: %u bytes (RAM, DV+DZ)\n", stats->bss_size);
printf("CONST section: %u bytes (ROM, initialized)\n", stats->const_size);
printf("BSS section: %u bytes (RAM, DZ)\n", stats->bss_size);
printf("CODE section: %u bytes (ROM, executable)\n", stats->code_size);
printf("Init data: %u bytes (RAM initialization)\n", stats->init_data_size);
printf("Init data: %u bytes (RAM initialization)\n", stats->data_size);
printf("---\n");
printf("File size: %u bytes\n", stats->total_file_size);
printf("ROM usage: %u bytes (DATA + CODE)\n", stats->total_rom_size);
printf("RAM usage: %u bytes (BSS)\n", stats->total_ram_size);
printf("\n");
}
}

View file

@ -44,7 +44,7 @@
| Chip32 Header | <-- Début du fichier
| (Magic, Versions, Sizes, Entry...) |
+--------------------------------------+
| CONST Section (DV) | <-- Données initialisées
| CONST Section (DV) | <-- Données initialisées
| (Variables globales initialisées) | e.g., const int x = 5;
+--------------------------------------+
| CODE Section | <-- Instructions du programme
@ -53,7 +53,6 @@
| DATA Section (Optional) | <-- Données pour l'initialisation de la RAM (copie de DV)
| (Contient les valeurs initiales pour la RAM)
+--------------------------------------+
*/
@ -93,11 +92,11 @@ typedef struct {
uint32_t magic; // Magic number "C32\0"
uint16_t version; // Format version
uint16_t flags; // Feature flags
uint32_t const_size; // Size of DATA section in bytes (ROM constants)
uint32_t const_size; // Size of CONST section in bytes (ROM constants)
uint32_t bss_size; // Total RAM size (DV + DZ)
uint32_t code_size; // Size of CODE section in bytes
uint32_t entry_point; // Entry point offset in CODE section
uint32_t init_data_size; // Size of INIT DATA section (DV values + DZ zeros)
uint32_t data_size; // Size of INIT DATA section (DV values + DZ zeros)
} chip32_binary_header_t;
#pragma pack(pop)
@ -106,7 +105,7 @@ typedef struct {
uint32_t const_size; // ROM constants
uint32_t bss_size; // Total RAM needed
uint32_t code_size; // Executable code
uint32_t init_data_size; // RAM initialization data
uint32_t data_size; // RAM initialization data
uint32_t total_file_size; // File size on disk
uint32_t total_rom_size; // DATA + CODE
uint32_t total_ram_size; // RAM needed
@ -129,7 +128,7 @@ chip32_binary_error_t chip32_binary_load(
uint32_t binary_size,
uint8_t* ram,
uint32_t ram_size,
chip32_binary_stats_t *out_stats
chip32_binary_header_t *header
);
@ -160,16 +159,16 @@ uint32_t chip32_binary_calculate_size(const chip32_binary_header_t* header);
/**
* Write a complete binary to memory
* @param header Binary header
* @param data_section DATA section content (can be NULL if const_size is 0)
* @param const_section DATA section content (can be NULL if const_size is 0)
* @param code_section CODE section content (can be NULL if code_size is 0)
* @param init_data_section INIT DATA section (can be NULL if init_data_size is 0)
* @param init_data_section INIT DATA section (can be NULL if data_size is 0)
* @param out_buffer Output buffer (must be large enough)
* @param buffer_size Size of output buffer
* @return Number of bytes written, or 0 on error
*/
uint32_t chip32_binary_write(
const chip32_binary_header_t* header,
const uint8_t* data_section,
const uint8_t* const_section,
const uint8_t* code_section,
const uint8_t* init_data_section,
uint8_t* out_buffer,
@ -180,6 +179,8 @@ uint32_t chip32_binary_write(
// DEBUG/UTILITY FUNCTIONS
// ============================================================================
void chip32_binary_build_stats(const chip32_binary_header_t *header, chip32_binary_stats_t* out_stats);
/**
* Print binary header information
* @param header Binary header

View file

@ -12,6 +12,7 @@
#include <sstream>
#include <functional>
#include <cstring>
#include <iomanip>
#include "chip32_assembler.h"
#include "chip32_binary_format.h"
@ -28,24 +29,142 @@ public:
chip32_result_t runResult{VM_OK};
std::string printOutput;
chip32_ctx_t ctx; // Public pour accès aux registres dans les tests
std::vector<uint8_t> ram; // RAM storage
std::vector<uint8_t> program;
Chip32::Assembler assembler;
chip32_binary_header_t header;
Machine() {
// Bind syscall handler to this instance
m_syscallHandler = std::bind(&Machine::HandleSyscall, this,
std::placeholders::_1,
std::placeholders::_2);
m_ram.resize(1024);
ram.resize(1024);
}
bool BuildBinary(Assembler &assembler, std::vector<uint8_t> &program, chip32_binary_stats_t &stats)
{
program.clear();
// ========================================================================
// PHASE 1: Créer les sections temporaires
// ========================================================================
std::vector<uint8_t> constSection; // DC - ROM constants only
std::vector<uint8_t> codeSection; // Executable code
std::vector<uint8_t> initDataSection; // DV values + DZ zeros (RAM init)
chip32_binary_header_init(&header);
Chip32::Instr instr;
if (assembler.GetMain(instr))
{
header.entry_point = instr.addr;
}
// else: no main found, we try to start at address zero...
for (auto instr : assembler)
{
// ====================================================================
// CODE INSTRUCTIONS
// ====================================================================
if (instr.isRomCode())
{
// Ajouter l'opcode
codeSection.push_back(instr.code.opcode);
// Ajouter les arguments compilés
std::copy(instr.compiledArgs.begin(),
instr.compiledArgs.end(),
std::back_inserter(codeSection));
}
// ====================================================================
// ROM DATA - Distinguer DC (vraies constantes) de DV init data
// ====================================================================
else if (instr.isRomData && !instr.isRamData)
{
std::copy(instr.compiledArgs.begin(),
instr.compiledArgs.end(),
std::back_inserter(constSection));
}
// ====================================================================
// RAM VARIABLES
// ====================================================================
else if (instr.isRamData)
{
// ====================================================================
// ZEROED DATA (DZ)
// ====================================================================
if (instr.isZeroData)
{
header.bss_size += instr.dataLen;
}
// ====================================================================
// INITIALIZED RAM VARIABLES (DV)
// ====================================================================
else
{
// Ces données appartiennent à cette variable DV
initDataSection.insert(initDataSection.end(),
instr.compiledArgs.begin(),
instr.compiledArgs.end());
}
}
}
// ========================================================================
// PHASE 5: Créer le header binaire
// ========================================================================
header.const_size = static_cast<uint32_t>(constSection.size());
header.code_size = static_cast<uint32_t>(codeSection.size());
header.data_size = static_cast<uint32_t>(initDataSection.size());
if (initDataSection.size() > 0)
{
header.flags |= CHIP32_FLAG_HAS_INIT_DATA;
}
// ========================================================================
// PHASE 6: Assembler le binaire final
// ========================================================================
uint32_t totalSize = chip32_binary_calculate_size(&header);
program.resize(totalSize);
uint32_t bytesWritten = chip32_binary_write(
&header,
constSection.empty() ? nullptr : constSection.data(),
codeSection.empty() ? nullptr : codeSection.data(),
initDataSection.empty() ? nullptr : initDataSection.data(),
program.data(),
static_cast<uint32_t>(program.size())
);
if (bytesWritten == 0 || bytesWritten != totalSize)
{
// Erreur lors de l'écriture
program.clear();
return false;
}
// ========================================================================
// PHASE 7: Remplir les statistiques
// ========================================================================
chip32_binary_build_stats(&header, &stats);
return true;
}
// ========================================================================
// Méthode principale : Parse, Build, Execute
// ========================================================================
void QuickExecute(const std::string &assemblyCode)
{
std::vector<uint8_t> program;
Chip32::Assembler assembler;
Chip32::Result result;
chip32_binary_stats_t stats;
// Parse
parseResult = assembler.Parse(assemblyCode);
@ -56,24 +175,22 @@ public:
}
// Build binary with new format
buildResult = assembler.BuildBinary(program, result);
buildResult = BuildBinary(assembler, program, stats);
chip32_binary_print_stats(&stats);
if (!buildResult) {
std::cout << "Build error: " << assembler.GetLastError().ToString() << std::endl;
return;
}
result.Print();
// Load binary using executable format
chip32_binary_stats_t stats;
chip32_binary_error_t error = chip32_binary_load(
&ctx,
program.data(),
static_cast<uint32_t>(program.size()),
m_ram.data(),
static_cast<uint32_t>(m_ram.size()),
&stats
ram.data(),
static_cast<uint32_t>(ram.size()),
&header
);
if (error != CHIP32_BIN_OK) {
@ -81,15 +198,14 @@ public:
buildResult = false;
return;
}
chip32_binary_build_stats(&header, &stats);
chip32_binary_print_stats(&stats);
// Set syscall handler using wrapper
ctx.syscall = SyscallWrapper;
ctx.user_data = this;
// Initialize VM
chip32_initialize(&ctx);
std::cout << "Starting execution at PC=0x" << std::hex << ctx.registers[PC]
<< std::dec << std::endl;
@ -249,77 +365,171 @@ public:
}
}
// ============================================================================
// Fonction helper pour charger un binaire Chip32 dans la VM
// IMPORTANT: Le vecteur binary doit rester en vie pendant toute l'exécution
// car vm_ctx pointe directement dans ses données
// ============================================================================
bool LoadBinaryIntoVM(
std::vector<uint8_t> &binary,
chip32_ctx_t &vm_ctx,
uint8_t *ram_buffer,
uint32_t ram_size)
// ========================================================================
// Hexdump utilities
// ========================================================================
void HexDump(const uint8_t* data, uint32_t size, uint32_t base_addr = 0, const std::string& title = "")
{
if (err != CHIP32_BIN_OK)
{
std::cerr << "Binary load error: " << chip32_binary_error_string(err) << std::endl;
return false;
if (!title.empty()) {
std::cout << "\n=== " << title << " ===" << std::endl;
}
// Afficher les informations du binaire (debug)
chip32_binary_print_header(&loaded.header);
const int bytes_per_line = 16;
// Vérifier que la RAM est suffisante
if (loaded.header.bss_size > ram_size)
{
std::cerr << "Insufficient RAM: need " << loaded.header.bss_size
<< " bytes, have " << ram_size << " bytes" << std::endl;
return false;
for (uint32_t i = 0; i < size; i += bytes_per_line) {
// Adresse
std::cout << std::hex << std::setfill('0') << std::setw(8)
<< (base_addr + i) << ": ";
// Octets en hexadécimal
for (int j = 0; j < bytes_per_line; j++) {
if (i + j < size) {
std::cout << std::setw(2) << static_cast<int>(data[i + j]) << " ";
} else {
std::cout << " ";
}
// Séparateur au milieu
if (j == 7) {
std::cout << " ";
}
}
std::cout << " |";
// Représentation ASCII
for (int j = 0; j < bytes_per_line && i + j < size; j++) {
uint8_t byte = data[i + j];
if (byte >= 32 && byte <= 126) {
std::cout << static_cast<char>(byte);
} else {
std::cout << '.';
}
}
std::cout << "|" << std::dec << std::endl;
}
// ========================================================================
// Configurer la VM - ROM pointe directement dans le binaire
// ========================================================================
// Pour la VM, on considère DATA + CODE comme une seule ROM
// DATA commence à l'offset après le header
// CODE suit immédiatement après DATA
vm_ctx.rom.mem = const_cast<uint8_t*>(loaded.data_section);
vm_ctx.rom.addr = 0;
vm_ctx.rom.size = loaded.header.data_size + loaded.header.code_size;
// ========================================================================
// Initialiser la RAM avec INIT DATA section
// ========================================================================
// D'abord mettre toute la RAM à zéro
memset(ram_buffer, 0, ram_size);
// Ensuite copier les données d'initialisation (DV values + DZ zeros)
if (loaded.header.init_data_size > 0)
{
uint32_t copied = chip32_binary_init_ram(&loaded, ram_buffer, ram_size);
std::cout << "Copied " << copied << " bytes of init data to RAM" << std::endl;
}
// Configurer RAM dans la VM
vm_ctx.ram.mem = ram_buffer;
vm_ctx.ram.addr = 0x8000; // Bit 31 = 1 pour RAM
vm_ctx.ram.size = ram_size;
// ========================================================================
// Configurer le point d'entrée
// ========================================================================
// Le PC doit pointer dans la section CODE, qui commence après DATA
vm_ctx.registers[PC] = loaded.header.data_size + loaded.header.entry_point;
return true;
std::cout << std::endl;
}
void DumpRom()
{
if (ctx.rom.mem == nullptr || ctx.rom.size == 0) {
std::cout << "ROM is empty or not loaded" << std::endl;
return;
}
std::cout << "\n" << std::string(80, '=') << std::endl;
std::cout << "ROM DUMP (Total: " << ctx.rom.size << " bytes)" << std::endl;
std::cout << std::string(80, '=') << std::endl;
uint32_t offset = 0;
// Dump CONST section
if (header.const_size > 0) {
HexDump(ctx.rom.mem + offset, header.const_size, offset,
"CONST Section (" + std::to_string(header.const_size) + " bytes)");
offset += header.const_size;
}
// Dump CODE section
if (header.code_size > 0) {
HexDump(ctx.rom.mem + offset, header.code_size, offset,
"CODE Section (" + std::to_string(header.code_size) + " bytes)");
offset += header.code_size;
}
// Dump INIT DATA section
if (header.data_size > 0) {
HexDump(ctx.rom.mem + offset, header.data_size, offset,
"INIT DATA Section (" + std::to_string(header.data_size) + " bytes)");
}
}
void DumpRam()
{
if (ctx.ram.mem == nullptr || ctx.ram.size == 0) {
std::cout << "RAM is empty or not allocated" << std::endl;
return;
}
std::cout << "\n" << std::string(80, '=') << std::endl;
std::cout << "RAM DUMP (Total: " << ctx.ram.size << " bytes)" << std::endl;
std::cout << std::string(80, '=') << std::endl;
uint32_t offset = 0;
// Dump DATA section (initialized variables)
if (header.data_size > 0) {
HexDump(ctx.ram.mem + offset, header.data_size, 0x80000000 + offset,
"DATA Section (DV - " + std::to_string(header.data_size) + " bytes)");
offset += header.data_size;
}
// Dump BSS section (zero-initialized variables)
if (header.bss_size > 0) {
HexDump(ctx.ram.mem + offset, header.bss_size, 0x80000000 + offset,
"BSS Section (DZ - " + std::to_string(header.bss_size) + " bytes)");
offset += header.bss_size;
}
// Dump remaining RAM (heap/stack area)
uint32_t remaining = ctx.ram.size - offset;
if (remaining > 0) {
// Limiter l'affichage à 256 bytes pour le reste
uint32_t display_size = std::min(remaining, 256u);
HexDump(ctx.ram.mem + offset, display_size, 0x80000000 + offset,
"Heap/Stack area (showing first " + std::to_string(display_size) +
" of " + std::to_string(remaining) + " bytes)");
if (remaining > display_size) {
std::cout << "... (" << (remaining - display_size)
<< " more bytes not shown)" << std::endl;
}
}
}
void DumpMemory()
{
DumpRom();
DumpRam();
}
// Variante pour dumper une région spécifique de RAM
void DumpRamRegion(uint32_t start_addr, uint32_t size)
{
// Enlever le bit RAM si présent
uint32_t addr = start_addr & 0x7FFFFFFF;
if (ctx.ram.mem == nullptr || ctx.ram.size == 0) {
std::cout << "RAM is not allocated" << std::endl;
return;
}
if (addr >= ctx.ram.size) {
std::cout << "Start address 0x" << std::hex << addr
<< " is out of RAM bounds (size: 0x" << ctx.ram.size
<< ")" << std::dec << std::endl;
return;
}
uint32_t actual_size = std::min(size, ctx.ram.size - addr);
std::cout << "\n" << std::string(80, '=') << std::endl;
std::cout << "RAM Region Dump" << std::endl;
std::cout << "Address: 0x" << std::hex << (0x80000000 | addr)
<< " - 0x" << (0x80000000 | (addr + actual_size - 1))
<< std::dec << std::endl;
std::cout << std::string(80, '=') << std::endl;
HexDump(ctx.ram.mem + addr, actual_size, 0x80000000 | addr, "");
}
private:
std::vector<uint8_t> m_ram; // RAM storage
std::function<uint8_t(chip32_ctx_t*, uint8_t)> m_syscallHandler;
// Wrapper statique qui récupère l'instance et appelle la méthode membre

View file

@ -102,10 +102,8 @@ static uint32_t pop(chip32_ctx_t *ctx) {
// =======================================================================================
void chip32_initialize(chip32_ctx_t *ctx)
{
memset(ctx->ram.mem, 0, ctx->ram.size);
memset(ctx->registers, 0, REGISTER_COUNT * sizeof(uint32_t));
ctx->instr_count = 0;
ctx->registers[SP] = ctx->ram.size;
}
chip32_result_t chip32_run(chip32_ctx_t *ctx)
@ -225,29 +223,43 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
_CHECK_REGISTER_VALID(reg2)
// address is located in reg1 reg
uint32_t addr = ctx->registers[reg1];
bool isRam = addr & 0x80000000;
addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing
bool isRam = addr & CHIP32_RAM_OFFSET;
addr &= ~CHIP32_RAM_OFFSET; // mask the RAM/ROM bit, ensure 31-bit addressing
if (isRam) {
_CHECK_RAM_ADDR_VALID(addr)
memcpy(&ctx->ram.mem[addr], &ctx->registers[reg2], size);
} else {
_CHECK_ROM_ADDR_VALID(addr)
memcpy(&ctx->rom.mem[addr], &ctx->registers[reg2], size);
// Invalid, cannot write in memory!
return VM_ERR_INVALID_ADDRESS;
}
break;
}
case OP_LOAD:
{
const uint8_t reg1 = _NEXT_BYTE;
const uint8_t reg2 = _NEXT_BYTE;
const uint8_t size = _NEXT_BYTE;
_CHECK_REGISTER_VALID(reg1)
_CHECK_REGISTER_VALID(reg2)
uint8_t reg1 = _NEXT_BYTE;
uint32_t addr = 0;
uint8_t size = 0;
// Variable based
if (reg1 & 0x80)
{
reg1 &= 0x7F;
addr = _NEXT_INT(ctx);
size = _NEXT_BYTE;
}
// address is located in reg2 reg
uint32_t addr = ctx->registers[reg2];
bool isRam = addr & 0x80000000;
addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing
else
{
const uint8_t reg2 = _NEXT_BYTE;
const uint8_t size = _NEXT_BYTE;
_CHECK_REGISTER_VALID(reg1)
_CHECK_REGISTER_VALID(reg2)
addr = ctx->registers[reg2];
}
bool isRam = addr & CHIP32_RAM_OFFSET;
addr &= ~CHIP32_RAM_OFFSET; // mask the RAM/ROM bit, ensure 31-bit addressing
if (isRam) {
_CHECK_RAM_ADDR_VALID(addr)
memcpy(&ctx->registers[reg1], &ctx->ram.mem[addr], size);

View file

@ -35,6 +35,8 @@ extern "C" {
#include <stdbool.h>
#define CHIP32_RAM_OFFSET 0x80000000
// General form: instruction destination, source
// coded as: instr dst, src
typedef enum

View file

@ -5,14 +5,32 @@ project(core_tests LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# ============================================================================
# Téléchargement et configuration de Catch2 v3
# ============================================================================
include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.7.1 # Dernière version stable de Catch2 v3
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(Catch2)
# Ajouter le module Catch2 pour la découverte automatique des tests
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
# ============================================================================
# Exécutable de tests
# ============================================================================
add_executable(${PROJECT_NAME}
main.cpp
test_parser.cpp
test_vm.cpp
test_ast.cpp
test_print_node.cpp
../story-manager/src/nodes/base_node.cpp
../story-manager/src/nodes/branch_node.cpp
../story-manager/src/nodes/print_node.cpp
@ -25,12 +43,23 @@ add_executable(${PROJECT_NAME}
../chip32/chip32_binary_format.c
)
target_include_directories(${PROJECT_NAME} PRIVATE
target_include_directories(${PROJECT_NAME} PRIVATE
../chip32
../story-manager/src
../story-manager/src/nodes
../story-manager/src/compiler
../story-manager/interfaces
../../shared
)
# ============================================================================
# Linkage avec Catch2
# ============================================================================
target_link_libraries(${PROJECT_NAME} PRIVATE Catch2::Catch2WithMain)
# ============================================================================
# Découverte automatique des tests (optionnel mais recommandé)
# ============================================================================
include(CTest)
include(Catch)
catch_discover_tests(${PROJECT_NAME})

File diff suppressed because it is too large Load diff

View file

@ -25,7 +25,8 @@ THE SOFTWARE.
#include <iostream>
#include <thread>
#include "catch.hpp"
#include <catch2/catch_test_macros.hpp>
#include "chip32_assembler.h"
#include "chip32_macros.h"
#include "compiler.h"

View file

@ -0,0 +1,75 @@
// ============================================================================
// TEST 3: Macro language with DV/DZ
// ============================================================================
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 ; Initialize loop counter
%1_loop: ; Loop start label
%endmacro
%macro LOOP_END 2
subi %2, 1 ; Decrement counter
skipz %2
jump %1_loop ; Jump if not zero
%endmacro
%section_text
lcons R3, 4
incr R3
LOOP_START .myLoop, r6, 5
print $printHello, r3
LOOP_END .myLoop, r6
halt
%section_data
; ROM constant (DC)
$printHello DC8 "Answer is %d"
; RAM zeroed buffer (DZ)
$tempBuffer DZ8 32 ; 32 bytes buffer for temporary data
)";
TEST_CASE("Check assembly macro language with DV/DZ")
{
std::cout << "\n=== Test 3: Macros with DV/DZ ===" << std::endl;
Chip32::ScriptProcessor processor;
processor.process(testMacro1);
processor.generate_assembly();
std::string resultAsm = processor.GetResult();
std::cout << "Generated Assembly:" << std::endl;
std::cout << resultAsm << std::endl;
Chip32::Machine machine;
machine.QuickExecute(resultAsm);
REQUIRE(machine.parseResult == true);
REQUIRE(machine.buildResult == true);
REQUIRE(machine.runResult == VM_FINISHED);
REQUIRE(machine.ctx.registers[R3] == 5);
std::cout << "✓ Test 3 passed: Macros with DV/DZ" << std::endl;
}

View file

@ -24,7 +24,7 @@ THE SOFTWARE.
#include <iostream>
#include "catch.hpp"
#include <catch2/catch_test_macros.hpp>
#include "chip32_machine.h"
#include "chip32_binary_format.h"
@ -92,6 +92,9 @@ TEST_CASE("Check various indentations and typos with DV/DZ")
Chip32::Machine machine;
machine.QuickExecute(test1);
// Hex Dump the memories
machine.DumpMemory();
// Verify results
REQUIRE(machine.parseResult == true);
@ -131,7 +134,7 @@ $counter DV32 10 ; Counter initialized to 10
halt
)";
TEST_CASE("Test printf with DV variable")
TEST_CASE("Test printf with DV variable", "[printf][dv]")
{
std::cout << "\n=== Test 2: Printf with DV ===" << std::endl;
@ -145,81 +148,7 @@ TEST_CASE("Test printf with DV variable")
std::cout << "✓ Test 2 passed: Printf with DV" << std::endl;
}
// ============================================================================
// TEST 3: Macro language with DV/DZ
// ============================================================================
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 ; Initialize loop counter
%1_loop: ; Loop start label
%endmacro
%macro LOOP_END 2
subi %2, 1 ; Decrement counter
skipz %2
jump %1_loop ; Jump if not zero
%endmacro
%section_text
lcons R3, 4
incr R3
LOOP_START .myLoop, r6, 5
print $printHello, r3
LOOP_END .myLoop, r6
halt
%section_data
; ROM constant (DC)
$printHello DC8 "Answer is %d"
; RAM zeroed buffer (DZ)
$tempBuffer DZ8 32 ; 32 bytes buffer for temporary data
)";
TEST_CASE("Check assembly macro language with DV/DZ")
{
std::cout << "\n=== Test 3: Macros with DV/DZ ===" << std::endl;
Chip32::ScriptProcessor processor;
processor.process(testMacro1);
processor.generate_assembly();
std::string resultAsm = processor.GetResult();
std::cout << "Generated Assembly:" << std::endl;
std::cout << resultAsm << std::endl;
Chip32::Machine machine;
machine.QuickExecute(resultAsm);
REQUIRE(machine.parseResult == true);
REQUIRE(machine.buildResult == true);
REQUIRE(machine.runResult == VM_FINISHED);
REQUIRE(machine.ctx.registers[R3] == 5);
std::cout << "✓ Test 3 passed: Macros with DV/DZ" << std::endl;
}
// ============================================================================
// TEST 4: DV vs DZ comprehensive test
@ -333,40 +262,50 @@ TEST_CASE("Binary format validation")
{
std::cout << "\n=== Test 5: Binary format validation ===" << std::endl;
Chip32::Machine machine;
// Parse and build
Chip32::Assembler assembler;
Chip32::Result result;
chip32_binary_stats_t stats;
std::vector<uint8_t> program;
Chip32::Machine machine;
REQUIRE(assembler.Parse(testBinaryFormat) == true);
REQUIRE(assembler.BuildBinary(program, result) == true);
REQUIRE(machine.BuildBinary(assembler, program, stats) == true);
std::cout << "\nBinary statistics:" << std::endl;
result.Print();
chip32_binary_print_stats(&stats);
uint8_t ram[1024];
// Validate binary format
chip32_loaded_binary_t loaded;
chip32_binary_header_t header;
chip32_ctx_t ctx;
chip32_binary_error_t error = chip32_binary_load(
&ctx,
program.data(),
static_cast<uint32_t>(program.size()),
&loaded
ram,
sizeof(ram),
&header
);
REQUIRE(error == CHIP32_BIN_OK);
std::cout << "\nBinary header:" << std::endl;
chip32_binary_print_header(&loaded.header);
chip32_binary_print_header(&header);
// Verify header values
REQUIRE(loaded.header.magic == CHIP32_MAGIC);
REQUIRE(loaded.header.version == CHIP32_VERSION);
REQUIRE((loaded.header.flags & CHIP32_FLAG_HAS_INIT_DATA) != 0);
REQUIRE(loaded.header.const_size > 0); // Has ROM constants
REQUIRE(loaded.header.bss_size > 0); // Has RAM
REQUIRE(loaded.header.code_size > 0); // Has code
REQUIRE(loaded.header.init_data_size == loaded.header.bss_size); // Must match
REQUIRE(header.magic == CHIP32_MAGIC);
REQUIRE(header.version == CHIP32_VERSION);
REQUIRE((header.flags & CHIP32_FLAG_HAS_INIT_DATA) != 0);
REQUIRE(header.const_size > 0); // Has ROM constants
REQUIRE(header.bss_size > 0); // Has RAM
REQUIRE(header.code_size > 0); // Has code
REQUIRE(header.data_size == header.bss_size); // Must match
chip32_binary_build_stats(&header, &stats);
std::cout << "\nBinary format validations:" << std::endl;
std::cout << " ✓ Magic number correct" << std::endl;
@ -374,13 +313,7 @@ TEST_CASE("Binary format validation")
std::cout << " ✓ Has init data flag set" << std::endl;
std::cout << " ✓ All sections present" << std::endl;
std::cout << " ✓ INIT DATA size matches BSS size" << std::endl;
// Initialize RAM and verify
std::vector<uint8_t> ram(loaded.header.bss_size);
uint32_t init_bytes = chip32_binary_init_ram(&loaded, ram.data(), ram.size());
REQUIRE(init_bytes == loaded.header.bss_size);
// Verify DV ramCounter is initialized to 99
uint32_t ramCounter = *reinterpret_cast<uint32_t*>(&ram[0]);
std::cout << " ✓ DV ramCounter = " << ramCounter << " (expected: 99)" << std::endl;

View file

@ -1,5 +1,5 @@
// test_print_node.cpp
#include "catch.hpp"
#include <catch2/catch_test_macros.hpp>
#include "print_node.h"
#include "variable_node.h"
#include "function_entry_node.h"
@ -211,13 +211,7 @@ TEST_CASE("Print with 4 arguments - TAC", "[print][args][tac]") {
REQUIRE(assembly.find("; B") != std::string::npos);
REQUIRE(assembly.find("; C") != std::string::npos);
REQUIRE(assembly.find("; D") != std::string::npos);
// Verify format string conversion {0} → %d
REQUIRE(assembly.find("{0}") == std::string::npos);
REQUIRE(assembly.find("{1}") == std::string::npos);
REQUIRE(assembly.find("{2}") == std::string::npos);
REQUIRE(assembly.find("{3}") == std::string::npos);
// Verify syscall with 4 arguments
REQUIRE(assembly.find("lcons r1, 4") != std::string::npos);
REQUIRE(assembly.find("syscall 4") != std::string::npos);

View file

@ -23,7 +23,7 @@ THE SOFTWARE.
*/
#include <iostream>
#include "catch.hpp"
#include <catch2/catch_test_macros.hpp>
#include "chip32_machine.h" // Inclure chip32_machine.h au lieu de assembler et vm
/*