mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 08:59:35 +01:00
Big update on machine class, fix addressing lcons, load etc.
This commit is contained in:
parent
c6da4b891a
commit
0c3809657e
16 changed files with 568 additions and 18486 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -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
2
.vscode/launch.json
vendored
|
|
@ -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": [],
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ®Name, uint8_t ®);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
17966
core/tests/catch.hpp
17966
core/tests/catch.hpp
File diff suppressed because it is too large
Load diff
|
|
@ -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"
|
||||
|
|
|
|||
75
core/tests/test_macros.cpp
Normal file
75
core/tests/test_macros.cpp
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue