1

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
7
  • run your code in a debugger and show us which exact assembly instruction produces the SIGILL. As well as the full call stack Commented Jul 24, 2024 at 7:23
  • I managed to retrieve only what I added to the post, if I need to add anything else let me know and I'll try to get it Commented Jul 24, 2024 at 15:25
  • Have you tried things like "address sanitizer" (asan), "undefined behavior sanitizer" (ubsan), "memory sanitizer" (msan), clang-tidy, raising your compilers warning level, etc? To maybe get a hint about the issue. Commented Jul 24, 2024 at 15:41
  • Could it be a case of mixing 32 & 64 bit code? Mixing objects built for debug versus release? Mixing objects built by different compilers? Commented Jul 24, 2024 at 15:44
  • Can you show the full call stack, not just your code? Commented Jul 24, 2024 at 15:52

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Ah. Good find. Make it a habit to compile with -Wall or the equivalent in MSVC, it should complain about that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.