mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
Add DATA and BSS RAM sections, crate binary format with header
This commit is contained in:
parent
741a3c633e
commit
9ab7b9bb14
9 changed files with 1261 additions and 363 deletions
|
|
@ -24,6 +24,7 @@ THE SOFTWARE.
|
|||
|
||||
|
||||
#include "chip32_assembler.h"
|
||||
#include "chip32_binary_format.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
|
@ -32,6 +33,7 @@ THE SOFTWARE.
|
|||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
namespace Chip32
|
||||
{
|
||||
|
|
@ -358,29 +360,197 @@ bool Assembler::CompileConstantArgument(Instr &instr, const std::string &a)
|
|||
bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
|
||||
{
|
||||
program.clear();
|
||||
result = { 0, 0, 0}; // clear stuff!
|
||||
result = { 0, 0, 0};
|
||||
|
||||
// serialize each instruction and arguments to program memory, assign address to variables (rom or ram)
|
||||
for (auto &i : m_instructions)
|
||||
// ========================================================================
|
||||
// PHASE 1: Créer les sections temporaires
|
||||
// ========================================================================
|
||||
std::vector<uint8_t> dataSection; // 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++)
|
||||
{
|
||||
if (i.isRamData)
|
||||
const auto &instr = m_instructions[idx];
|
||||
|
||||
// Détecter une variable DV (le marqueur final)
|
||||
if (instr.isRamData && !instr.isZeroData && instr.mnemonic[0] == '$')
|
||||
{
|
||||
result.ramUsageSize += i.dataLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i.isRomCode())
|
||||
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++)
|
||||
{
|
||||
program.push_back(i.code.opcode);
|
||||
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());
|
||||
}
|
||||
}
|
||||
else if (i.isRomData) // Seulement pour ROM DATA
|
||||
{
|
||||
result.constantsSize += i.compiledArgs.size();
|
||||
}
|
||||
std::copy (i.compiledArgs.begin(), i.compiledArgs.end(), std::back_inserter(program));
|
||||
|
||||
dvInitData[ramAddr] = initData;
|
||||
}
|
||||
}
|
||||
result.romUsageSize = program.size();
|
||||
|
||||
// ========================================================================
|
||||
// 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 dataSection
|
||||
std::copy(i.compiledArgs.begin(),
|
||||
i.compiledArgs.end(),
|
||||
std::back_inserter(dataSection));
|
||||
|
||||
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.data_size = static_cast<uint32_t>(dataSection.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,
|
||||
dataSection.empty() ? nullptr : dataSection.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.data_size + header.code_size;
|
||||
result.constantsSize = header.data_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -474,44 +644,97 @@ bool Assembler::Parse(const std::string &data)
|
|||
std::string type = lineParts[1];
|
||||
|
||||
CHIP32_CHECK(instr, (type.size() >= 3), "bad data type size");
|
||||
CHIP32_CHECK(instr, (type[0] == 'D') && ((type[1] == 'C') || (type[1] == 'V')), "bad data type (must be DCxx or DVxx");
|
||||
CHIP32_CHECK(instr, (type[0] == 'D') && ((type[1] == 'C') || (type[1] == 'V') || (type[1] == 'Z')),
|
||||
"bad data type (must be DCxx, DVxx or DZxx)");
|
||||
CHIP32_CHECK(instr, m_labels.count(opcode) == 0, "duplicated label : " + opcode);
|
||||
|
||||
instr.isRomData = type[1] == 'C' ? true : false;
|
||||
instr.isRamData = type[1] == 'V' ? true : false;
|
||||
// Parse data type size (8, 16, or 32)
|
||||
type.erase(0, 2);
|
||||
instr.dataTypeSize = static_cast<uint32_t>(strtol(type.c_str(), NULL, 0));
|
||||
instr.dataTypeSize = static_cast<uint32_t>(strtol(type.c_str(), NULL, 0));
|
||||
|
||||
// Determine data type
|
||||
char typeChar = lineParts[1][1];
|
||||
instr.isRomData = (typeChar == 'C');
|
||||
instr.isRamData = (typeChar == 'V' || typeChar == 'Z');
|
||||
instr.isZeroData = (typeChar == 'Z');
|
||||
|
||||
// =======================================================================================
|
||||
// DC - ROM Constants (read-only data in program memory)
|
||||
// =======================================================================================
|
||||
if (instr.isRomData)
|
||||
{
|
||||
instr.addr = code_addr;
|
||||
m_labels[opcode] = instr; // location of the start of the data
|
||||
// if ROM data, we generate one instruction per argument
|
||||
// reason: arguments may be labels, easier to replace later
|
||||
|
||||
// Generate one instruction per argument
|
||||
// Reason: arguments may be labels, easier to replace later
|
||||
for (unsigned int i = 2; i < lineParts.size(); i++)
|
||||
{
|
||||
CHIP32_CHECK(instr, CompileConstantArgument(instr, lineParts[i]), "Compile argument error, stopping.");
|
||||
CHIP32_CHECK(instr, CompileConstantArgument(instr, lineParts[i]),
|
||||
"Compile argument error, stopping.");
|
||||
m_instructions.push_back(instr);
|
||||
code_addr += instr.compiledArgs.size();
|
||||
instr.addr = code_addr;
|
||||
}
|
||||
}
|
||||
else // RAM DATA, only one argument is used: the size of the array
|
||||
// =======================================================================================
|
||||
// DV - RAM Variables with initial values (data stored in ROM, copied to RAM at startup)
|
||||
// =======================================================================================
|
||||
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
|
||||
|
||||
instr.addr = ram_addr;
|
||||
instr.dataLen = static_cast<uint16_t>(strtol(lineParts[2].c_str(), NULL, 0)) * instr.dataTypeSize/8;
|
||||
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]),
|
||||
"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();
|
||||
}
|
||||
|
||||
// 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)
|
||||
// =======================================================================================
|
||||
else // DZ
|
||||
{
|
||||
// DZ only takes ONE argument: the number of elements
|
||||
CHIP32_CHECK(instr, lineParts.size() == 3,
|
||||
"DZ directive requires exactly one argument (number of elements)");
|
||||
|
||||
instr.addr = 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;
|
||||
m_labels[opcode] = instr;
|
||||
m_instructions.push_back(instr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lastError.message = "Unknown mnemonic or badly formatted line";
|
||||
m_lastError.line = lineNum;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Second pass: replace all label or RAM data by the real address in memory
|
||||
|
|
|
|||
|
|
@ -50,12 +50,14 @@ struct Instr {
|
|||
uint16_t dataTypeSize{0};
|
||||
uint16_t dataLen{0};
|
||||
|
||||
bool isLabel{false}; //!< If true, this is a label, otherwise it is an instruction
|
||||
bool useLabel{false}; //!< If true, the instruction uses a label
|
||||
bool isRomData{false}; //!< True is constant data in program
|
||||
bool isRamData{false}; //!< True is constant data in program
|
||||
bool isLabel{false}; //!< If true, this is a label, otherwise it is an instruction
|
||||
bool useLabel{false}; //!< If true, the instruction uses a label
|
||||
bool isRomData{false}; //!< True if constant data in ROM (DC)
|
||||
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 address when assembled in program memory
|
||||
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
|
||||
|
||||
bool isRomCode() const { return !(isLabel || isRomData || isRamData); }
|
||||
};
|
||||
|
|
|
|||
287
core/chip32/chip32_binary_format.c
Normal file
287
core/chip32/chip32_binary_format.c
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* Chip32 Binary Format - Implementation
|
||||
*/
|
||||
|
||||
#include "chip32_binary_format.h"
|
||||
#include <stdio.h>
|
||||
|
||||
// Verify header size at compile time
|
||||
_Static_assert(sizeof(chip32_binary_header_t) == 28, "Header must be 28 bytes");
|
||||
|
||||
// ============================================================================
|
||||
// LOADING FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
chip32_binary_error_t chip32_binary_load(
|
||||
uint8_t* binary,
|
||||
uint32_t size,
|
||||
chip32_loaded_binary_t* out_loaded
|
||||
)
|
||||
{
|
||||
if (!binary || !out_loaded) {
|
||||
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;
|
||||
return CHIP32_BIN_ERR_TOO_SMALL;
|
||||
}
|
||||
|
||||
// Copy header
|
||||
memcpy(&out_loaded->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;
|
||||
return CHIP32_BIN_ERR_INVALID_MAGIC;
|
||||
}
|
||||
|
||||
// Check version
|
||||
if (out_loaded->header.version > CHIP32_VERSION) {
|
||||
out_loaded->error = CHIP32_BIN_ERR_UNSUPPORTED_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;
|
||||
|
||||
if (size != expected_size) {
|
||||
out_loaded->error = CHIP32_BIN_ERR_SIZE_MISMATCH;
|
||||
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;
|
||||
}
|
||||
|
||||
if (out_loaded->header.code_size > 0) {
|
||||
out_loaded->code_section = binary + offset;
|
||||
offset += out_loaded->header.code_size;
|
||||
}
|
||||
|
||||
if (out_loaded->header.init_data_size > 0) {
|
||||
out_loaded->init_data_section = binary + offset;
|
||||
}
|
||||
|
||||
out_loaded->error = CHIP32_BIN_OK;
|
||||
return CHIP32_BIN_OK;
|
||||
}
|
||||
|
||||
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->total_file_size = sizeof(chip32_binary_header_t) +
|
||||
loaded->header.data_size +
|
||||
loaded->header.code_size +
|
||||
loaded->header.init_data_size;
|
||||
|
||||
out_stats->total_rom_size = loaded->header.data_size +
|
||||
loaded->header.code_size;
|
||||
|
||||
out_stats->total_ram_size = loaded->header.bss_size;
|
||||
}
|
||||
|
||||
const char* chip32_binary_error_string(chip32_binary_error_t error)
|
||||
{
|
||||
switch (error) {
|
||||
case CHIP32_BIN_OK:
|
||||
return "No error";
|
||||
case CHIP32_BIN_ERR_TOO_SMALL:
|
||||
return "Binary too small (less than header size)";
|
||||
case CHIP32_BIN_ERR_INVALID_MAGIC:
|
||||
return "Invalid magic number (not a Chip32 binary)";
|
||||
case CHIP32_BIN_ERR_UNSUPPORTED_VERSION:
|
||||
return "Unsupported binary version";
|
||||
case CHIP32_BIN_ERR_SIZE_MISMATCH:
|
||||
return "Binary size mismatch (corrupted file?)";
|
||||
case CHIP32_BIN_ERR_NULL_POINTER:
|
||||
return "NULL pointer argument";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BUILDING FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
void chip32_binary_header_init(chip32_binary_header_t* header)
|
||||
{
|
||||
if (!header) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(header, 0, sizeof(chip32_binary_header_t));
|
||||
header->magic = CHIP32_MAGIC;
|
||||
header->version = CHIP32_VERSION;
|
||||
header->flags = 0;
|
||||
}
|
||||
|
||||
uint32_t chip32_binary_calculate_size(const chip32_binary_header_t* header)
|
||||
{
|
||||
if (!header) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sizeof(chip32_binary_header_t) +
|
||||
header->data_size +
|
||||
header->code_size +
|
||||
header->init_data_size;
|
||||
}
|
||||
|
||||
uint32_t chip32_binary_write(
|
||||
const chip32_binary_header_t* header,
|
||||
const uint8_t* data_section,
|
||||
const uint8_t* code_section,
|
||||
const uint8_t* init_data_section,
|
||||
uint8_t* out_buffer,
|
||||
uint32_t buffer_size
|
||||
)
|
||||
{
|
||||
if (!header || !out_buffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate required size
|
||||
uint32_t required_size = chip32_binary_calculate_size(header);
|
||||
|
||||
if (buffer_size < required_size) {
|
||||
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);
|
||||
|
||||
// Write DATA section
|
||||
if (header->data_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;
|
||||
}
|
||||
|
||||
// Write CODE section
|
||||
if (header->code_size > 0) {
|
||||
if (!code_section) {
|
||||
return 0; // Code expected but NULL pointer
|
||||
}
|
||||
memcpy(out_buffer + offset, code_section, header->code_size);
|
||||
offset += header->code_size;
|
||||
}
|
||||
|
||||
// Write INIT DATA section
|
||||
if (header->init_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;
|
||||
}
|
||||
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
void chip32_binary_print_header(const chip32_binary_header_t* header)
|
||||
{
|
||||
if (!header) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("=== Chip32 Binary Header ===\n");
|
||||
printf("Magic: 0x%08X", header->magic);
|
||||
if (header->magic == CHIP32_MAGIC) {
|
||||
printf(" (valid)\n");
|
||||
} else {
|
||||
printf(" (INVALID!)\n");
|
||||
}
|
||||
printf("Version: %u\n", header->version);
|
||||
printf("Flags: 0x%04X", header->flags);
|
||||
if (header->flags & CHIP32_FLAG_HAS_INIT_DATA) {
|
||||
printf(" (has init data)");
|
||||
}
|
||||
printf("\n");
|
||||
printf("DATA section: %u bytes (ROM constants)\n", header->data_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("\n");
|
||||
}
|
||||
|
||||
void chip32_binary_print_stats(const chip32_binary_stats_t* stats)
|
||||
{
|
||||
if (!stats) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("=== Chip32 Binary Statistics ===\n");
|
||||
printf("DATA section: %u bytes (ROM, initialized)\n", stats->data_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);
|
||||
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");
|
||||
}
|
||||
213
core/chip32/chip32_binary_format.h
Normal file
213
core/chip32/chip32_binary_format.h
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Chip32 Binary Format - Pure C Implementation
|
||||
* Compatible with embedded systems
|
||||
*
|
||||
* Variable types:
|
||||
* DC8, DC32 : ROM constants (initialized data in ROM)
|
||||
* DV8, DV32 : RAM variables with initial value
|
||||
* DZ8, DZ32 : RAM zero-initialized arrays/buffers
|
||||
*
|
||||
* Examples:
|
||||
* $romString DC8 "Hello" ; String in ROM
|
||||
* $romValue DC32 42 ; Constant in ROM
|
||||
* $ramCounter DV32 100 ; RAM variable initialized to 100
|
||||
* $ramMessage DV8 "Test" ; RAM string initialized to "Test"
|
||||
* $ramBuffer DZ8 256 ; RAM buffer of 256 bytes (zeroed)
|
||||
* $ramArray DZ32 100 ; RAM array of 100 x 32-bit (zeroed)
|
||||
*
|
||||
* Binary file structure:
|
||||
*
|
||||
* [HEADER - 28 bytes]
|
||||
* - Magic number: "C32\0" (4 bytes)
|
||||
* - Version: uint16_t (2 bytes)
|
||||
* - Flags: uint16_t (2 bytes)
|
||||
* - Data section size: uint32_t (4 bytes)
|
||||
* - BSS section size: uint32_t (4 bytes)
|
||||
* - Code section size: uint32_t (4 bytes)
|
||||
* - Entry point: uint32_t (4 bytes)
|
||||
* - Init data size: uint32_t (4 bytes)
|
||||
*
|
||||
* [DATA SECTION]
|
||||
* - ROM constants (DC8, DC32, etc.)
|
||||
*
|
||||
* [CODE SECTION]
|
||||
* - Executable instructions
|
||||
*
|
||||
* [INIT DATA SECTION]
|
||||
* - Initial RAM values: DV values + zeros for DZ areas
|
||||
* - Size = sum of all DV and DZ declarations
|
||||
* - Layout matches RAM layout exactly
|
||||
*/
|
||||
|
||||
#ifndef CHIP32_BINARY_FORMAT_H
|
||||
#define CHIP32_BINARY_FORMAT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Magic number: "C32\0" in little-endian
|
||||
#define CHIP32_MAGIC 0x00323343
|
||||
|
||||
// Current version
|
||||
#define CHIP32_VERSION 1
|
||||
|
||||
// Flags
|
||||
#define CHIP32_FLAG_HAS_INIT_DATA 0x0001 // Binary contains RAM init data
|
||||
|
||||
// Error codes
|
||||
typedef enum {
|
||||
CHIP32_BIN_OK = 0,
|
||||
CHIP32_BIN_ERR_TOO_SMALL,
|
||||
CHIP32_BIN_ERR_INVALID_MAGIC,
|
||||
CHIP32_BIN_ERR_UNSUPPORTED_VERSION,
|
||||
CHIP32_BIN_ERR_SIZE_MISMATCH,
|
||||
CHIP32_BIN_ERR_NULL_POINTER
|
||||
} chip32_binary_error_t;
|
||||
|
||||
// Binary header structure (28 bytes, packed)
|
||||
#pragma pack(push, 1)
|
||||
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 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)
|
||||
} 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 bss_size; // Total RAM needed
|
||||
uint32_t code_size; // Executable code
|
||||
uint32_t init_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
|
||||
} chip32_binary_stats_t;
|
||||
|
||||
// ============================================================================
|
||||
// LOADING FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Load and validate a Chip32 binary
|
||||
* @param binary Pointer to binary data
|
||||
* @param size Size of binary data in bytes
|
||||
* @param out_loaded Output structure (filled on success)
|
||||
* @return Error code
|
||||
*/
|
||||
chip32_binary_error_t chip32_binary_load(
|
||||
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,
|
||||
chip32_binary_stats_t* out_stats
|
||||
);
|
||||
|
||||
/**
|
||||
* Get error string from error code
|
||||
* @param error Error code
|
||||
* @return Human-readable error message
|
||||
*/
|
||||
const char* chip32_binary_error_string(chip32_binary_error_t error);
|
||||
|
||||
// ============================================================================
|
||||
// BUILDING FUNCTIONS (for assembler/compiler)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Initialize a binary header
|
||||
* @param header Header to initialize
|
||||
*/
|
||||
void chip32_binary_header_init(chip32_binary_header_t* header);
|
||||
|
||||
/**
|
||||
* Calculate total binary size from header
|
||||
* @param header Binary header
|
||||
* @return Total size in bytes
|
||||
*/
|
||||
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 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)
|
||||
* @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* code_section,
|
||||
const uint8_t* init_data_section,
|
||||
uint8_t* out_buffer,
|
||||
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
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Print binary header information
|
||||
* @param header Binary header
|
||||
*/
|
||||
void chip32_binary_print_header(const chip32_binary_header_t* header);
|
||||
|
||||
/**
|
||||
* Print binary statistics
|
||||
* @param stats Binary statistics
|
||||
*/
|
||||
void chip32_binary_print_stats(const chip32_binary_stats_t* stats);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CHIP32_BINARY_FORMAT_H
|
||||
|
|
@ -1,17 +1,22 @@
|
|||
/*
|
||||
* Chip32 Machine - VM wrapper with binary format support
|
||||
* Updated to use chip32_binary_format for proper DV/DZ handling
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
#include <cstring>
|
||||
|
||||
#include "chip32_assembler.h"
|
||||
#include "chip32_binary_format.h"
|
||||
#include "chip32_macros.h"
|
||||
|
||||
// Dans chip32_machine.h
|
||||
|
||||
namespace Chip32
|
||||
{
|
||||
|
||||
|
|
@ -22,8 +27,7 @@ public:
|
|||
bool buildResult{false};
|
||||
chip32_result_t runResult{VM_OK};
|
||||
std::string printOutput;
|
||||
|
||||
static Machine *m_instance;
|
||||
chip32_ctx_t ctx; // Public pour accès aux registres dans les tests
|
||||
|
||||
Machine() {
|
||||
// Bind syscall handler to this instance
|
||||
|
|
@ -32,7 +36,94 @@ public:
|
|||
std::placeholders::_2);
|
||||
}
|
||||
|
||||
// Lecture d'une chaîne depuis la mémoire (non statique maintenant)
|
||||
// ========================================================================
|
||||
// Méthode principale : Parse, Build, Execute
|
||||
// ========================================================================
|
||||
|
||||
void QuickExecute(const std::string &assemblyCode)
|
||||
{
|
||||
std::vector<uint8_t> program;
|
||||
Chip32::Assembler assembler;
|
||||
Chip32::Result result;
|
||||
|
||||
// Parse
|
||||
parseResult = assembler.Parse(assemblyCode);
|
||||
|
||||
if (!parseResult) {
|
||||
std::cout << "Parse error: " << assembler.GetLastError().ToString() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Build binary with new format
|
||||
buildResult = assembler.BuildBinary(program, result);
|
||||
|
||||
if (!buildResult) {
|
||||
std::cout << "Build error: " << assembler.GetLastError().ToString() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
result.Print();
|
||||
|
||||
// Load binary using new format
|
||||
chip32_loaded_binary_t loaded;
|
||||
chip32_binary_error_t error = chip32_binary_load(
|
||||
program.data(),
|
||||
static_cast<uint32_t>(program.size()),
|
||||
&loaded
|
||||
);
|
||||
|
||||
if (error != CHIP32_BIN_OK) {
|
||||
std::cout << "Binary load error: " << chip32_binary_error_string(error) << std::endl;
|
||||
buildResult = false;
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
// Run
|
||||
runResult = chip32_run(&ctx);
|
||||
|
||||
std::cout << "Execution finished with result: " << runResult << std::endl;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Helper functions
|
||||
// ========================================================================
|
||||
|
||||
static std::string GetStringFromMemory(chip32_ctx_t *ctx, uint32_t addr)
|
||||
{
|
||||
if (!ctx) {
|
||||
|
|
@ -91,72 +182,41 @@ public:
|
|||
|
||||
// Vérifier si on a assez d'arguments
|
||||
if (argIndex >= static_cast<int>(args.size())) {
|
||||
result << "{" << argIndex << ":?}"; // Argument manquant
|
||||
pos += 2;
|
||||
result << "{" << argIndex << ":?}";
|
||||
pos += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t argValue = args[argIndex];
|
||||
// Vérifier le type (i ou s)
|
||||
if (pos + 2 < format.length() && format[pos + 2] == ':') {
|
||||
if (pos + 3 < format.length()) {
|
||||
char typeChar = format[pos + 3];
|
||||
uint32_t argValue = args[argIndex];
|
||||
|
||||
// Vérifier s'il y a un type spécifié {:d}, {:s}, {:f}, {:x}
|
||||
if (pos + 3 < format.length() && format[pos + 2] == ':') {
|
||||
char typeChar = format[pos + 3];
|
||||
|
||||
// Vérifier si le placeholder se termine bien par '}'
|
||||
if (pos + 4 < format.length() && format[pos + 4] == '}') {
|
||||
// Parser le type et formater
|
||||
switch (typeChar) {
|
||||
case 'd': // Entier décimal signé
|
||||
case 'i':
|
||||
result << static_cast<int32_t>(argValue);
|
||||
break;
|
||||
|
||||
case 'u': // Entier non signé
|
||||
result << argValue;
|
||||
break;
|
||||
|
||||
case 'x': // Hexadécimal minuscule
|
||||
result << "0x" << std::hex << argValue << std::dec;
|
||||
break;
|
||||
|
||||
case 'X': // Hexadécimal majuscule
|
||||
result << "0x" << std::hex << std::uppercase
|
||||
<< argValue << std::nouppercase << std::dec;
|
||||
break;
|
||||
|
||||
case 's': // String (adresse)
|
||||
try {
|
||||
result << GetStringFromMemory(ctx, argValue);
|
||||
} catch (const std::exception& e) {
|
||||
result << "<error:0x" << std::hex << argValue << std::dec << ">";
|
||||
}
|
||||
break;
|
||||
|
||||
case 'f': // Float
|
||||
{
|
||||
float floatValue;
|
||||
std::memcpy(&floatValue, &argValue, sizeof(float));
|
||||
result << floatValue;
|
||||
break;
|
||||
if (typeChar == 's') {
|
||||
// String: argValue est une adresse
|
||||
try {
|
||||
std::string str = GetStringFromMemory(ctx, argValue);
|
||||
result << str;
|
||||
pos += 5; // Avancer de "{0:s}"
|
||||
continue;
|
||||
} catch (const std::exception& e) {
|
||||
result << "{str_error}";
|
||||
pos += 5;
|
||||
continue;
|
||||
}
|
||||
|
||||
case 'c': // Caractère
|
||||
result << static_cast<char>(argValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Type inconnu, afficher tel quel
|
||||
result << "{" << argIndex << ":" << typeChar << "}";
|
||||
} else if (typeChar == 'i' || typeChar == 'd') {
|
||||
// Integer
|
||||
result << static_cast<int32_t>(argValue);
|
||||
pos += 5;
|
||||
continue;
|
||||
}
|
||||
|
||||
pos += 5; // Avancer de "{0:d}"
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Format court {0} sans type → défaut: entier
|
||||
else if (pos + 2 < format.length() && format[pos + 2] == '}') {
|
||||
} else if (pos + 2 < format.length() && format[pos + 2] == '}') {
|
||||
// Format simple {0} - traiter comme int par défaut
|
||||
uint32_t argValue = args[argIndex];
|
||||
result << static_cast<int32_t>(argValue);
|
||||
pos += 3; // Avancer de "{0}"
|
||||
pos += 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -170,7 +230,10 @@ public:
|
|||
return result.str();
|
||||
}
|
||||
|
||||
// Handler de syscall (méthode membre, non statique)
|
||||
// ========================================================================
|
||||
// Syscall handler
|
||||
// ========================================================================
|
||||
|
||||
uint8_t HandleSyscall(chip32_ctx_t *ctx, uint8_t code)
|
||||
{
|
||||
try {
|
||||
|
|
@ -207,44 +270,84 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void QuickExecute(const std::string &assemblyCode)
|
||||
// ============================================================================
|
||||
// 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)
|
||||
{
|
||||
std::vector<uint8_t> program;
|
||||
Chip32::Assembler assembler;
|
||||
Chip32::Result result;
|
||||
std::vector<uint8_t> data(8*1024);
|
||||
// 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
|
||||
);
|
||||
|
||||
parseResult = assembler.Parse(assemblyCode);
|
||||
std::cout << assembler.GetLastError().ToString() << std::endl;
|
||||
|
||||
buildResult = assembler.BuildBinary(program, result);
|
||||
result.Print();
|
||||
|
||||
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.data();
|
||||
chip32_ctx.ram.addr = 40 * 1024;
|
||||
chip32_ctx.ram.size = data.size();
|
||||
|
||||
// Utiliser le wrapper statique qui appelle notre fonction membre
|
||||
chip32_ctx.syscall = SyscallWrapper;
|
||||
chip32_ctx.user_data = this; // Stocker le pointeur vers cette instance
|
||||
|
||||
chip32_initialize(&chip32_ctx);
|
||||
|
||||
Instr mainLine;
|
||||
if (assembler.GetMain(mainLine)) {
|
||||
chip32_ctx.registers[PC] = mainLine.addr;
|
||||
if (err != CHIP32_BIN_OK)
|
||||
{
|
||||
std::cerr << "Binary load error: " << chip32_binary_error_string(err) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
runResult = chip32_run(&chip32_ctx);
|
||||
// Afficher les informations du binaire (debug)
|
||||
chip32_binary_print_header(&loaded.header);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 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;
|
||||
}
|
||||
|
||||
private:
|
||||
// std::function contenant le bind
|
||||
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
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@ bool StoryProject::GenerateCompleteProgram(std::string &assembly)
|
|||
}
|
||||
}
|
||||
|
||||
// ✅ PHASE 2 : GÉNÉRATION DE TOUS LES TAC (avant la section DATA!)
|
||||
// PHASE 2 : GÉNÉRATION DE TOUS LES TAC (avant la section DATA!)
|
||||
std::cout << "\n=== Generating all TAC programs ===\n";
|
||||
std::map<std::string, TACProgram> pageTACPrograms;
|
||||
|
||||
|
|
@ -491,7 +491,7 @@ bool StoryProject::GenerateCompleteProgram(std::string &assembly)
|
|||
generator.GenerateGlobalVariables();
|
||||
|
||||
// Constantes de tous les nœuds de toutes les pages
|
||||
// ✅ Les format strings ont déjà été modifiés par le TAC generator
|
||||
// Les format strings ont déjà été modifiés par le TAC generator
|
||||
generator.GenerateNodesVariables(allNodes);
|
||||
|
||||
// === SECTION TEXT (chaque page = une fonction) ===
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ add_executable(${PROJECT_NAME}
|
|||
|
||||
../chip32/chip32_assembler.cpp
|
||||
../chip32/chip32_vm.c
|
||||
../chip32/chip32_binary_format.c
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
|
|
|
|||
|
|
@ -23,194 +23,133 @@ THE SOFTWARE.
|
|||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "chip32_assembler.h"
|
||||
#include "chip32_macros.h"
|
||||
#include "chip32_machine.h"
|
||||
#include "chip32_binary_format.h"
|
||||
|
||||
/*
|
||||
Purpose: grammar, ram usage and macros, rom code generation
|
||||
Tests updated with new DV/DZ syntax:
|
||||
- DV8, DV32 : RAM variables WITH initial value
|
||||
- DZ8, DZ32 : RAM zero-initialized areas (buffers)
|
||||
*/
|
||||
|
||||
void hexdump(void *ptr, int buflen);
|
||||
|
||||
// ============================================================================
|
||||
// TEST 1: Basic grammar with DV/DZ
|
||||
// ============================================================================
|
||||
static const std::string test1 = R"(
|
||||
; label definition
|
||||
.main: ;; comment here should work
|
||||
; We create a stupid loop just for RAM variable testing
|
||||
; ============================================================================
|
||||
; Test grammar with new DV/DZ syntax
|
||||
; ============================================================================
|
||||
|
||||
lcons r0, 4 ; prepare loop: 4 iterations
|
||||
lcons r2, $RamData1 ; save in R2 a ram address
|
||||
store @r2, r0, 4 ; save R0 in RAM
|
||||
lcons r1, 1
|
||||
.loop:
|
||||
load r0, @r2, 4 ; load this variable
|
||||
sub r0, r1
|
||||
store @r2, r0, 4 ; save R0 in RAM
|
||||
skipz r0 ; skip loop if R0 == 0
|
||||
jump .loop
|
||||
; ROM constants (DC)
|
||||
$imageBird DC8 "example.bmp"
|
||||
$someConstant DC32 12456789
|
||||
|
||||
; RAM initialized variables (DV)
|
||||
$RamData1 DV32 0 ; Integer initialized to 0
|
||||
$counter DV32 10 ; Counter initialized to 10
|
||||
|
||||
mov r0, r2 ; copy R2 into R0 (blank space between , and R2)
|
||||
mov R0,R2 ; copy R2 into R0 (NO blank space between , and R2)
|
||||
; RAM zeroed areas (DZ)
|
||||
$MyArray DZ8 10 ; Array of 10 bytes (zeroed)
|
||||
$WorkBuffer DZ8 64 ; Buffer of 64 bytes (zeroed)
|
||||
|
||||
halt
|
||||
|
||||
$imageBird DC8 "example.bmp", 8 ; data
|
||||
$someConstant DC32 12456789
|
||||
|
||||
; DSxx to declare a variable in RAM, followed by the number of elements
|
||||
$RamData1 DV32 1 ; one 32-bit integer
|
||||
$MyArray DV8 10 ; array of 10 bytes
|
||||
|
||||
)";
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
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<uint8_t> program;
|
||||
Chip32::Assembler assembler;
|
||||
Chip32::Result result;
|
||||
uint8_t data[8*1024];
|
||||
|
||||
bool parseResult = assembler.Parse(test1);
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
// ====================================================================================================
|
||||
static const std::string testPrintf = R"(
|
||||
|
||||
; ========================================================
|
||||
; We test the printf system call
|
||||
; ========================================================
|
||||
$printHello DC8 "La réponse est %d"
|
||||
$answer DC32 42
|
||||
|
||||
$counter DV32 10
|
||||
; ============================================================================
|
||||
; CODE
|
||||
; ============================================================================
|
||||
|
||||
.main:
|
||||
; We create a loop for RAM variable testing
|
||||
lcons r0, 4 ; prepare loop: 4 iterations
|
||||
lcons r2, $RamData1 ; save in R2 a ram address
|
||||
store @r2, r0, 4 ; save R0 in RAM
|
||||
lcons r1, 1
|
||||
|
||||
; 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
|
||||
.loop:
|
||||
load r0, @r2, 4 ; load this variable
|
||||
sub r0, r1
|
||||
store @r2, r0, 4 ; save R0 in RAM
|
||||
skipz r0 ; skip loop if R0 == 0
|
||||
jump .loop
|
||||
|
||||
; Test spacing variations
|
||||
mov r0, r2 ; copy R2 into R0 (blank space)
|
||||
mov R0,R2 ; copy R2 into R0 (NO blank space)
|
||||
|
||||
halt
|
||||
|
||||
|
||||
|
||||
)";
|
||||
|
||||
// ============================================================================
|
||||
// TEST CASE 1: Grammar and indentation with DV/DZ
|
||||
// ============================================================================
|
||||
|
||||
TEST_CASE("Check various indentations and typos with DV/DZ")
|
||||
{
|
||||
std::cout << "\n=== Test 1: Grammar and indentation ===" << std::endl;
|
||||
|
||||
Chip32::Machine machine;
|
||||
machine.QuickExecute(test1);
|
||||
|
||||
// Verify results
|
||||
REQUIRE(machine.parseResult == true);
|
||||
REQUIRE(machine.buildResult == true);
|
||||
REQUIRE(machine.runResult == VM_FINISHED);
|
||||
|
||||
std::cout << "✓ Test 1 passed: Grammar and DV/DZ syntax" << std::endl;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TEST 2: Printf with DV variable
|
||||
// ============================================================================
|
||||
|
||||
static const std::string testPrintf = R"(
|
||||
; ============================================================================
|
||||
; Test printf system call with DV variable
|
||||
; ============================================================================
|
||||
|
||||
; ROM constants (DC)
|
||||
$printHello DC8 "La réponse est %d"
|
||||
$answer DC32 42
|
||||
|
||||
; RAM initialized variable (DV)
|
||||
$counter DV32 10 ; Counter initialized to 10
|
||||
|
||||
; ============================================================================
|
||||
; CODE
|
||||
; ============================================================================
|
||||
|
||||
.main:
|
||||
; Simple test - print the counter value
|
||||
lcons r0, $printHello
|
||||
lcons r1, 1 ; 1 argument
|
||||
load r2, $counter, 4 ; Load counter value
|
||||
syscall 4 ; Printf
|
||||
|
||||
halt
|
||||
)";
|
||||
|
||||
TEST_CASE("Test printf with DV variable")
|
||||
{
|
||||
std::cout << "\n=== Test 2: Printf with DV ===" << std::endl;
|
||||
|
||||
Chip32::Machine machine;
|
||||
machine.QuickExecute(testPrintf);
|
||||
|
||||
REQUIRE(machine.parseResult == true);
|
||||
REQUIRE(machine.buildResult == true);
|
||||
REQUIRE(machine.runResult == VM_FINISHED);
|
||||
|
||||
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
|
||||
|
|
@ -220,24 +159,22 @@ static const std::string testMacro1 = R"(
|
|||
pop t0
|
||||
%endmacro
|
||||
|
||||
|
||||
%macro print 2
|
||||
lcons r0, %1 ; string text
|
||||
lcons r1, 1 ; number of arguments
|
||||
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
|
||||
lcons %2, %3 ; Initialize loop counter
|
||||
%1_loop: ; Loop start label
|
||||
%endmacro
|
||||
|
||||
|
||||
%macro LOOP_END 2
|
||||
subi %2, 1 ; Décrémente le registre spécifié
|
||||
subi %2, 1 ; Decrement counter
|
||||
skipz %2
|
||||
jump %1_loop ; Saute si le registre n'est pas zéro
|
||||
jump %1_loop ; Jump if not zero
|
||||
%endmacro
|
||||
|
||||
%section_text
|
||||
|
|
@ -248,76 +185,207 @@ static const std::string testMacro1 = R"(
|
|||
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 part 1" )
|
||||
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 << "-----------------------------------------------------" << std::endl;
|
||||
std::cout << "Generated Assembly:" << 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;
|
||||
}
|
||||
Chip32::Machine machine;
|
||||
machine.QuickExecute(resultAsm);
|
||||
|
||||
out.close();
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> program;
|
||||
// ============================================================================
|
||||
// TEST 4: DV vs DZ comprehensive test
|
||||
// ============================================================================
|
||||
|
||||
static const std::string testDvVsDz = R"(
|
||||
; ============================================================================
|
||||
; Comprehensive test: DV (initialized) vs DZ (zeroed)
|
||||
; ============================================================================
|
||||
|
||||
; ROM constants (DC)
|
||||
$appName DC8 "TestApp"
|
||||
$version DC32 100
|
||||
|
||||
; RAM initialized variables (DV) - WITH VALUES
|
||||
$counter DV32 42 ; int counter = 42
|
||||
$temperature DV32 20 ; int temp = 20
|
||||
$userName DV8 "Guest" ; char name[] = "Guest"
|
||||
$flags DV8 1, 0, 1 ; uint8_t flags[] = {1, 0, 1}
|
||||
|
||||
; RAM zeroed areas (DZ) - BUFFERS AND ARRAYS
|
||||
$rxBuffer DZ8 128 ; uint8_t buffer[128] = {0}
|
||||
$dataArray DZ32 50 ; int32_t array[50] = {0}
|
||||
$workArea DZ8 256 ; uint8_t work[256] = {0}
|
||||
|
||||
; ============================================================================
|
||||
; CODE
|
||||
; ============================================================================
|
||||
|
||||
.main:
|
||||
; Test 1: DV counter should be 42
|
||||
load r0, $counter, 4
|
||||
; r0 should be 42
|
||||
|
||||
; Test 2: DV temperature should be 20
|
||||
load r1, $temperature, 4
|
||||
; r1 should be 20
|
||||
|
||||
; Test 3: DZ rxBuffer[0] should be 0
|
||||
lcons r2, $rxBuffer
|
||||
load r3, @r2, 1
|
||||
; r3 should be 0
|
||||
|
||||
; Test 4: Write to DZ buffer
|
||||
lcons r4, 0x42
|
||||
store @r2, r4, 1
|
||||
; rxBuffer[0] = 0x42
|
||||
|
||||
; Test 5: Read back modified value
|
||||
load r5, @r2, 1
|
||||
; r5 should be 0x42
|
||||
|
||||
halt
|
||||
)";
|
||||
|
||||
TEST_CASE("DV vs DZ comprehensive test")
|
||||
{
|
||||
std::cout << "\n=== Test 4: DV vs DZ comprehensive ===" << std::endl;
|
||||
|
||||
Chip32::Machine machine;
|
||||
machine.QuickExecute(testDvVsDz);
|
||||
|
||||
REQUIRE(machine.parseResult == true);
|
||||
REQUIRE(machine.buildResult == true);
|
||||
REQUIRE(machine.runResult == VM_FINISHED);
|
||||
|
||||
// Verify register values
|
||||
std::cout << "\nRegister values after execution:" << std::endl;
|
||||
std::cout << " R0 (counter) = " << machine.ctx.registers[R0]
|
||||
<< " (expected: 42)" << std::endl;
|
||||
std::cout << " R1 (temperature) = " << machine.ctx.registers[R1]
|
||||
<< " (expected: 20)" << std::endl;
|
||||
std::cout << " R3 (rxBuffer[0] initial) = " << machine.ctx.registers[R3]
|
||||
<< " (expected: 0)" << std::endl;
|
||||
std::cout << " R5 (rxBuffer[0] after write) = " << machine.ctx.registers[R5]
|
||||
<< " (expected: 66)" << std::endl;
|
||||
|
||||
REQUIRE(machine.ctx.registers[R0] == 42); // counter DV value
|
||||
REQUIRE(machine.ctx.registers[R1] == 20); // temperature DV value
|
||||
REQUIRE(machine.ctx.registers[R3] == 0); // rxBuffer DZ initial (zero)
|
||||
REQUIRE(machine.ctx.registers[R5] == 0x42); // rxBuffer after write
|
||||
|
||||
std::cout << "✓ Test 4 passed: DV vs DZ comprehensive" << std::endl;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TEST 5: Binary format validation
|
||||
// ============================================================================
|
||||
|
||||
static const std::string testBinaryFormat = R"(
|
||||
; Test binary format with all section types
|
||||
|
||||
; DC: ROM constants
|
||||
$romString DC8 "Hello"
|
||||
$romValue DC32 123
|
||||
|
||||
; DV: Initialized RAM variables
|
||||
$ramCounter DV32 99
|
||||
$ramMessage DV8 "OK"
|
||||
|
||||
; DZ: Zeroed RAM areas
|
||||
$ramBuffer DZ8 64
|
||||
$ramArray DZ32 20
|
||||
|
||||
.main:
|
||||
load r0, $ramCounter, 4
|
||||
halt
|
||||
)";
|
||||
|
||||
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;
|
||||
uint8_t data[8*1024];
|
||||
std::vector<uint8_t> program;
|
||||
|
||||
bool parseResult = assembler.Parse(resultAsm);
|
||||
REQUIRE(assembler.Parse(testBinaryFormat) == true);
|
||||
REQUIRE(assembler.BuildBinary(program, result) == true);
|
||||
|
||||
std::cout << assembler.GetLastError().ToString() << std::endl;
|
||||
|
||||
REQUIRE( parseResult == true );
|
||||
|
||||
|
||||
REQUIRE( assembler.BuildBinary(program, result) == true);
|
||||
std::cout << "\nBinary statistics:" << std::endl;
|
||||
result.Print();
|
||||
hexdump(program.data(), program.size());
|
||||
|
||||
chip32_ctx_t chip32_ctx;
|
||||
// Validate binary format
|
||||
chip32_loaded_binary_t loaded;
|
||||
chip32_binary_error_t error = chip32_binary_load(
|
||||
program.data(),
|
||||
static_cast<uint32_t>(program.size()),
|
||||
&loaded
|
||||
);
|
||||
|
||||
chip32_ctx.stack_size = 512;
|
||||
REQUIRE(error == CHIP32_BIN_OK);
|
||||
|
||||
chip32_ctx.rom.mem = program.data();
|
||||
chip32_ctx.rom.addr = 0;
|
||||
chip32_ctx.rom.size = program.size();
|
||||
std::cout << "\nBinary header:" << std::endl;
|
||||
chip32_binary_print_header(&loaded.header);
|
||||
|
||||
chip32_ctx.ram.mem = data;
|
||||
chip32_ctx.ram.addr = 40 *1024;
|
||||
chip32_ctx.ram.size = sizeof(data);
|
||||
// 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.data_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
|
||||
|
||||
chip32_ctx.syscall = story_player_syscall;
|
||||
std::cout << "\nBinary format validations:" << std::endl;
|
||||
std::cout << " ✓ Magic number correct" << std::endl;
|
||||
std::cout << " ✓ Version correct" << std::endl;
|
||||
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;
|
||||
|
||||
chip32_initialize(&chip32_ctx);
|
||||
chip32_result_t runResult = chip32_run(&chip32_ctx);
|
||||
// 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( runResult == VM_FINISHED );
|
||||
|
||||
REQUIRE( chip32_ctx.registers[R3] == 5);
|
||||
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;
|
||||
REQUIRE(ramCounter == 99);
|
||||
|
||||
std::cout << "\n✓ Test 5 passed: Binary format validation" << std::endl;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ set(SRCS
|
|||
|
||||
../core/chip32/chip32_assembler.cpp
|
||||
../core/chip32/chip32_vm.c
|
||||
../core/chip32/chip32_binary_format.c
|
||||
|
||||
|
||||
# Add lua source code files
|
||||
|
|
|
|||
Loading…
Reference in a new issue