commit 8d93de4be7ab6faa7bfffd431b0d541f599014b9 Author: Sebastian Mihai Pantoc Date: Tue May 12 11:15:47 2026 +0200 test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8ea558 --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +# Ignore Build folder +build/ +# ignore .DS_Store on macos +.DS_Store +# ingore .cache folder +.cache +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# debug information files +*.dwo diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8b962ca --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 4.1.1) +project(chippotto) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +include(FetchContent) +FetchContent_Declare( + SDL3 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG 0f8d062e1084d269d6437b4d1173148e14a7da74# release 3.4 +) +FetchContent_MakeAvailable(SDL3) + +file(GLOB src CONFIGURE_DEPENDS src/*.cpp) + +add_executable(${PROJECT_NAME} ${src}) +file(COPY src/assets/audio.wav DESTINATION assets) +target_link_libraries(${PROJECT_NAME} SDL3::SDL3) diff --git a/Chip8-Tests/1-chip8-logo.ch8 b/Chip8-Tests/1-chip8-logo.ch8 new file mode 100644 index 0000000..19c5cf3 Binary files /dev/null and b/Chip8-Tests/1-chip8-logo.ch8 differ diff --git a/Chip8-Tests/15 Puzzle [Roger Ivie].ch8 b/Chip8-Tests/15 Puzzle [Roger Ivie].ch8 new file mode 100644 index 0000000..251e416 Binary files /dev/null and b/Chip8-Tests/15 Puzzle [Roger Ivie].ch8 differ diff --git a/Chip8-Tests/2-ibm-logo.ch8 b/Chip8-Tests/2-ibm-logo.ch8 new file mode 100644 index 0000000..677f227 Binary files /dev/null and b/Chip8-Tests/2-ibm-logo.ch8 differ diff --git a/Chip8-Tests/3-corax+.ch8 b/Chip8-Tests/3-corax+.ch8 new file mode 100644 index 0000000..9fc874c Binary files /dev/null and b/Chip8-Tests/3-corax+.ch8 differ diff --git a/Chip8-Tests/4-flags.ch8 b/Chip8-Tests/4-flags.ch8 new file mode 100644 index 0000000..0698a10 Binary files /dev/null and b/Chip8-Tests/4-flags.ch8 differ diff --git a/Chip8-Tests/5-quirks.ch8 b/Chip8-Tests/5-quirks.ch8 new file mode 100644 index 0000000..1c87f6c Binary files /dev/null and b/Chip8-Tests/5-quirks.ch8 differ diff --git a/Chip8-Tests/6-keypad.ch8 b/Chip8-Tests/6-keypad.ch8 new file mode 100644 index 0000000..4d1ecdc Binary files /dev/null and b/Chip8-Tests/6-keypad.ch8 differ diff --git a/Chip8-Tests/7-beep.ch8 b/Chip8-Tests/7-beep.ch8 new file mode 100644 index 0000000..27c205f Binary files /dev/null and b/Chip8-Tests/7-beep.ch8 differ diff --git a/Chip8-Tests/8-scrolling.ch8 b/Chip8-Tests/8-scrolling.ch8 new file mode 100644 index 0000000..ae1c970 Binary files /dev/null and b/Chip8-Tests/8-scrolling.ch8 differ diff --git a/Chip8-Tests/Space Invaders [David Winter].ch8 b/Chip8-Tests/Space Invaders [David Winter].ch8 new file mode 100644 index 0000000..3ada8df Binary files /dev/null and b/Chip8-Tests/Space Invaders [David Winter].ch8 differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..8426656 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +A Chip8 Emulator made in modern C++ and SDL3 +Passes all the tests for Timendus' Test Suite. + +To install: + 1. Create a "build" directory by running $ mkdir build + 2. $ cd build + 3. $ cmake .. && cmake --build . + 4. run by $ ./chippotto + diff --git a/src/assets/audio.wav b/src/assets/audio.wav new file mode 100644 index 0000000..32aa8a3 Binary files /dev/null and b/src/assets/audio.wav differ diff --git a/src/chip8.cpp b/src/chip8.cpp new file mode 100644 index 0000000..bf52f39 --- /dev/null +++ b/src/chip8.cpp @@ -0,0 +1,280 @@ +#include "chip8.hpp" +#include "SDL3/SDL_log.h" +#include "utils.hpp" +#include +#include +#include +#include +#include + +Chip8::Chip8(std::ifstream &file, Screen *screen) { + for (auto i{0}; i < MEMORY_SIZE; ++i) { + mem[i] = {0}; + } + file.seekg(0, std::ios::end); + rom_size = file.tellg(); + file.seekg(std::ios::beg); + char ch; + while (file.read(&ch, 1)) { + mem[pc++] = static_cast(ch); + } + pc = {0x200}; + for (auto i{0}; i < 0xF; ++i) { + v[i] = 0; + keypad_[i] = 0; + } + sound_ = 0; + delay_ = 0; + halted = {false}; + + screen_ = {screen}; + load_font(); +} +Chip8::~Chip8() {} +Display &Chip8::display() { return display_; } +Keypad &Chip8::keypad() { return keypad_; } +u8 &Chip8::sound() { return sound_; } +u8 &Chip8::delay() { return delay_; } + +void Chip8::load_font() { + for (auto i{0}; i < 80; ++i) { + mem[i] = font[i]; + } +} + +u16 Chip8::fetch() { + u16 opcode = (mem[pc] << 8) | mem[pc + 1]; + pc += 2; + return opcode; +} + +void Chip8::execute(u16 opcode) { + if (opcode == 0x0000) { + return; + } + u8 X = {static_cast((opcode & 0x0F00) >> 8)}; + u8 Y = {static_cast((opcode & 0x00F0) >> 4)}; + u16 NN = {static_cast((opcode & 0x00FF))}; + u16 NNN = {static_cast((opcode & 0x0FFF))}; + + switch (opcode & 0xF000) { + case 0x0000: + switch (opcode & 0x000F) { + case 0x0000: + display_.clear(); + break; + case 0x000E: + pc = {stack.pop()}; + break; + default: + break; + } + break; + case 0x1000: + pc = {NNN}; + break; + case 0x2000: + stack.push(pc); + pc = {NNN}; + break; + case 0x3000: + if (v[X] == (NN)) { + pc += 2; + } + break; + case 0x4000: + if (v[X] != (NN)) { + pc += 2; + } + break; + case 0x5000: + if (v[X] == v[Y]) { + pc += 2; + } + break; + case 0x6000: + v[X] = {static_cast(NN)}; + break; + case 0x7000: + v[X] += {static_cast(NN)}; + break; + case 0x8000: { + switch (opcode & 0x000F) { + case 0x0000: + v[X] = {v[Y]}; + break; + case 0x0001: + v[X] |= {v[Y]}; + break; + case 0x0002: + v[X] &= {v[Y]}; + break; + case 0x0003: + v[X] ^= {v[Y]}; + break; + case 0x0004: { + u8 tmp = {0}; + if ((static_cast(v[X]) + static_cast(v[Y])) > 255) { + tmp = {1}; + } + v[X] += v[Y]; + v[0xF] = tmp; + } break; + case 0x0005: { + u8 tmp = {static_cast((v[X] < v[Y]) ? 0 : 1)}; + v[X] -= v[Y]; + v[0xF] = tmp; + } break; + case 0x0006: { + u8 tmp = {static_cast(v[Y] & 0x01)}; + v[X] = v[Y] >> 1; + v[0xF] = tmp; + } break; + case 0x0007: + v[X] = {static_cast(v[Y] - v[X])}; + v[0xF] = (v[Y] < v[X]) ? 0 : 1; + break; + case 0x000E: { + u8 tmp = {static_cast((v[Y] & 0x80) >> 7)}; + v[X] = v[Y] << 1; + v[0xF] = tmp; + } break; + } + } break; + case 0x9000: + if (v[X] != v[Y]) { + pc += 2; + } + break; + case 0xA000: + i = {static_cast(NNN)}; + break; + case 0xB000: + pc = {static_cast(NNN + v[0])}; + break; + case 0xC000: { + std::random_device device; + std::mt19937 rng(device()); + std::uniform_int_distribution<> distribution(0, 255); + u8 random{static_cast(distribution(rng))}; + v[X] = random & NN; + } break; + case 0xD000: { + u8 x = v[X] & 63; + u8 y = v[Y] & 31; + for (auto N{0}; N < (opcode & 0x000F); ++N) { + if (y + N > 31) { + break; + } + u8 byte{mem[i + N]}; + for (auto k{0}; k < 8; ++k) { + if (x + k > 63) { + break; + } + if (u8 bit{static_cast(byte & (0x80 >> k))}) { + if (display_(x + k, y + N)) { + v[0xF] = {true}; + } + display_(x + k, y + N) ^= 1; + } + } + } + display_.should_draw() = true; + } break; + case 0xE000: + switch (opcode & 0x000F) { + case 0x000E: + if (keypad_[v[X]]) { + pc += 2; + } + break; + case 0x0001: + if (!keypad_[v[X]]) { + pc += 2; + } + break; + } + break; + case 0xF000: + switch (opcode & 0x000F) { + case 0x0003: { + u8 number = v[X]; + int l = 2; + while (l >= 0) { + mem[i + l] = {static_cast(number % 10)}; + number /= 10; + --l; + } + } break; + case 0x0005: + switch (opcode & 0x00F0) { + case 0x0010: + delay_ = v[X]; + break; + case 0x0050: + for (auto j{0}; j <= X; ++j) { + mem[i + j] = v[j]; + } + break; + case 0x0060: + for (auto j{0}; j <= X; ++j) { + v[j] = mem[i + j]; + } + break; + } + break; + + case 0x0007: + v[X] = delay_; + break; + case 0x0008: + sound_ = v[X]; + break; + case 0x0009: + i = (v[X] & 0x000F) * 5; + break; + case 0x000A: { + halted = {true}; + int pressed_key; + for (auto i{0}; i < 16; ++i) { + if (keypad_[i]) { + current_key = i; + break; + } + } + if (current_key != -1) { + for (auto i{0}; i < 16; ++i) { + if (!keypad_[current_key]) { + halted = {false}; + v[X] = i; + current_key = {-1}; + } + } + } + if (halted) { + pc -= 2; + } + break; + } + case 0x000E: + i += v[X]; + v[0xF] = (i > NNN) ? 1 : 0; + break; + } + break; + default: + SDL_Log("OPCODE not recognized: 0x%04x", opcode); + } +} + +void Chip8::cycle() { execute(fetch()); } + +void Chip8::update_timers() { + if (delay_ > 0) { + --delay_; + } + if (sound_ > 0) { + screen_->beep(); + --sound_; + } +} diff --git a/src/chip8.hpp b/src/chip8.hpp new file mode 100644 index 0000000..604723f --- /dev/null +++ b/src/chip8.hpp @@ -0,0 +1,40 @@ +#include "display.hpp" +#include "keypad.hpp" +#include "screen.hpp" +#include "stack.hpp" +#include "utils.hpp" +#include +#include +#include +#include +#include +class Chip8 { +public: + Chip8(std::ifstream &file, Screen *screen); + ~Chip8(); + void cycle(); + void update_timers(); + u8 &delay(); + u8 &sound(); + Display &display(); + Keypad &keypad(); + +private: + u16 fetch(); + void execute(u16 opcode); + void load_font(); + + Screen *screen_; + Display display_; + Keypad keypad_; + u16 pc{0x200}; + u16 i; + Stack stack; + u8 delay_; + u8 sound_; + bool halted; + int current_key{-1}; + std::array v; + std::array mem; + std::size_t rom_size; +}; diff --git a/src/display.cpp b/src/display.cpp new file mode 100644 index 0000000..233a94d --- /dev/null +++ b/src/display.cpp @@ -0,0 +1,25 @@ +#include "display.hpp" +#include "utils.hpp" +#include + +Display::Display() { + clear(); + should_draw_ = {false}; +} +Display::~Display() {} +void Display::draw() {} +bool &Display::operator()(int i, int j) { + if (i < 0 || i > 63 || j < 0 || j > 31) throw std::out_of_range("Porcoddio"); + return pixels_[j * 64 + i]; +} +bool *Display::pixels() { + return pixels_; +} + +bool &Display::should_draw() { return should_draw_; } +void Display::clear() { + for (auto i{0}; i < DISPLAY_WIDTH * DISPLAY_HEIGHT; ++i) { + pixels_[i] = {false}; + } + should_draw_ = {true}; +} diff --git a/src/display.hpp b/src/display.hpp new file mode 100644 index 0000000..bbe7bfa --- /dev/null +++ b/src/display.hpp @@ -0,0 +1,22 @@ +#ifndef DISPLAY + +#define DISPLAY +#include "utils.hpp" +#include +#include +#include +class Display { +public: + Display(); + ~Display(); + void draw(); + bool &operator()(int i, int j); + bool &should_draw(); + bool *pixels(); + void clear(); + +private: + bool pixels_[2048]; + bool should_draw_; +}; +#endif // !DISPLAY diff --git a/src/keypad.cpp b/src/keypad.cpp new file mode 100644 index 0000000..9245360 --- /dev/null +++ b/src/keypad.cpp @@ -0,0 +1,11 @@ +#include "keypad.hpp" +#include + +Keypad::Keypad() { + for (auto i{0}; i < 16; ++i) { + keys[i] = {false}; + } +} +Keypad::~Keypad() {} + +bool &Keypad::operator[](int i) { return keys[i]; } diff --git a/src/keypad.hpp b/src/keypad.hpp new file mode 100644 index 0000000..b61770e --- /dev/null +++ b/src/keypad.hpp @@ -0,0 +1,14 @@ +#ifndef KEYPAD +#define KEYPAD +#include +constexpr unsigned int KEYS = 16; +class Keypad { +public: + Keypad(); + ~Keypad(); + bool& operator[](int i); + +private: + std::array keys; +}; +#endif // !KEYPAD diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..2e6f548 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,51 @@ +#include "SDL3/SDL_stdinc.h" +#include "SDL3/SDL_timer.h" +#include "chip8.hpp" +#include "utils.hpp" +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + std::vector args{convert_args(argc, argv)}; + if (args.size() != 2) { + std::cerr << "Incorrect Usage, ./chippotto /path/to/rom" << std::endl; + exit(1); + } + std::ifstream is{args[1], std::ios::binary}; + if (!is) { + std::cerr << "Unable to open rom" << std::endl; + return 1; + } + + Screen screen(64, 32); + Chip8 chippotto(is, &screen); + + bool quit{false}; + u32 to_render[2048]; + int frame_rate{60}; + Uint64 frame_ms = 1000000000 / frame_rate; + + while (!quit) { + Uint64 start = SDL_GetTicksNS(); + screen.handler(quit, chippotto.keypad()); + for (int i = 0; i < 10; ++i) + chippotto.cycle(); + + if (chippotto.display().should_draw()) { + modify_pixels(chippotto.display().pixels(), to_render); + screen.render(to_render, DISPLAY_WIDTH * sizeof(u32)); + chippotto.display().should_draw() = {false}; + } + Uint64 end = SDL_GetTicksNS(); + Uint64 total = end - start; + chippotto.update_timers(); + if (total < frame_ms) { + SDL_DelayNS(frame_ms - total); + } + } + + return 0; +} diff --git a/src/screen.cpp b/src/screen.cpp new file mode 100644 index 0000000..1d4754b --- /dev/null +++ b/src/screen.cpp @@ -0,0 +1,97 @@ +#include "screen.hpp" +#include "SDL3/SDL_events.h" +#include "SDL3/SDL_log.h" +#include "SDL3/SDL_properties.h" +#include "SDL3/SDL_render.h" +#include "SDL3/SDL_video.h" +#include "utils.hpp" +#include +#include + +Screen::Screen(int w, int h) { + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) { + SDL_Log("SDL not initialized, error: %s", SDL_GetError()); + } + SDL_PropertiesID props{SDL_CreateProperties()}; + + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, + "Chippotto"); + SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, true); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, 1280); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, 640); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, + SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, + SDL_WINDOWPOS_CENTERED); + + if (window = SDL_CreateWindowWithProperties(props); window == nullptr) { + SDL_Log("Unable To create window"); + } + if (renderer = SDL_CreateRenderer(window, nullptr); renderer == nullptr) { + SDL_Log("Window not created, error: %s", SDL_GetError()); + } + + if (texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, w, h); + texture == nullptr) { + SDL_Log("Failed to create Texture, error: %s", SDL_GetError()); + } + SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_PIXELART); + SDL_AudioSpec audio_spec; + if (!SDL_LoadWAV("assets/audio.wav", &audio_spec, &buf, &len)) { + SDL_Log("Error Loading the beep sound"); + } + + stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, + &audio_spec, nullptr, nullptr); + SDL_ResumeAudioStreamDevice(stream); +} + +Screen::~Screen() { + SDL_DestroyTexture(texture); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} + +void Screen::render(u32 *buffer, int pitch) { + void *pixels; + int r_pitch; + SDL_LockTexture(texture, nullptr, &pixels, &r_pitch); + r_pitch = {pitch}; + memcpy(pixels, buffer, DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(u32)); + SDL_UnlockTexture(texture); + + SDL_RenderClear(renderer); + SDL_RenderTexture(renderer, texture, nullptr, nullptr); + SDL_RenderPresent(renderer); +} + +void Screen::beep() { + if (SDL_GetAudioStreamQueued(stream) < (int)len) { + SDL_PutAudioStreamData(stream, buf, len); + } +} + +void Screen::handler(bool &quit, Keypad &keypad) { + + SDL_Event e; + SDL_zero(e); + while (SDL_PollEvent(&e)) { + if (e.type == SDL_EVENT_QUIT) { + quit = {true}; + } else if (e.type == SDL_EVENT_KEY_DOWN) { + for (auto i{0}; i < 16; ++i) { + if (e.key.key == keys[i]) { + keypad[i] = true; + } + } + } else if (e.type == SDL_EVENT_KEY_UP) { + for (auto i{0}; i < 16; ++i) { + if (e.key.key == keys[i]) { + keypad[i] = false; + } + } + } + } +} diff --git a/src/screen.hpp b/src/screen.hpp new file mode 100644 index 0000000..19c91fa --- /dev/null +++ b/src/screen.hpp @@ -0,0 +1,29 @@ +#include "SDL3/SDL_audio.h" +#include "SDL3/SDL_events.h" +#include "SDL3/SDL_log.h" +#include "SDL3/SDL_render.h" +#include "SDL3/SDL_stdinc.h" +#include "SDL3/SDL_surface.h" +#include "SDL3/SDL_video.h" +#include "keypad.hpp" +#include "utils.hpp" +#include +#include +#include + +class Screen { +public: + Screen(int w, int h); + ~Screen(); + void render(u32 *buffer, int pitch); + void beep(); + void handler(bool &quit, Keypad &keypad); + +private: + Uint8 *buf; + Uint32 len; + SDL_AudioStream *stream; + SDL_Window *window; + SDL_Renderer *renderer{nullptr}; + SDL_Texture *texture; +}; diff --git a/src/stack.cpp b/src/stack.cpp new file mode 100644 index 0000000..b733bf4 --- /dev/null +++ b/src/stack.cpp @@ -0,0 +1,25 @@ +#include "stack.hpp" +Stack::Stack() {} +Stack::~Stack() {} + +bool Stack::is_empty() { + return v.empty(); +} + +void Stack::push(u16 element) { + v.push_back(element); +} + +u16 Stack::pop() { + u16 top{v.back()}; + v.pop_back(); + return top; +} + +int Stack::size() { + return v.size(); +} + +u16 Stack::top() { + return v.back(); +} diff --git a/src/stack.hpp b/src/stack.hpp new file mode 100644 index 0000000..4b5b30e --- /dev/null +++ b/src/stack.hpp @@ -0,0 +1,17 @@ +#include +#include +using u16 = std::uint16_t; + +class Stack { +public: + Stack(); + ~Stack(); + void push(u16 element); + u16 pop(); + u16 top(); + bool is_empty(); + int size(); + +private: + std::vector v; +}; diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..9db7536 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,20 @@ +#include "utils.hpp" +#include +#include +#include + +std::vector convert_args(int argc, char **argv) { + std::vector v(argv, argv + argc); + return v; +} + + +void modify_pixels(bool *src, u32 *dst) { + for (auto i{0}; i < 2048; ++i) { + if (src[i]) { + dst[i] = 0xFFFFFFFF; + } else { + dst[i] = 0x00000000; + } + } +} diff --git a/src/utils.hpp b/src/utils.hpp new file mode 100644 index 0000000..e47efcc --- /dev/null +++ b/src/utils.hpp @@ -0,0 +1,65 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include +#include +#include + +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; + +constexpr unsigned int MEMORY_SIZE = 4096; +constexpr unsigned int REGISTER_NUMBER = 16; +constexpr unsigned int FONT_SIZE = 80; +constexpr unsigned int START_ADDRESS = 0x200; +constexpr unsigned int FONTSET_START_ADDRESS = 0x050; +constexpr unsigned int DISPLAY_WIDTH = 64; +constexpr unsigned int DISPLAY_HEIGHT = 32; + +using pixels = std::array; + +constexpr std::array font = { + 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 + 0x20, 0x60, 0x20, 0x20, 0x70, // 1 + 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 + 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3 + 0x90, 0x90, 0xF0, 0x10, 0x10, // 4 + 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5 + 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6 + 0xF0, 0x10, 0x20, 0x40, 0x40, // 7 + 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8 + 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9 + 0xF0, 0x90, 0xF0, 0x90, 0x90, // A + 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B + 0xF0, 0x80, 0x80, 0x80, 0xF0, // C + 0xE0, 0x90, 0x90, 0x90, 0xE0, // D + 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E + 0xF0, 0x80, 0xF0, 0x80, 0x80 // F +}; + +constexpr std::array keys{ + SDLK_X, // 0 + SDLK_1, // 1 + SDLK_2, // 2 + SDLK_3, // 3 + SDLK_Q, // 4 + SDLK_W, // 5 + SDLK_E, // 6 + SDLK_A, // 7 + SDLK_S, // 8 + SDLK_D, // 9 + SDLK_Z, // A + SDLK_C, // B + SDLK_4, // C + SDLK_R, // D + SDLK_F, // E + SDLK_V // F +}; + +std::vector convert_args(int argc, char **argv); +u32 *turn_to_pixels(bool *src); +void modify_pixels(bool *src, u32 *dst); +#endif // !UTILS_H