Make editor full use of Machine class

This commit is contained in:
Anthony Rabine 2025-10-17 00:45:12 +02:00
parent da1cb31ac2
commit 2b6d9946df
10 changed files with 138 additions and 240 deletions

View file

@ -111,7 +111,8 @@
"*.m": "cpp",
"*.inc": "cpp",
"chip32_binary_format.h": "c",
"hash_map": "c"
"hash_map": "c",
"csignal": "cpp"
}
}

View file

@ -13,6 +13,9 @@
#include <functional>
#include <cstring>
#include <iomanip>
#include <fstream>
#include <vector>
#include <iterator>
#include "chip32_assembler.h"
#include "chip32_binary_format.h"
@ -33,7 +36,7 @@ public:
std::vector<uint8_t> program;
Chip32::Assembler assembler;
chip32_binary_header_t header;
chip32_binary_error_t error;
Machine() {
// Bind syscall handler to this instance
@ -159,10 +162,50 @@ public:
return true;
}
// ========================================================================
// Méthode principale : Parse, Build, Execute
// ========================================================================
void QuickExecute(const std::string &assemblyCode)
bool GetAssemblyLine(uint32_t pointer_counter, uint32_t &line)
{
bool success = false;
// On recherche quelle est la ligne qui possède une instruction à cette adresse
for (auto instr : assembler)
{
if ((instr->addr == pointer_counter) && instr->isRomCode())
{
line = instr->line;
success = true;
break;
}
}
return success;
}
bool LoadBinary(const std::string &filename)
{
bool success = false;
std::ifstream file(filename, std::ios::binary);
if (file)
{
// Méthode 1 : La plus simple avec les itérateurs
program = std::vector<uint8_t>(
std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>()
);
success = true;
}
return success;
}
void SaveBinary(const std::string &filename)
{
std::ofstream o(filename , std::ios::out | std::ios::binary);
o.write(reinterpret_cast<const char*>(program.data()), program.size());
o.close();
}
bool Build(const std::string &assemblyCode)
{
chip32_binary_stats_t stats;
@ -171,7 +214,7 @@ public:
if (!parseResult) {
std::cout << "Parse error: " << assembler.GetLastError().ToString() << std::endl;
return;
return false;
}
// Build binary with new format
@ -180,11 +223,29 @@ public:
if (!buildResult) {
std::cout << "Build error: " << assembler.GetLastError().ToString() << std::endl;
return;
return false;
}
// Load binary using executable format
chip32_binary_error_t error = chip32_binary_load(
if (!LoadCurrentProgram()) {
std::cout << "Binary load error: " << chip32_binary_error_string(error) << std::endl;
buildResult = false;
return false;
}
chip32_binary_build_stats(&header, &stats);
chip32_binary_print_stats(&stats);
return true;
}
void SetEvent(uint32_t ev) {
ctx.registers[R0] = ev;
}
bool LoadCurrentProgram()
{
// Load binary using executable format
error = chip32_binary_load(
&ctx,
program.data(),
static_cast<uint32_t>(program.size()),
@ -193,15 +254,16 @@ public:
&header
);
if (error != CHIP32_BIN_OK) {
std::cout << "Binary load error: " << chip32_binary_error_string(error) << std::endl;
buildResult = false;
return;
}
return error == CHIP32_BIN_OK;
}
// ========================================================================
// Méthode principale : Parse, Build, Execute
// ========================================================================
void QuickExecute(const std::string &assemblyCode)
{
Build(assemblyCode);
chip32_binary_build_stats(&header, &stats);
chip32_binary_print_stats(&stats);
// Set syscall handler using wrapper
ctx.syscall = SyscallWrapper;
ctx.user_data = this;
@ -544,4 +606,4 @@ private:
}
};
} // namespace Chip32
} // namespace Chip32

View file

@ -54,14 +54,16 @@ void StoryProject::CopyToDevice(const std::string &outputDir, NodesFactory &fact
std::cout << code << std::endl;
Chip32::Assembler::Error err;
if (GenerateBinary(code, err))
{
std::filesystem::copy(BinaryFileName(), destRootDir, std::filesystem::copy_options::overwrite_existing);
// FIXME génération
// Convert resources (if necessary) and copy them to destination assets
manager.ConvertResources(AssetsPath(), destAssetsDir, m_storyOptions.image_format, m_storyOptions.sound_format);
}
// Chip32::Assembler::Error err;
// if (GenerateBinary(code, err))
// {
// std::filesystem::copy(BinaryFileName(), destRootDir, std::filesystem::copy_options::overwrite_existing);
// // Convert resources (if necessary) and copy them to destination assets
// manager.ConvertResources(AssetsPath(), destAssetsDir, m_storyOptions.image_format, m_storyOptions.sound_format);
// }
}
void StoryProject::New(const std::string &uuid, const std::string &library_path)
@ -86,14 +88,6 @@ std::filesystem::path StoryProject::BinaryFileName() const
return m_working_dir / "story.c32";
}
void StoryProject::SaveBinary()
{
std::ofstream o(BinaryFileName() , std::ios::out | std::ios::binary);
o.write(reinterpret_cast<const char*>(m_program.data()), m_program.size());
o.close();
}
bool StoryProject::ParseStoryInformation(nlohmann::json &j)
{
bool success = false;
@ -321,17 +315,6 @@ bool StoryProject::ModelFromJson(const nlohmann::json &model, NodesFactory &fact
return success;
}
bool StoryProject::CopyProgramTo(uint8_t *memory, uint32_t size)
{
bool success = false;
// Update ROM memory
if (m_program.size() < size)
{
std::copy(m_program.begin(), m_program.end(), memory);
success = true;
}
return success;
}
std::vector<IStoryProject::FunctionInfo> StoryProject::GetFunctionsList() const
{
@ -357,27 +340,6 @@ std::vector<IStoryProject::FunctionInfo> StoryProject::GetFunctionsList() const
return functions;
}
bool StoryProject::GetAssemblyLine(uint32_t pointer_counter, uint32_t &line)
{
bool success = false;
// On recherche quelle est la ligne qui possède une instruction à cette adresse
std::vector<Chip32::Instr>::const_iterator ptr = m_assembler.Begin();
for (; ptr != m_assembler.End(); ++ptr)
{
if ((ptr->addr == pointer_counter) && ptr->isRomCode())
{
break;
}
}
if (ptr != m_assembler.End())
{
line = ptr->line;
success = true;
}
return success;
}
std::list<std::shared_ptr<Connection>> StoryProject::GetNodeConnections(const std::string &nodeId)
{
@ -542,37 +504,6 @@ bool StoryProject::GenerateCompleteProgram(std::string &assembly)
return true;
}
bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err)
{
Chip32::Result result;
bool success = false;
if (m_assembler.Parse(code) == true)
{
if (m_assembler.BuildBinary(m_program, result) == true)
{
result.Print();
m_log.Log("Binary successfully generated.");
SaveBinary();
success = true;
}
else
{
err = m_assembler.GetLastError();
}
}
else
{
err = m_assembler.GetLastError();
m_log.Log(err.ToString(), true);
}
return success;
}
bool StoryProject::Load(ResourceManager &manager, NodesFactory &factory)
{
try {

View file

@ -31,44 +31,22 @@ public:
return &m_selected;
}
// std::shared_ptr<StoryProject> shared_from_this() {
// return shared_from_this();
// }
std::string MainUuid() const {
return "490745ab-df4d-476d-ae27-027e94b8ee0a";
}
bool FindMain(Chip32::Instr &mainLine) {
std::shared_ptr<Chip32::Instr> m;
bool success = m_assembler.GetMain(m);
if (success)
{
mainLine = *m;
}
return success;
}
bool GenerateCompleteProgram(std::string &assembly);
void New(const std::string &uuid, const std::string &library_path);
std::filesystem::path BinaryFileName() const;
bool GenerateBinary(const std::string &code, Chip32::Assembler::Error &err);
bool Load(ResourceManager &manager, NodesFactory &factory);
void Save(ResourceManager &manager);
void SaveBinary();
void SetPaths(const std::string &uuid, const std::string &library_path);
void CopyToDevice(const std::string &outputDir, NodesFactory &factory);
void ModelToJson(nlohmann::json &model);
bool ModelFromJson(const nlohmann::json &model, NodesFactory &factory);
bool CopyProgramTo(uint8_t *memory, uint32_t size);
// returns >= 0 on success
bool GetAssemblyLine(uint32_t pointer_counter, uint32_t &line);
void Clear();
void Select(bool selected) { m_selected = selected; }
@ -148,10 +126,6 @@ private:
std::unordered_set<std::string> m_usedLabels; // permet de ne pas générer un label qui existe déjà
std::filesystem::path m_assetsPath;
Chip32::Assembler m_assembler;
std::vector<uint8_t> m_program;
std::list<std::shared_ptr<StoryPage>> m_pages;
std::vector<std::shared_ptr<Variable>> m_variables;

View file

@ -27,23 +27,9 @@ AppController::AppController(ILogger& logger, EventBus& eventBus)
, m_player(*this) // m_player a besoin d'un IAudioEvent
, m_webServer(m_libraryManager)
{
// VM Initialize - Déplacé du constructeur de MainWindow
m_chip32_ctx.stack_size = 512;
m_chip32_ctx.rom.mem = m_rom_data;
m_chip32_ctx.rom.addr = 0;
m_chip32_ctx.rom.size = sizeof(m_rom_data);
m_chip32_ctx.ram.mem = m_ram_data;
m_chip32_ctx.ram.addr = sizeof(m_rom_data);
m_chip32_ctx.ram.size = sizeof(m_ram_data);
// Initialise le trampoline de syscall avec cette instance
// SyscallTrampoline::s_instance = this;
// m_chip32_ctx.syscall = SyscallTrampoline::Callback;
Callback<uint8_t(chip32_ctx_t *, uint8_t)>::func = std::bind(&AppController::Syscall, this, std::placeholders::_1, std::placeholders::_2);
m_chip32_ctx.syscall = static_cast<syscall_t>(Callback<uint8_t(chip32_ctx_t *, uint8_t)>::callback);
m_machine.ctx.syscall = static_cast<syscall_t>(Callback<uint8_t(chip32_ctx_t *, uint8_t)>::callback);
// Assurez-vous que ces fonctions existent ou sont implémentées ailleurs
// CloseProject() et CloseModule() étaient dans MainWindow
@ -247,38 +233,20 @@ void AppController::Build(bool compileonly)
m_resources.ConvertResources(m_story->AssetsPath(), "", options.image_format, options.sound_format);
}
Chip32::Assembler::Error err;
// La GUI (DebuggerWindow) doit être notifiée pour effacer les erreurs. FIXME
// m_debuggerWindow.ClearErrors();
if (m_story->GenerateBinary(m_storyAssembly, err))
if (m_machine.Build(m_storyAssembly))
{
m_result.Print(); // Imprime le résultat de l'assemblage (Debug uniquement)
m_machine.SaveBinary(m_story->BinaryFileName());
m_dbg.run_result = VM_READY;
UpdateVmView(); // Notifie la GUI de mettre à jour la vue VM
m_logger.Log("Build successful. VM ready.");
if (m_story->CopyProgramTo(m_rom_data, sizeof (m_rom_data)))
{
m_story->SaveBinary();
chip32_initialize(&m_chip32_ctx);
Chip32::Instr mainLine;
if (m_story->FindMain(mainLine)) {
m_chip32_ctx.registers[PC] = mainLine.addr;
}
m_dbg.run_result = VM_READY;
UpdateVmView(); // Notifie la GUI de mettre à jour la vue VM
m_logger.Log("Build successful. VM ready.");
}
else
{
m_logger.Log("Program too big. Expand ROM memory.", true);
}
m_eventBus.Emit(std::make_shared<GenericResultEvent>(false, "Build success"));
}
else
{
m_logger.Log(err.ToString(), true);
// La GUI (DebuggerWindow) doit être notifiée pour ajouter l'erreur. FIXME
// m_debuggerWindow.AddError(err.line, err.message);
auto err = m_machine.assembler.GetLastError(); // FIXME: l'erreur ne vient pas uniquement de l'assembleur, enfouir cela et remonter d'autres erreurs liées à la machine
m_eventBus.Emit(std::make_shared<GenericResultEvent>(false, "Build error: " + err.ToString()));
}
}
@ -297,44 +265,22 @@ void AppController::BuildModule(bool compileonly)
m_logger.Log(m_moduleAssembly);
m_logger.Log("============================");
// Try to compile the module code directly
Chip32::Assembler::Error err;
if (m_module->GenerateBinary(m_moduleAssembly, err))
if (m_machine.Build(m_moduleAssembly))
{
m_logger.Log("Module compiled successfully!");
// Save the binary to disk
m_module->SaveBinary();
// Load into VM for testing
if (m_module->CopyProgramTo(m_rom_data, sizeof(m_rom_data)))
{
chip32_initialize(&m_chip32_ctx);
Chip32::Instr mainLine;
if (m_module->FindMain(mainLine)) {
m_chip32_ctx.registers[PC] = mainLine.addr;
}
m_dbg.run_result = VM_READY;
UpdateVmView();
m_logger.Log("Module binary ready for testing.");
m_eventBus.Emit(std::make_shared<ModuleEvent>(ModuleEvent::Type::BuildSuccess, m_module->GetUuid()));
}
else
{
auto errObj = std::make_shared<ModuleEvent>(ModuleEvent::Type::BuildFailure, m_module->GetUuid());
errObj->SetFailure("Module program too big. Expand ROM memory.", -1);
m_eventBus.Emit(errObj);
}
m_machine.SaveBinary(m_module->BinaryFileName());
m_dbg.run_result = VM_READY;
UpdateVmView(); // Notifie la GUI de mettre à jour la vue VM
m_eventBus.Emit(std::make_shared<ModuleEvent>(ModuleEvent::Type::BuildSuccess, m_module->GetUuid()));
}
else
{
// Compilation failed - show error
auto err = m_machine.assembler.GetLastError(); // FIXME: l'erreur ne vient pas uniquement de l'assembleur, enfouir cela et remonter d'autres erreurs liées à la machine
std::string errorMsg = err.ToString();
auto errObj = std::make_shared<ModuleEvent>(ModuleEvent::Type::BuildFailure, m_module->GetUuid());
errObj->SetFailure(errorMsg, err.line);
m_eventBus.Emit(errObj);
@ -372,32 +318,15 @@ void AppController::SetExternalSourceFile(const std::string &filename)
void AppController::LoadBinaryStory(const std::string &filename)
{
FILE *fp = fopen(filename.c_str(), "rb");
if (fp != NULL)
{
fseek(fp, 0L, SEEK_END);
long int sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if (sz <= m_chip32_ctx.rom.size)
{
size_t sizeRead = fread(m_chip32_ctx.rom.mem, 1, sz, fp); // Corrected fread args
if (sizeRead == (size_t)sz) // Cast sz to size_t for comparison
{
m_dbg.run_result = VM_READY;
chip32_initialize(&m_chip32_ctx);
m_logger.Log("Loaded binary file: " + filename);
UpdateVmView();
}
else
{
m_logger.Log("Failed to load binary file completely. Read " + std::to_string(sizeRead) + " of " + std::to_string(sz) + " bytes.", true);
}
} else {
m_logger.Log("Binary file is too large for ROM: " + std::to_string(sz) + " bytes, max " + std::to_string(m_chip32_ctx.rom.size) + " bytes.", true);
}
fclose(fp);
} else {
if (m_machine.LoadBinary(filename))
{
m_dbg.run_result = VM_READY;
m_logger.Log("Loaded binary file: " + filename);
UpdateVmView();
}
else
{
m_logger.Log("Failed to open binary file: " + filename, true);
}
}
@ -422,7 +351,7 @@ uint32_t AppController::GetRegister(int reg)
uint32_t regVal = 0;
if (reg >= 0 && reg < REGISTER_COUNT) // Assurez-vous que REGISTER_COUNT est défini
{
regVal = m_chip32_ctx.registers[reg];
regVal = m_machine.ctx.registers[reg];
}
return regVal;
}
@ -620,7 +549,7 @@ void AppController::ProcessStory()
{
if (m_dbg.IsValidEvent(EV_MASK_OK_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_OK_BUTTON;
m_machine.SetEvent(EV_MASK_OK_BUTTON);
m_dbg.run_result = VM_OK;
}
}
@ -628,7 +557,7 @@ void AppController::ProcessStory()
{
if (m_dbg.IsValidEvent(EV_MASK_PREVIOUS_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_PREVIOUS_BUTTON;
m_machine.SetEvent(EV_MASK_PREVIOUS_BUTTON);
m_dbg.run_result = VM_OK;
}
}
@ -636,7 +565,7 @@ void AppController::ProcessStory()
{
if (m_dbg.IsValidEvent(EV_MASK_NEXT_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_NEXT_BUTTON;
m_machine.SetEvent(EV_MASK_NEXT_BUTTON);
m_dbg.run_result = VM_OK;
}
}
@ -644,7 +573,7 @@ void AppController::ProcessStory()
{
if (m_dbg.IsValidEvent(EV_MASK_END_OF_AUDIO))
{
m_chip32_ctx.registers[R0] = EV_MASK_END_OF_AUDIO;
m_machine.SetEvent(EV_MASK_END_OF_AUDIO);
m_dbg.run_result = VM_OK;
}
}
@ -652,7 +581,7 @@ void AppController::ProcessStory()
{
if (m_dbg.IsValidEvent(EV_MASK_HOME_BUTTON))
{
m_chip32_ctx.registers[R0] = EV_MASK_HOME_BUTTON;
m_machine.SetEvent(EV_MASK_HOME_BUTTON);
m_dbg.run_result = VM_OK;
}
}
@ -705,7 +634,7 @@ void AppController::ProcessStory()
void AppController::StepInstruction()
{
m_dbg.run_result = chip32_step(&m_chip32_ctx);
m_dbg.run_result = chip32_step(&m_machine.ctx);
UpdateVmView();
}
@ -788,11 +717,11 @@ void AppController::UpdateVmView()
// C'est une fonction de notification pour la GUI.
// AppController ne devrait pas directement manipuler les vues GUI.
// Au lieu de cela, il émettrait un signal ou appellerait un observer.
uint32_t pcVal = m_chip32_ctx.registers[PC];
uint32_t pcVal = m_machine.ctx.registers[PC];
if (m_story)
{
if (m_story->GetAssemblyLine(pcVal, m_dbg.line))
if (m_machine.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
@ -835,7 +764,6 @@ void AppController::CloseProject()
m_dbg.run_result = VM_FINISHED;
m_dbg.free_run = false;
m_dbg.m_breakpoints.clear();
chip32_initialize(&m_chip32_ctx); // Reset VM context
m_resources.Clear(); // Clear loaded resources
m_eventQueue.clear(); // Clear any pending VM events

View file

@ -25,6 +25,7 @@
#include "variable.h" // Pour Variable (si géré par AppController)
#include "debug_context.h" // Pour DebugContext
#include "event_bus.h"
#include "chip32_machine.h"
// Forward declaration pour éviter les dépendances circulaires si le logger est une GUI
class ILogger; // Peut être implémenté par la console_window par exemple
@ -98,7 +99,7 @@ public:
void LoadParams();
// Méthodes pour interagir avec la VM et le débogueur
chip32_ctx_t* GetChip32Context() { return &m_chip32_ctx; }
chip32_ctx_t* GetChip32Context() { return &m_machine.ctx; }
DebugContext* GetDebugContext() { return &m_dbg; }
void ProcessStory();
@ -130,15 +131,13 @@ private:
std::shared_ptr<StoryProject> m_story;
std::shared_ptr<StoryProject> m_module;
uint8_t m_rom_data[16*1024];
uint8_t m_ram_data[16*1024];
chip32_ctx_t m_chip32_ctx;
Chip32::Result m_result;
Chip32::Machine m_machine;
DebugContext m_dbg; // Contexte de débogage
std::string m_storyAssembly;
std::string m_moduleAssembly;
std::string m_externalSourceFileName;
std::vector<std::string> m_recentProjects;
std::string m_externalSourceFileName;
NodesFactory m_nodesFactory;
ResourceManager m_resources; // Gère les ressources (images, sons)

View file

@ -24,10 +24,10 @@ struct DebugContext
return (event_mask & event) != 0;
}
static void DumpCodeAssembler(Chip32::Assembler & assembler) {
static void DumpCodeAssembler(Chip32::Assembler & assembler)
{
for (std::vector<Chip32::Instr>::const_iterator iter = assembler.Begin();
iter != assembler.End(); ++iter)
for (auto iter : assembler)
{
if (iter->isRomCode() || iter->isRomData)
{

View file

@ -568,8 +568,6 @@ bool MainWindow::Loop()
if (m_appController.IsLibraryManagerInitialized())
{
bool nodeEditorFocused = m_nodeEditorWindow.IsFocused();
bool moduleEditorFocused = m_moduleEditorWindow.IsFocused();

View file

@ -29,6 +29,7 @@
#include "node_widget_factory.h"
#include "Localization.h"
#include "LanguageSelector.h"
#include "chip32_machine.h"
class MainWindow : public std::enable_shared_from_this<MainWindow>, public ILogSubject
{

View file

@ -0,0 +1,4 @@
[Project]
CreatedFrom=CMakeLists.txt
Manager=KDevCMakeManager
Name=story-editor