I'm writing a simple emulator for the NES. I'm trying to use SDL2 for the graphics, with ImGui (SDL renderer, not necessarily limited to it).
It all worked with the corresponding example on github, until I did some refactoring and moved everything graphics related to its own class.
Now the program won't start, throwing a SIGILL error (code -1073741795), on ImGui_ImplSDLRenderer2_Init(renderer);
. If I comment it out, then the error get's thrown one line before it, on ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);
.
Debugging the error shows that the error happens when one of the two functions try to call ImGui::GetIO()
. I tried everything but nothing seems to work.
Here is my code.
Graphics.h
#include "imgui.h"
#include "imgui_memory_editor.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_sdlrenderer2.h"
#include <SDL.h>
struct WindowSettings {
explicit WindowSettings(const char *title, int width, int height, bool use_default = true);
SDL_WindowFlags window_flags;
SDL_RendererFlags renderer_flags;
ImGuiConfigFlags_ imgui_flags;
int width, height;
const char* title;
};
class Window {
public:
Window(WindowSettings *settings);
~Window();
int init();
void quit();
bool poll_events();
bool should_close() { return event.type == SDL_QUIT || (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)); }
void start_imgui_frame();
void render();
void set_background_color(float r, float g, float b, float a) { background = {r, g, b, a}; }
private:
WindowSettings *settings;
SDL_Window *window;
SDL_Renderer *renderer;
ImGuiIO *io;
SDL_Event event;
ImVec4 background = {58, 58, 77, 255};
};
Graphics.cpp
#include "Graphics.h"
WindowSettings::WindowSettings(const char *title, int width, int height, bool use_default) {
this->title = title;
this->width = width;
this->height = height;
if (!use_default) return;
window_flags = (SDL_WindowFlags)(SDL_WINDOW_ALLOW_HIGHDPI);
renderer_flags = (SDL_RendererFlags)(SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
imgui_flags = ImGuiConfigFlags_NavEnableKeyboard;
}
Window::Window(WindowSettings *settings) {
this->settings = settings;
window = nullptr;
renderer = nullptr;
}
Window::~Window() {
quit();
}
void Window::quit() {
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
int Window::init() {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
window = SDL_CreateWindow(settings->title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, settings->width, settings->height, settings->window_flags);
if (window == nullptr) {
printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
return -1;
}
renderer = SDL_CreateRenderer(window, -1, settings->renderer_flags);
if (renderer == nullptr) {
SDL_Log("Error creating SDL_Renderer!");
return 0;
}
SDL_RendererInfo info;
SDL_GetRendererInfo(renderer, &info);
SDL_Log("Current SDL_Renderer: %s", info.name);
IMGUI_CHECKVERSION();
ImGui::CreateContext();
io = &ImGui::GetIO(); (void)io;
io->ConfigFlags |= settings->imgui_flags;
ImGui::StyleColorsDark();
ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);
ImGui_ImplSDLRenderer2_Init(renderer); // Error line
}
bool Window::poll_events() {
if (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
return true;
}
return false;
}
void Window::start_imgui_frame() { // NOLINT(*-convert-member-functions-to-static)
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
}
void Window::render() {
ImGui::Render();
SDL_RenderSetScale(renderer, io->DisplayFramebufferScale.x, io->DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(renderer, (Uint8)background.x, (Uint8)background.y, (Uint8)background.z, (Uint8)background.w);
SDL_RenderClear(renderer);
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer);
SDL_RenderPresent(renderer);
}
main.cpp
#include "imgui.h"
#include "imgui_memory_editor.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_sdlrenderer2.h"
#include <SDL.h>
#include "Cpu.h"
#include "Graphics.h"
int main(int, char**)
{
WindowSettings settings("NesMu - alpha1.0.0", 800, 800);
Window *win = new Window(&settings);
win->init();
uint8_t program[] = {0XA9, 0XC0, 0XAA, 0XE8, 0X00};
CPU cpu;
cpu.set_demo(program);
bool end = false;
static MemoryEditor prog_editor;
prog_editor.ReadOnly = true;
// Main loop
bool done = false;
while (!done)
{
while (win->poll_events())
{
if (win->should_close()) done = true;
}
win->start_imgui_frame();
{
ImGui::Begin("CPU Registers");
ImGui::Text("PC: %04X", cpu.getPC());
ImGui::Text("A: %02X", cpu.getA());
ImGui::Text("X: %02X", cpu.getX());
ImGui::Text("Y: %02X", cpu.getY());
ImGui::Text("SP: %02X", cpu.getSP());
ImGui::Text("Flags: %08p", cpu.getFlags());
if (ImGui::Button("Step", {60, 20}) && !end) end = cpu.step();
ImGui::SameLine();
if (ImGui::Button("Reset", {60, 20})) { cpu.reset(); end = false; }
ImGui::End();
}
strcpy(reinterpret_cast<char *>(program), reinterpret_cast<const char *>(cpu.getDemo()));
prog_editor.DrawWindow("Program memory", program, sizeof(program));
prog_editor.GotoAddrAndHighlight(cpu.getPC(), cpu.getPC() + 1);
win->render();
}
win->quit();
return 0;
}
Could someone please help me?
Edit 1 As requested here are the Assembly instruction and the full call stack. This seems to be the instruction:
ud2
As per the call stack, Clion is only letting me retrieve the stack for the current thread. Here it is:
Window::init Graphics.cpp:67
SDL_main main.cpp:15
Solution:
After I decided to setup a WSL toolchain in Clion, the c++ compiler warned me of a missing return statement in the Window::init()
function.
Added the statement at the end, everything started to work as intended again.
I still don't know why neither g++ nor gcc warned me about this.
Thank to everyone who tried to help me.