Fixed data connection, compilation order and print format
Some checks are pending
Build-StoryEditor / build_linux (push) Waiting to run
Build-StoryEditor / build_win32 (push) Waiting to run

This commit is contained in:
anthony@rabine.fr 2025-10-06 15:00:37 +02:00
parent eb08627029
commit 741a3c633e
16 changed files with 358 additions and 430 deletions

View file

@ -33,7 +33,7 @@ public:
}
// Lecture d'une chaîne depuis la mémoire (non statique maintenant)
std::string GetStringFromMemory(chip32_ctx_t *ctx, uint32_t addr)
static std::string GetStringFromMemory(chip32_ctx_t *ctx, uint32_t addr)
{
if (!ctx) {
throw std::runtime_error("Invalid context in GetStringFromMemory");
@ -73,66 +73,101 @@ public:
}
}
// Formatter avec nombre variable d'arguments (non statique)
std::string FormatString(const std::string& format,
const std::vector<uint32_t>& args)
static std::string FormatStringWithPlaceholders(chip32_ctx_t *ctx,
const std::string& format,
const std::vector<uint32_t>& args)
{
if (args.empty()) {
return format;
}
std::ostringstream result;
size_t pos = 0;
std::vector<char> buffer(1024);
int result = -1;
while (pos < format.length()) {
// Chercher le prochain placeholder '{'
if (format[pos] == '{' && pos + 1 < format.length()) {
char nextChar = format[pos + 1];
switch (args.size()) {
case 1:
result = std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0]);
break;
case 2:
result = std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0], args[1]);
break;
case 3:
result = std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0], args[1], args[2]);
break;
case 4:
result = std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0], args[1], args[2], args[3]);
break;
default:
throw std::runtime_error("Too many arguments for printf (max 4)");
}
// Vérifier si c'est un placeholder valide {0} à {3}
if (nextChar >= '0' && nextChar <= '3') {
int argIndex = nextChar - '0';
if (result < 0) {
throw std::runtime_error("Error formatting string");
}
// Vérifier si on a assez d'arguments
if (argIndex >= static_cast<int>(args.size())) {
result << "{" << argIndex << ":?}"; // Argument manquant
pos += 2;
continue;
}
if (static_cast<size_t>(result) >= buffer.size()) {
buffer.resize(result + 1);
uint32_t argValue = args[argIndex];
switch (args.size()) {
case 1:
std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0]);
break;
case 2:
std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0], args[1]);
break;
case 3:
std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0], args[1], args[2]);
break;
case 4:
std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0], args[1], args[2], args[3]);
break;
// 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;
}
case 'c': // Caractère
result << static_cast<char>(argValue);
break;
default:
// Type inconnu, afficher tel quel
result << "{" << argIndex << ":" << typeChar << "}";
}
pos += 5; // Avancer de "{0:d}"
continue;
}
}
// Format court {0} sans type → défaut: entier
else if (pos + 2 < format.length() && format[pos + 2] == '}') {
result << static_cast<int32_t>(argValue);
pos += 3; // Avancer de "{0}"
continue;
}
}
}
// Caractère normal, copier tel quel
result << format[pos];
pos++;
}
return std::string(buffer.data());
return result.str();
}
// Handler de syscall (méthode membre, non statique)
@ -149,7 +184,7 @@ public:
args.push_back(ctx->registers[R2 + i]);
}
printOutput = FormatString(format, args);
printOutput = FormatStringWithPlaceholders(ctx, format, args);
std::cout << "[SYSCALL PRINT] " << printOutput << std::endl;
}
else if (code == 5) // WAIT

View file

@ -284,10 +284,28 @@ private:
m_assembly << " lcons " << targetReg << ", " << op->GetValue() << "\n";
return targetReg;
case TACOperand::Type::VARIABLE:
m_assembly << " load " << targetReg << ", $" << op->GetValue() << ", 4\n";
case TACOperand::Type::VARIABLE: {
std::shared_ptr<Variable> var = nullptr;
// Chercher la variable par son label
for (const auto& v : m_context.variables) {
if (v->GetLabel() == op->GetValue()) {
var = v;
break;
}
}
if (var && var->GetValueType() == Variable::ValueType::STRING) {
// Pour les strings, charger l'ADRESSE
m_assembly << " lcons " << targetReg << ", $" << op->GetValue() << "\n";
} else {
// Pour les autres types, charger la VALEUR
m_assembly << " load " << targetReg << ", $" << op->GetValue() << ", 4\n";
}
return targetReg;
}
case TACOperand::Type::TEMPORARY: {
auto it = m_tempLocations.find(op->GetValue());
if (it == m_tempLocations.end()) {
@ -542,16 +560,6 @@ private:
case Variable::ValueType::STRING: {
std::string value = v->GetValue<std::string>();
// Convertir {0} {1} {2} {3} en %d
for (int i = 0; i < 4; ++i) {
std::string placeholder = "{" + std::to_string(i) + "}";
size_t pos = 0;
while ((pos = value.find(placeholder, pos)) != std::string::npos) {
value.replace(pos, placeholder.length(), "%d");
pos += 2;
}
}
m_assembly << "$" << v->GetLabel() << " DC8, \""
<< value << "\" ; " << v->GetVariableName() << "\n";
break;

View file

@ -183,11 +183,17 @@ public:
// Maintenant, on va ajouter les connexions de données
for (const auto& conn : m_connections)
{
std::cout << ">>> ASTBuilder: Processing connection from " << conn->outNodeId
<< " to " << conn->inNodeId
<< " type=" << conn->type << " (0=EXEC, 1=DATA)" << std::endl;
// Ne traiter que les connexions DATA_LINK
if (conn->type != Connection::DATA_LINK) {
continue;
}
std::cout << " -> Adding DATA connection!" << std::endl;
auto outNode = nodeMap[conn->outNodeId];
auto inNode = nodeMap[conn->inNodeId];

View file

@ -460,10 +460,19 @@ private:
std::shared_ptr<TACOperand> GenerateVariableNode(std::shared_ptr<ASTNode> node) {
auto* varNode = node->GetAs<VariableNode>();
if (!varNode) return nullptr;
if (!varNode) {
std::cout << " ERROR: node is not a VariableNode!\n";
return nullptr;
}
auto var = varNode->GetVariable();
if (!var) return nullptr;
if (!var) {
std::cout << " ERROR: Variable is NULL for node " << varNode->GetId()
<< " (UUID: " << varNode->GetVariableUuid() << ")\n";
std::cout << " This should have been resolved before TAC generation!\n";
return nullptr;
}
// Créer une opérande qui référence la variable
return std::make_shared<TACOperand>(
@ -552,17 +561,12 @@ private:
std::cout << " GeneratePrintNode: START\n";
// Créer l'opérande pour la chaîne de format
auto formatOperand = std::make_shared<TACOperand>(
TACOperand::Type::VARIABLE,
printNode->GetLabel()
);
// RÉCUPÉRER ET CONVERTIR LE FORMAT STRING
std::string formatString = printNode->GetText();
std::cout << " Format string label: " << printNode->GetLabel() << "\n";
std::cout << " Number of data inputs: " << node->dataInputs.size() << "\n";
// Évaluer tous les arguments
// Évaluer tous les arguments et déterminer leurs types
std::vector<std::shared_ptr<TACOperand>> args;
std::vector<Variable::ValueType> argTypes;
// Collecter et trier les inputs par port index
std::vector<std::pair<unsigned int, std::shared_ptr<ASTNode>>> sortedInputs;
@ -574,18 +578,76 @@ private:
std::sort(sortedInputs.begin(), sortedInputs.end(),
[](const auto& a, const auto& b) { return a.first < b.first; });
// Générer le code pour chaque argument
// Générer le code pour chaque argument ET récupérer son type
for (const auto& [port, inputNode] : sortedInputs) {
std::cout << " Processing input port " << port << "\n";
auto argOperand = GenerateNode(inputNode);
if (argOperand) {
std::cout << " -> Got operand: " << argOperand->ToString() << "\n";
args.push_back(argOperand);
// DÉTERMINER LE TYPE DE L'ARGUMENT
Variable::ValueType argType = Variable::ValueType::INTEGER; // défaut
if (inputNode->IsType<VariableNode>()) {
auto* varNode = inputNode->GetAs<VariableNode>();
auto var = varNode->GetVariable();
if (var) {
argType = var->GetValueType();
std::cout << " -> Variable type: "
<< Variable::ValueTypeToString(argType) << "\n";
}
}
// Pour les OperatorNode, le résultat est toujours un INTEGER
else if (inputNode->IsType<OperatorNode>()) {
argType = Variable::ValueType::INTEGER;
}
argTypes.push_back(argType);
}
}
std::cout << " Total args collected: " << args.size() << "\n";
// CONVERTIR LES PLACEHOLDERS EN FONCTION DU TYPE
for (size_t i = 0; i < argTypes.size() && i < 4; i++) {
std::string placeholder = "{" + std::to_string(i) + "}";
std::string formatSpec;
switch (argTypes[i]) {
case Variable::ValueType::STRING:
formatSpec = "{" + std::to_string(i) + ":s}";
break;
case Variable::ValueType::INTEGER:
formatSpec = "{" + std::to_string(i) + ":d}";
break;
case Variable::ValueType::FLOAT:
formatSpec = "{" + std::to_string(i) + ":f}";
break;
default:
formatSpec = "{" + std::to_string(i) + ":d}";
}
// Remplacer {0} par {0:d} ou {0:s}
size_t pos = formatString.find(placeholder);
if (pos != std::string::npos) {
formatString.replace(pos, placeholder.length(), formatSpec);
}
}
// METTRE À JOUR LE FORMAT STRING DANS LA VARIABLE
auto formatVar = printNode->GetVariable(printNode->GetLabel());
if (formatVar) {
formatVar->SetTextValue(formatString);
std::cout << " Updated format string to: " << formatString << "\n";
}
// Créer l'opérande pour la chaîne de format (avec le format mis à jour)
auto formatOperand = std::make_shared<TACOperand>(
TACOperand::Type::VARIABLE,
printNode->GetLabel()
);
// Générer les instructions PARAM pour chaque argument
for (size_t i = 0; i < args.size(); i++) {
std::cout << " Generating PARAM instruction [" << i << "] for "

View file

@ -2,11 +2,12 @@
void to_json(nlohmann::json &j, const Connection &p) {
j = nlohmann::json{
{"outNodeId", p.outNodeId },
{"outPortIndex", static_cast<int64_t>(p.outPortIndex)},
{"inNodeId", p.inNodeId},
{"inPortIndex", static_cast<int64_t>(p.inPortIndex)},
};
{"outNodeId", p.outNodeId },
{"outPortIndex", static_cast<int64_t>(p.outPortIndex)},
{"inNodeId", p.inNodeId},
{"inPortIndex", static_cast<int64_t>(p.inPortIndex)},
{"type", static_cast<int>(p.type)}
};
}
void from_json(const nlohmann::json &j, Connection &p) {
@ -15,4 +16,11 @@ void from_json(const nlohmann::json &j, Connection &p) {
p.inNodeId = j["inNodeId"].get<std::string>();
p.outPortIndex = j["outPortIndex"].get<int>();
p.inPortIndex = j["inPortIndex"].get<int>();
if (j.contains("type")) {
p.type = static_cast<Connection::Type>(j["type"].get<int>());
} else {
// Par défaut EXECUTION_LINK pour les anciens fichiers
p.type = Connection::EXECUTION_LINK;
}
}

View file

@ -29,15 +29,24 @@ struct Connection
std::string inNodeId;
unsigned int inPortIndex{0};
Connection(const Connection &other){
*this = other;
Connection(const Connection &other)
: type(other.type)
, outNodeId(other.outNodeId)
, outPortIndex(other.outPortIndex)
, inNodeId(other.inNodeId)
, inPortIndex(other.inPortIndex)
{
}
Connection& operator=(const Connection& other) {
this->outNodeId = other.outNodeId;
this->outPortIndex = other.outPortIndex;
this->inNodeId = other.inNodeId;
this->inPortIndex = other.inPortIndex;
if (this != &other) {
this->outNodeId = other.outNodeId;
this->outPortIndex = other.outPortIndex;
this->inNodeId = other.inNodeId;
this->inPortIndex = other.inPortIndex;
this->type = other.type;
}
return *this;
}
};
@ -45,7 +54,7 @@ struct Connection
inline bool operator==(Connection const &a, Connection const &b)
{
return a.outNodeId == b.outNodeId && a.outPortIndex == b.outPortIndex
&& a.inNodeId == b.inNodeId && a.inPortIndex == b.inPortIndex;
&& a.inNodeId == b.inNodeId && a.inPortIndex == b.inPortIndex && a.type == b.type;
}
inline bool operator!=(Connection const &a, Connection const &b)

View file

@ -19,6 +19,14 @@ public:
static constexpr int MAX_INPUT_COUNT = 4;
std::shared_ptr<Variable> GetVariable(const std::string& label) const {
auto it = m_variables.find(label);
if (it != m_variables.end()) {
return it->second;
}
return nullptr;
}
private:
std::string m_label; // Label for the string literal
};

View file

@ -46,3 +46,32 @@ std::shared_ptr<Variable> VariableNode::GetVariable() const
{
return m_variable;
}
bool VariableNode::ResolveVariable(const std::vector<std::shared_ptr<Variable>>& variables)
{
// Si la variable est déjà résolue, pas besoin de chercher
if (m_variable) {
return true;
}
// Si pas d'UUID, impossible de résoudre
if (m_variableUuid.empty()) {
std::cout << "WARNING: VariableNode " << GetId()
<< " has no variable UUID!" << std::endl;
return false;
}
// Chercher la variable correspondant à l'UUID
for (const auto& var : variables) {
if (var->GetUuid() == m_variableUuid) {
m_variable = var;
std::cout << "✓ Resolved variable '" << var->GetVariableName()
<< "' for VariableNode " << GetId() << std::endl;
return true;
}
}
std::cout << "ERROR: Could not resolve variable UUID " << m_variableUuid
<< " for VariableNode " << GetId() << std::endl;
return false;
}

View file

@ -20,6 +20,8 @@ public:
void SetVariable(std::shared_ptr<Variable> var);
std::shared_ptr<Variable> GetVariable() const;
bool ResolveVariable(const std::vector<std::shared_ptr<Variable>>& variables);
private:
std::string m_variableUuid;
std::shared_ptr<Variable> m_variable;

View file

@ -437,7 +437,38 @@ bool StoryProject::GenerateCompleteProgram(std::string &assembly)
pageData[std::string(page->Uuid())] = {pageNodes, pageLinks};
}
// === PHASE 2 : GÉNÉRATION ===
std::cout << "\n=== Resolving VariableNode references ===\n";
for (const auto& baseNode : allNodes) {
auto varNode = std::dynamic_pointer_cast<VariableNode>(baseNode);
if (varNode) {
varNode->ResolveVariable(m_variables);
}
}
// ✅ 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;
for (const auto& page : m_pages) {
std::string pageUuid(page->Uuid());
auto& [nodes, connections] = pageData[pageUuid];
// Construire l'AST pour cette page
ASTBuilder builder(nodes, connections);
auto astNodes = builder.BuildAST();
// Générer le TAC pour cette page
TACGenerator tacGen;
TACProgram pageTAC = tacGen.Generate(astNodes);
// Stocker le TAC
pageTACPrograms[pageUuid] = pageTAC;
std::cout << "Generated TAC for page: " << page->GetName() << std::endl;
}
std::cout << "=== All TAC programs generated ===\n\n";
// === PHASE 3 : GÉNÉRATION DE L'ASSEMBLEUR ===
AssemblyGenerator::GeneratorContext context(
m_variables,
"2025-01-10 15:30:00",
@ -453,13 +484,14 @@ bool StoryProject::GenerateCompleteProgram(std::string &assembly)
generator.Reset();
generator.GenerateHeader();
// === SECTION DATA (commune à toutes les pages) ===
// === SECTION DATA (maintenant les format strings sont corrects!) ===
generator.StartSection(AssemblyGenerator::Section::DATA);
// Variables globales (partagées entre toutes les pages)
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
generator.GenerateNodesVariables(allNodes);
// === SECTION TEXT (chaque page = une fonction) ===
@ -469,11 +501,9 @@ bool StoryProject::GenerateCompleteProgram(std::string &assembly)
bool isFirstPage = true;
for (const auto& page : m_pages) {
std::string pageUuid(page->Uuid());
auto& [nodes, connections] = pageData[pageUuid];
// Construire l'AST pour cette page
ASTBuilder builder(nodes, connections);
auto astNodes = builder.BuildAST();
// Récupérer le TAC pré-généré
TACProgram& pageTAC = pageTACPrograms[pageUuid];
// Générer le label de fonction
std::string functionLabel;
@ -490,10 +520,6 @@ bool StoryProject::GenerateCompleteProgram(std::string &assembly)
generator.AddComment("========================================");
generator.GetAssembly() << functionLabel << ":\n";
// Générer le TAC pour cette page
TACGenerator tacGen;
TACProgram pageTAC = tacGen.Generate(astNodes);
if (context.debugOutput) {
std::cout << "\n=== TAC for page: " << page->GetName() << " ===\n";
std::cout << pageTAC.ToString() << std::endl;

View file

@ -1,6 +1,6 @@
[Window][WindowOverViewport_11111111]
Pos=60,26
Size=1220,694
Size=1220,836
Collapsed=0
[Window][Debug##Default]
@ -10,31 +10,31 @@ Collapsed=0
[Window][Library Manager]
Pos=630,26
Size=650,235
Size=650,518
Collapsed=0
DockId=0x00000002,0
[Window][Console]
Pos=60,263
Size=628,457
Pos=60,546
Size=628,316
Collapsed=0
DockId=0x00000004,0
[Window][Emulator]
Pos=630,26
Size=650,235
Size=650,518
Collapsed=0
DockId=0x00000002,5
[Window][Code viewer]
Pos=630,26
Size=650,235
Size=650,518
Collapsed=0
DockId=0x00000002,4
[Window][Resources]
Pos=630,26
Size=650,235
Size=650,518
Collapsed=0
DockId=0x00000002,1
@ -50,36 +50,36 @@ Size=150,42
Collapsed=0
[Window][Variables]
Pos=690,263
Size=590,457
Pos=690,546
Size=590,316
Collapsed=0
DockId=0x00000005,0
[Window][CPU]
Pos=630,26
Size=650,235
Size=650,518
Collapsed=0
DockId=0x00000002,2
[Window][RAM view]
Pos=630,26
Size=650,235
Size=650,518
Collapsed=0
DockId=0x00000002,3
[Window][Properties]
Pos=690,263
Size=590,457
Pos=690,546
Size=590,316
Collapsed=0
DockId=0x00000005,1
[Window][ToolBar]
Pos=0,26
Size=60,694
Size=60,836
Collapsed=0
[Window][QuitConfirm]
Pos=508,312
Pos=508,383
Size=264,96
Collapsed=0
@ -90,13 +90,13 @@ Collapsed=0
[Window][Module editor]
Pos=60,26
Size=568,235
Size=568,518
Collapsed=0
DockId=0x00000001,0
[Window][Story editor]
Pos=60,26
Size=568,235
Size=568,518
Collapsed=0
DockId=0x00000001,1
@ -106,8 +106,8 @@ Size=687,422
Collapsed=0
[Window][Error List]
Pos=60,263
Size=628,457
Pos=60,546
Size=628,316
Collapsed=0
DockId=0x00000004,1
@ -155,11 +155,11 @@ Column 2 Weight=1.0000
Column 3 Width=60
[Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,235 Split=X
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,836 Split=Y
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,549 Split=X
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=1144,694 CentralNode=1 Selected=0x93ADCAAB
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=650,694 Selected=0x4B07C626
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,457 Split=X Selected=0xEA83D666
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=650,694 Selected=0xE5897A33
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,316 Split=X Selected=0xEA83D666
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=616,192 Selected=0xEA83D666
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=578,192 Selected=0x8C72BEA8

View file

@ -16,51 +16,7 @@
#include "variable.h" // Pour Variable
#include "all_events.h"
#include "Localization.h"
// Définitions des registres et événements CHIP-32 si non déjà dans chip32_vm.h
#ifndef R0
#define R0 0
#endif
#ifndef R1
#define R1 1
#endif
#ifndef R2
#define R2 2
#endif
#ifndef R3
#define R3 3
#endif
#ifndef R4
#define R4 4
#endif
#ifndef PC
#define PC 7 // Exemple de registre Program Counter
#endif
#ifndef EV_MASK_OK_BUTTON
#define EV_MASK_OK_BUTTON 1
#endif
#ifndef EV_MASK_HOME_BUTTON
#define EV_MASK_HOME_BUTTON 2
#endif
#ifndef EV_MASK_PREVIOUS_BUTTON
#define EV_MASK_PREVIOUS_BUTTON 4
#endif
#ifndef EV_MASK_NEXT_BUTTON
#define EV_MASK_NEXT_BUTTON 8
#endif
#ifndef EV_MASK_END_OF_AUDIO
#define EV_MASK_END_OF_AUDIO 16
#endif
// Définitions pour les codes de retour de Syscall
#ifndef SYSCALL_RET_OK
#define SYSCALL_RET_OK 0
#endif
#ifndef SYSCALL_RET_WAIT_EV
#define SYSCALL_RET_WAIT_EV 1 // Exemple: VM doit attendre un événement
#endif
#include "chip32_machine.h"
AppController::AppController(ILogger& logger, EventBus& eventBus)
: m_logger(logger)
@ -633,37 +589,6 @@ void AppController::LoadParams()
}
}
std::string AppController::GetStringFromMemory(uint32_t addr)
{
// Buffer local pour la chaîne
// Assurez-vous qu'il est assez grand pour gérer les chaînes de votre VM
// et qu'il est terminé par null
char strBuf[256]; // Augmenté la taille pour plus de sécurité
// Le bit le plus significatif indique si c'est de la RAM (0x80000000) ou ROM
bool isRam = (addr & 0x80000000) != 0;
addr &= 0xFFFF; // Masque pour obtenir l'adresse 16 bits
// Vérification de l'adresse pour éviter les dépassements de buffer
if (isRam) {
if (addr < m_chip32_ctx.ram.size) {
strncpy(strBuf, (const char *)&m_chip32_ctx.ram.mem[addr], sizeof(strBuf) - 1);
strBuf[sizeof(strBuf) - 1] = '\0'; // S'assurer que c'est null-terminated
} else {
m_logger.Log("GetStringFromMemory: Invalid RAM address: 0x" + std::to_string(addr), true);
return "";
}
} else {
if (addr < m_chip32_ctx.rom.size) {
strncpy(strBuf, (const char *)&m_chip32_ctx.rom.mem[addr], sizeof(strBuf) - 1);
strBuf[sizeof(strBuf) - 1] = '\0'; // S'assurer que c'est null-terminated
} else {
m_logger.Log("GetStringFromMemory: Invalid ROM address: 0x" + std::to_string(addr), true);
return "";
}
}
return strBuf;
}
void AppController::ProcessStory()
{
@ -784,6 +709,7 @@ void AppController::StepInstruction()
UpdateVmView();
}
uint8_t AppController::Syscall(chip32_ctx_t *ctx, uint8_t code)
{
uint8_t retCode = SYSCALL_RET_OK;
@ -795,7 +721,7 @@ uint8_t AppController::Syscall(chip32_ctx_t *ctx, uint8_t code)
// R0: image file name address, R1: sound file name address
if (ctx->registers[R0] != 0)
{
std::string imageFile = m_story->BuildFullAssetsPath(GetStringFromMemory(ctx->registers[R0]));
std::string imageFile = m_story->BuildFullAssetsPath(Chip32::Machine::GetStringFromMemory(ctx, ctx->registers[R0]));
m_logger.Log("Image: " + imageFile);
// Ici, vous notifieriez la fenêtre de l'émulateur
// m_emulatorDock.SetImage(imageFile); // Ceci est une dépendance GUI
@ -807,7 +733,7 @@ uint8_t AppController::Syscall(chip32_ctx_t *ctx, uint8_t code)
if (ctx->registers[R1] != 0)
{
std::string soundFile = m_story->BuildFullAssetsPath(GetStringFromMemory(ctx->registers[R1]));
std::string soundFile = m_story->BuildFullAssetsPath(Chip32::Machine::GetStringFromMemory(ctx, ctx->registers[R1]));
m_logger.Log("Sound: " + soundFile);
m_player.Play(soundFile);
}
@ -827,23 +753,18 @@ uint8_t AppController::Syscall(chip32_ctx_t *ctx, uint8_t code)
}
else if (code == 4) // Printf (printf-like behavior)
{
std::string text = GetStringFromMemory(ctx->registers[R0]);
std::string format = Chip32::Machine::GetStringFromMemory(ctx, ctx->registers[R0]);
int arg_count = ctx->registers[R1];
char working_buf[400] = {0};
// Simplified printf logic for logging
switch(arg_count){
case 0: strcpy(working_buf, text.c_str()); break;
case 1: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2]); break;
case 2: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3]); break;
case 3: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]); break;
default: m_logger.Log("Printf with unsupported arg_count: " + std::to_string(arg_count) + " text: " + text, true); break;
std::vector<uint32_t> args;
for (int i = 0; i < arg_count && i < 4; ++i) {
args.push_back(ctx->registers[R2 + i]);
}
// Send event to UI
auto evObj = std::make_shared<VmStateEvent>();
evObj->type = VmStateEvent::Type::PrintEvent;
evObj->printOutput = working_buf;
evObj->printOutput = Chip32::Machine::FormatStringWithPlaceholders(ctx, format, args);
m_eventBus.Emit(evObj);
}
else if (code == 5) // WAIT (sleep)
@ -869,14 +790,13 @@ void AppController::UpdateVmView()
// Au lieu de cela, il émettrait un signal ou appellerait un observer.
uint32_t pcVal = m_chip32_ctx.registers[PC];
if (m_story && m_story->GetAssemblyLine(pcVal, m_dbg.line))
if (m_story)
{
m_logger.Log("Executing line: " + std::to_string(m_dbg.line + 1));
if (m_story->GetAssemblyLine(pcVal, m_dbg.line))
{
m_logger.Log("Executing line: " + std::to_string(m_dbg.line + 1));
// m_debuggerWindow.HighlightLine(m_dbg.line); // Dépendance GUI
}
else
{
m_logger.Log("Reached end or instruction not found (line: " + std::to_string(m_dbg.line) + ")", false);
}
}
// m_cpuWindow.updateRegistersView(m_chip32_ctx); // Dépendance GUI
// m_memoryEditor.DrawWindow("RAM view", m_chip32_ctx.ram.mem, m_chip32_ctx.ram.size); // Dépendance GUI

View file

@ -100,7 +100,7 @@ public:
// Méthodes pour interagir avec la VM et le débogueur
chip32_ctx_t* GetChip32Context() { return &m_chip32_ctx; }
DebugContext* GetDebugContext() { return &m_dbg; }
std::string GetStringFromMemory(uint32_t addr);
void ProcessStory();
void StepInstruction();
void StopAudio() { m_player.Stop(); }

View file

@ -117,7 +117,7 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
// Show success message if no errors
if (!m_errorListDock.HasErrors()) {
// You can also open a popup if desired
m_logger.Log("Module compilation successful - Binary saved and loaded into VM");
m_logger.Log("Module compilation successful - Binary saved and loaded into VM");
}
}
else if (event.GetType() == ModuleEvent::Type::BuildFailure) {

View file

@ -134,37 +134,6 @@ struct NodeEditorPage : public ImFlow::BaseNode
return m_uuid;
}
std::list<std::shared_ptr<BaseNodeWidget>> GetNodes()
{
std::list<std::shared_ptr<BaseNodeWidget>> nlist;
// std::unordered_map<ImFlow::NodeUID, std::shared_ptr<BaseNode>>&
for (auto &node : mINF.getNodes())
{
auto delegate = dynamic_cast<NodeDelegate*>(node.second.get());
if (delegate == nullptr)
continue;
nlist.push_back(delegate->GetWidget());
}
return nlist;
}
std::list<std::shared_ptr<Connection>> GetLinks() {
std::list<std::shared_ptr<Connection>> links;
// const std::vector<std::weak_ptr<Link>>& getLinks()
for (auto &link : mINF.getLinks())
{
auto linkInfo = std::make_shared<Connection>();
links.push_back(linkInfo);
}
return links;
}
void Select()
{
}

View file

@ -379,6 +379,22 @@ void NodeEditorWindow::SaveNodesToProject()
connection->inNodeId = rightBaseNode->GetId();
connection->inPortIndex = rightPinIndex;
// Detect type
auto outputPort = leftBaseNode->GetOutputPort(leftPinIndex);
if (outputPort.type == BaseNode::Port::Type::DATA_PORT) {
connection->type = Connection::DATA_LINK;
} else {
connection->type = Connection::EXECUTION_LINK;
}
// // 🔍 DEBUG
// std::cout << "=== SAVE CONNECTION ===" << std::endl;
// std::cout << " From: " << connection->outNodeId << "[" << connection->outPortIndex << "]" << std::endl;
// std::cout << " To: " << connection->inNodeId << "[" << connection->inPortIndex << "]" << std::endl;
// std::cout << " Output port type: " << (outputPort.type == BaseNode::Port::Type::DATA_PORT ? "DATA_PORT" : "EXECUTION_PORT") << std::endl;
// std::cout << " Connection type: " << (connection->type == Connection::DATA_LINK ? "DATA_LINK" : "EXECUTION_LINK") << std::endl;
// std::cout << "========================" << std::endl;
// Add connection to project
m_story->AddConnection(projectPage->Uuid(), connection);
@ -474,165 +490,15 @@ void NodeEditorWindow::Draw()
ToolbarUI();
m_currentPage->Draw(m_nodesFactory, m_widgetFactory, m_manager);
/*
ed::Begin(m_currentPage->Uuid().data(), ImVec2(0.0, 0.0f));
// Draw our nodes
m_currentPage->Draw();
// Handle creation action, returns true if editor want to create new object (node or link)
if (ed::BeginCreate())
{
ed::PinId startId, endId;
if (ed::QueryNewLink(&startId, &endId))
{
// QueryNewLink returns true if editor want to create new link between pins.
//
// Link can be created only for two valid pins, it is up to you to
// validate if connection make sense. Editor is happy to make any.
//
// Link always goes from input to output. User may choose to drag
// link from output pin or input pin. This determine which pin ids
// are valid and which are not:
// * input valid, output invalid - user started to drag new ling from input pin
// * input invalid, output valid - user started to drag new ling from output pin
// * input valid, output valid - user dragged link over other pin, can be validated
if (startId && endId) // both are valid, let's accept link
{
// ed::AcceptNewItem() return true when user release mouse button.
if (ed::AcceptNewItem())
{
auto c = std::make_shared<Connection>();
// On cherche à quel noeud appartien les pin (selon si le lien a été créé à partir d'une entrée ou d'une sortie)
if (FillConnection(c, startId))
{
if (FillConnection(c, endId))
{
m_story->AddConnection(m_currentPage->Uuid(), c);
CreateLink(c, startId, endId);
// Draw new link.
ed::Link(m_currentPage->m_links.back()->ed_link->Id, startId, endId);
}
}
}
// You may choose to reject connection between these nodes
// by calling ed::RejectNewItem(). This will allow editor to give
// visual feedback by changing link thickness and color.
}
}
ed::EndCreate(); // Wraps up object creation action handling.
}
// Handle deletion action
if (ed::BeginDelete())
{
ed::NodeId nodeId = 0;
while (ed::QueryDeletedNode(&nodeId))
{
if (ed::AcceptDeletedItem())
{
std::shared_ptr<BaseNodeWidget> node;
if (m_currentPage->GetNode(nodeId, node))
{
// First delete model, then current entry
m_story->DeleteNode(m_currentPage->Uuid(), node->Base()->GetId());
m_currentPage->DeleteNode(nodeId);
}
}
}
// There may be many links marked for deletion, let's loop over them.
ed::LinkId deletedLinkId;
while (ed::QueryDeletedLink(&deletedLinkId))
{
// If you agree that link can be deleted, accept deletion.
if (ed::AcceptDeletedItem())
{
std::shared_ptr<Connection> model;
if (m_currentPage->GetModel(deletedLinkId, model))
{
m_story->DeleteLink(m_currentPage->Uuid(), model);
m_currentPage->EraseLink(deletedLinkId);
}
}
// You may reject link deletion by calling:
// ed::RejectDeletedItem();
}
}
ed::EndDelete(); // Wrap up deletion action
auto openPopupPosition = ImGui::GetMousePos();
ed::Suspend();
if (ed::ShowBackgroundContextMenu())
{
ImGui::OpenPopup("Create New Node");
}
if (ImGui::BeginPopup("Create New Node"))
{
auto newNodePostion = openPopupPosition;
std::shared_ptr<BaseNode> base;
auto nodeTypes = m_nodesFactory.ListOfNodes();
for (auto &type : nodeTypes)
{
if (ImGui::MenuItem(type.name.c_str()))
{
base = m_nodesFactory.CreateNode(type.uuid);
if (base)
{
m_story->AddNode(m_currentPage->Uuid(), base);
auto n = CreateNodeWidget(type.uuid, m_manager, base);
if (n)
{
n->Base()->SetPosition(newNodePostion.x, newNodePostion.y);
n->Initialize();
m_currentPage->AddNode(n);
}
}
}
}
ImGui::EndPopup();
}
if (m_loaded)
{
ed::NavigateToContent();
m_loaded = false;
}
ed::Resume();
ed::End();
ed::SetCurrentEditor(nullptr);
*/
}
else
{
// Set background color to light gray
// ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::Text("Please load or create a project.");
ImGui::PopStyleColor(1); // Pop both colors
}
}
WindowBase::EndDraw();
@ -640,16 +506,6 @@ void NodeEditorWindow::Draw()
void NodeEditorWindow::ToolbarUI()
{
// auto& io = ImGui::GetIO();
// ImVec2 window_pos = ImGui::GetWindowPos();
// ImVec2 window_size = ImGui::GetWindowSize();
// ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoMove;
// ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
// ImGui::Begin("TOOLBAR", NULL, window_flags);
ImGui::SetCursorPos(ImVec2(10, 40));
ImGui::BeginChild("ToolbarChild", ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 1.5f), false, ImGuiWindowFlags_NoScrollbar);
@ -678,16 +534,6 @@ void NodeEditorWindow::ToolbarUI()
}
ImGui::PopID();
// ImGui::End();
ImGui::EndChild(); // Fin de la ChildWindow de la barre d'outils
ImGui::SetCursorPos(ImVec2(0, 0));
// if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
// {
// io.ConfigViewportsNoDecoration = false;
// }
}