(WIP) new binary format
Some checks failed
Build-StoryEditor / build_win32 (push) Has been cancelled
Build-StoryEditor / build_linux (push) Has been cancelled

This commit is contained in:
anthony@rabine.fr 2025-10-12 20:39:32 +02:00
parent 9ab7b9bb14
commit c6da4b891a
10 changed files with 128 additions and 219 deletions

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": [],
"args": ["[vm]"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/core/tests/build",
"environment": [],

View file

@ -109,7 +109,9 @@
"serializers.h": "c",
"ni_parser.h": "c",
"*.m": "cpp",
"*.inc": "cpp"
"*.inc": "cpp",
"chip32_binary_format.h": "c",
"hash_map": "c"
}
}

View file

@ -365,7 +365,7 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
// ========================================================================
// PHASE 1: Créer les sections temporaires
// ========================================================================
std::vector<uint8_t> dataSection; // DC - ROM constants only
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)
@ -471,10 +471,10 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
if (!isDvInitData)
{
// C'est une vraie constante DC - va dans dataSection
// C'est une vraie constante DC - va dans constSection
std::copy(i.compiledArgs.begin(),
i.compiledArgs.end(),
std::back_inserter(dataSection));
std::back_inserter(constSection));
result.constantsSize += i.compiledArgs.size();
}
@ -511,7 +511,7 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
chip32_binary_header_t header;
chip32_binary_header_init(&header);
header.data_size = static_cast<uint32_t>(dataSection.size());
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;
@ -530,7 +530,7 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
uint32_t bytesWritten = chip32_binary_write(
&header,
dataSection.empty() ? nullptr : dataSection.data(),
constSection.empty() ? nullptr : constSection.data(),
codeSection.empty() ? nullptr : codeSection.data(),
initDataSection.empty() ? nullptr : initDataSection.data(),
program.data(),
@ -548,8 +548,8 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
// PHASE 7: Remplir les statistiques
// ========================================================================
result.ramUsageSize = bssSize;
result.romUsageSize = header.data_size + header.code_size;
result.constantsSize = header.data_size;
result.romUsageSize = header.const_size + header.code_size;
result.constantsSize = header.const_size;
return true;
}

View file

@ -13,94 +13,92 @@ _Static_assert(sizeof(chip32_binary_header_t) == 28, "Header must be 28 bytes");
// ============================================================================
chip32_binary_error_t chip32_binary_load(
chip32_ctx_t *ctx,
uint8_t* binary,
uint32_t size,
chip32_loaded_binary_t* out_loaded
uint32_t binary_size,
uint8_t* ram,
uint32_t ram_size,
chip32_binary_stats_t *out_stats
)
{
if (!binary || !out_loaded) {
memset(ctx, 0, sizeof(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;
}
// Clear output structure
memset(out_loaded, 0, sizeof(chip32_loaded_binary_t));
// Check minimum size
if (size < sizeof(chip32_binary_header_t)) {
out_loaded->error = CHIP32_BIN_ERR_TOO_SMALL;
if (binary_size < sizeof(chip32_binary_header_t)) {
return CHIP32_BIN_ERR_TOO_SMALL;
}
// Copy header
memcpy(&out_loaded->header, binary, sizeof(chip32_binary_header_t));
memcpy(&header, binary, sizeof(chip32_binary_header_t));
// Verify magic number
if (out_loaded->header.magic != CHIP32_MAGIC) {
out_loaded->error = CHIP32_BIN_ERR_INVALID_MAGIC;
if (header.magic != CHIP32_MAGIC) {
return CHIP32_BIN_ERR_INVALID_MAGIC;
}
// Check version
if (out_loaded->header.version > CHIP32_VERSION) {
out_loaded->error = CHIP32_BIN_ERR_UNSUPPORTED_VERSION;
if (header.version > CHIP32_VERSION) {
return CHIP32_BIN_ERR_UNSUPPORTED_VERSION;
}
// Calculate expected size
uint32_t expected_size = sizeof(chip32_binary_header_t) +
out_loaded->header.data_size +
out_loaded->header.code_size +
out_loaded->header.init_data_size;
header.const_size +
header.code_size +
header.init_data_size;
if (size != expected_size) {
out_loaded->error = CHIP32_BIN_ERR_SIZE_MISMATCH;
if (binary_size != expected_size) {
return CHIP32_BIN_ERR_SIZE_MISMATCH;
}
// Set section pointers
uint32_t offset = sizeof(chip32_binary_header_t);
if (out_loaded->header.data_size > 0) {
out_loaded->data_section = binary + offset;
offset += out_loaded->header.data_size;
}
// Skip header for ROM executable (must start at a valide code address)
ctx->rom.mem = binary + offset;
ctx->rom.size = binary_size - offset;
ctx->rom.addr = 0;
if (out_loaded->header.code_size > 0) {
out_loaded->code_section = binary + offset;
offset += out_loaded->header.code_size;
}
// RAM and ROM are in the same logical memory plane
// So we set it begin after the ROM (why not)
ctx->ram.mem = ram;
ctx->ram.addr = ctx->rom.size;
ctx->ram.size = ram_size;
if (out_loaded->header.init_data_size > 0) {
out_loaded->init_data_section = binary + offset;
}
// Set entry point (DATA size + entry point offset in CODE)
ctx->registers[PC] = header.entry_point;
out_loaded->error = CHIP32_BIN_OK;
return CHIP32_BIN_OK;
}
// Load data initialized values
const uint8_t *data = binary + header.code_size;
memcpy(ram, data, header.init_data_size);
void chip32_binary_get_stats(
const chip32_loaded_binary_t* loaded,
chip32_binary_stats_t* out_stats
)
{
if (!loaded || !out_stats) {
return;
}
out_stats->data_size = loaded->header.data_size;
out_stats->bss_size = loaded->header.bss_size;
out_stats->code_size = loaded->header.code_size;
out_stats->init_data_size = loaded->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) +
loaded->header.data_size +
loaded->header.code_size +
loaded->header.init_data_size;
header.const_size +
header.code_size +
header.init_data_size;
out_stats->total_rom_size = loaded->header.data_size +
loaded->header.code_size;
out_stats->total_rom_size = header.const_size +
header.code_size;
out_stats->total_ram_size = loaded->header.bss_size;
out_stats->total_ram_size = header.bss_size;
return CHIP32_BIN_OK;
}
const char* chip32_binary_error_string(chip32_binary_error_t error)
@ -146,7 +144,7 @@ uint32_t chip32_binary_calculate_size(const chip32_binary_header_t* header)
}
return sizeof(chip32_binary_header_t) +
header->data_size +
header->const_size +
header->code_size +
header->init_data_size;
}
@ -178,12 +176,12 @@ uint32_t chip32_binary_write(
offset += sizeof(chip32_binary_header_t);
// Write DATA section
if (header->data_size > 0) {
if (header->const_size > 0) {
if (!data_section) {
return 0; // Data expected but NULL pointer
}
memcpy(out_buffer + offset, data_section, header->data_size);
offset += header->data_size;
memcpy(out_buffer + offset, data_section, header->const_size);
offset += header->const_size;
}
// Write CODE section
@ -207,35 +205,6 @@ uint32_t chip32_binary_write(
return offset;
}
// ============================================================================
// RAM INITIALIZATION HELPER
// ============================================================================
uint32_t chip32_binary_init_ram(
const chip32_loaded_binary_t* loaded,
uint8_t* ram_buffer,
uint32_t ram_size
)
{
if (!loaded || !ram_buffer) {
return 0;
}
// Check if binary has init data
if (loaded->header.init_data_size == 0 || !loaded->init_data_section) {
return 0;
}
// Copy init data to RAM (respect buffer limits)
uint32_t copy_size = loaded->header.init_data_size;
if (copy_size > ram_size) {
copy_size = ram_size; // Truncate if RAM is smaller
}
memcpy(ram_buffer, loaded->init_data_section, copy_size);
return copy_size;
}
// ============================================================================
// DEBUG/UTILITY FUNCTIONS
@ -260,7 +229,7 @@ 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->data_size);
printf("DATA 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);
@ -275,7 +244,7 @@ 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->data_size);
printf("DATA section: %u bytes (ROM, initialized)\n", stats->const_size);
printf("BSS section: %u bytes (RAM, DV+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);

View file

@ -37,6 +37,24 @@
* - Initial RAM values: DV values + zeros for DZ areas
* - Size = sum of all DV and DZ declarations
* - Layout matches RAM layout exactly
*
*
+--------------------------------------+
| Chip32 Header | <-- Début du fichier
| (Magic, Versions, Sizes, Entry...) |
+--------------------------------------+
| CONST Section (DV) | <-- Données initialisées
| (Variables globales initialisées) | e.g., const int x = 5;
+--------------------------------------+
| CODE Section | <-- Instructions du programme
| (Les opcodes et leurs opérandes) |
+--------------------------------------+
| DATA Section (Optional) | <-- Données pour l'initialisation de la RAM (copie de DV)
| (Contient les valeurs initiales pour la RAM)
+--------------------------------------+
*/
#ifndef CHIP32_BINARY_FORMAT_H
@ -44,6 +62,7 @@
#include <stdint.h>
#include <string.h>
#include "chip32_vm.h"
#ifdef __cplusplus
extern "C" {
@ -74,7 +93,7 @@ typedef struct {
uint32_t magic; // Magic number "C32\0"
uint16_t version; // Format version
uint16_t flags; // Feature flags
uint32_t data_size; // Size of DATA section in bytes (ROM constants)
uint32_t const_size; // Size of DATA 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
@ -82,18 +101,9 @@ typedef struct {
} chip32_binary_header_t;
#pragma pack(pop)
// Loaded binary structure
typedef struct {
chip32_binary_header_t header;
uint8_t* data_section; // Points to DATA section (ROM)
uint8_t* code_section; // Points to CODE section
uint8_t* init_data_section; // Points to INIT DATA (RAM initialization)
chip32_binary_error_t error;
} chip32_loaded_binary_t;
// Statistics
typedef struct {
uint32_t data_size; // ROM constants
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
@ -114,21 +124,15 @@ typedef struct {
* @return Error code
*/
chip32_binary_error_t chip32_binary_load(
chip32_ctx_t *ctx,
uint8_t* binary,
uint32_t size,
chip32_loaded_binary_t* out_loaded
);
/**
* Get statistics from a loaded binary
* @param loaded Loaded binary structure
* @param out_stats Output statistics
*/
void chip32_binary_get_stats(
const chip32_loaded_binary_t* loaded,
uint32_t binary_size,
uint8_t* ram,
uint32_t ram_size,
chip32_binary_stats_t *out_stats
);
/**
* Get error string from error code
* @param error Error code
@ -156,7 +160,7 @@ 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 data_size is 0)
* @param data_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 out_buffer Output buffer (must be large enough)
@ -172,24 +176,6 @@ uint32_t chip32_binary_write(
uint32_t buffer_size
);
// ============================================================================
// RAM INITIALIZATION HELPER
// ============================================================================
/**
* Initialize RAM from binary INIT DATA section
* This copies all initial values (DV) and zeros (DZ) to RAM
* @param loaded Loaded binary with init data
* @param ram_buffer Destination RAM buffer
* @param ram_size Size of RAM buffer
* @return Number of bytes copied, or 0 if no init data
*/
uint32_t chip32_binary_init_ram(
const chip32_loaded_binary_t* loaded,
uint8_t* ram_buffer,
uint32_t ram_size
);
// ============================================================================
// DEBUG/UTILITY FUNCTIONS
// ============================================================================

View file

@ -34,6 +34,7 @@ public:
m_syscallHandler = std::bind(&Machine::HandleSyscall, this,
std::placeholders::_1,
std::placeholders::_2);
m_ram.resize(1024);
}
// ========================================================================
@ -64,12 +65,15 @@ public:
result.Print();
// Load binary using new format
chip32_loaded_binary_t loaded;
// 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()),
&loaded
m_ram.data(),
static_cast<uint32_t>(m_ram.size()),
&stats
);
if (error != CHIP32_BIN_OK) {
@ -78,29 +82,6 @@ public:
return;
}
// Allocate and initialize RAM
m_ram.resize(loaded.header.bss_size);
uint32_t init_bytes = chip32_binary_init_ram(&loaded, m_ram.data(), m_ram.size());
if (init_bytes > 0) {
std::cout << "RAM initialized: " << init_bytes << " bytes" << std::endl;
}
// Setup VM context
memset(&ctx, 0, sizeof(ctx));
ctx.stack_size = 512;
// ROM = DATA + CODE (contiguous in loaded binary)
ctx.rom.mem = loaded.data_section;
ctx.rom.addr = 0;
ctx.rom.size = loaded.header.data_size + loaded.header.code_size;
// RAM
ctx.ram.mem = m_ram.data();
ctx.ram.addr = ctx.rom.size;
ctx.ram.size = m_ram.size();
// Set syscall handler using wrapper
ctx.syscall = SyscallWrapper;
ctx.user_data = this;
@ -108,8 +89,6 @@ public:
// Initialize VM
chip32_initialize(&ctx);
// Set entry point (DATA size + entry point offset in CODE)
ctx.registers[PC] = loaded.header.data_size + loaded.header.entry_point;
std::cout << "Starting execution at PC=0x" << std::hex << ctx.registers[PC]
<< std::dec << std::endl;
@ -281,13 +260,6 @@ public:
uint8_t *ram_buffer,
uint32_t ram_size)
{
// Charger et valider le binaire
chip32_loaded_binary_t loaded;
chip32_binary_error_t err = chip32_binary_load(
binary.data(),
static_cast<uint32_t>(binary.size()),
&loaded
);
if (err != CHIP32_BIN_OK)
{

View file

@ -104,14 +104,14 @@ 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->instrCount = 0;
ctx->instr_count = 0;
ctx->registers[SP] = ctx->ram.size;
}
chip32_result_t chip32_run(chip32_ctx_t *ctx)
{
chip32_result_t result = VM_OK;
while ((ctx->max_instr == 0) || (ctx->instrCount < ctx->max_instr))
while ((ctx->max_instr == 0) || (ctx->instr_count < ctx->max_instr))
{
result = chip32_step(ctx);
@ -434,7 +434,7 @@ chip32_result_t chip32_step(chip32_ctx_t *ctx)
}
ctx->registers[PC]++;
ctx->instrCount++;
ctx->instr_count++;
return result;
}

View file

@ -208,7 +208,7 @@ struct chip32_ctx_t
virtual_mem_t rom;
virtual_mem_t ram;
uint16_t stack_size;
uint32_t instrCount;
uint32_t instr_count;
uint16_t prog_size;
uint32_t max_instr;
uint32_t registers[REGISTER_COUNT];

View file

@ -363,7 +363,7 @@ TEST_CASE("Binary format validation")
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.data_size > 0); // Has ROM constants
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

View file

@ -24,59 +24,37 @@ THE SOFTWARE.
#include <iostream>
#include "catch.hpp"
#include "chip32_assembler.h"
#include "chip32_vm.h"
#include "chip32_machine.h" // Inclure chip32_machine.h au lieu de assembler et vm
/*
Purpose: test all opcodes
*/
void hexdump(void *ptr, int buflen);
static uint8_t story_player_syscall(chip32_ctx_t *ctx, uint8_t code)
{
uint8_t retCode = SYSCALL_RET_OK;
return retCode;
}
// Suppression des fonctions et classes de configuration de la VM
// qui sont maintenant encapsulées dans Chip32::Machine.
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.
}
void Execute(const std::string &assemblyCode)
{
// --------- BUILD BINARY ---------
REQUIRE( assembler.Parse(assemblyCode) == true );
REQUIRE( assembler.BuildBinary(program, result) == true );
result.Print();
// Utiliser la méthode QuickExecute de Machine pour Parse, Build et Run
machine.QuickExecute(assemblyCode);
chip32_ctx.stack_size = 512;
// Vérification de base: le parsing et le build doivent réussir
REQUIRE( machine.parseResult == true );
REQUIRE( machine.buildResult == true );
chip32_ctx.rom.mem = program.data();
chip32_ctx.rom.addr = 18*1024;
chip32_ctx.rom.size = program.size();
chip32_ctx.ram.mem = data;
chip32_ctx.ram.addr = 56 *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 );
// Vérification que l'exécution a fini normalement (HALT)
REQUIRE( machine.runResult == VM_FINISHED );
}
uint8_t data[8*1024];
std::vector<uint8_t> program;
Chip32::Assembler assembler;
Chip32::Result result;
chip32_ctx_t chip32_ctx;
Chip32::Machine machine; // Instance de la Machine à utiliser pour les tests
};
@ -89,7 +67,8 @@ TEST_CASE_METHOD(VmTestContext, "MUL", "[vm]") {
)";
Execute(test1);
uint32_t result = chip32_ctx.registers[R0];
// 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);
}
@ -102,6 +81,7 @@ TEST_CASE_METHOD(VmTestContext, "DIV", "[vm]") {
)";
Execute(test1);
uint32_t result = chip32_ctx.registers[R0];
// 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));
}