test
This commit is contained in:
+47
@@ -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
|
||||
@@ -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)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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 <path/to/rom>
|
||||
|
||||
Binary file not shown.
+280
@@ -0,0 +1,280 @@
|
||||
#include "chip8.hpp"
|
||||
#include "SDL3/SDL_log.h"
|
||||
#include "utils.hpp"
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sys/resource.h>
|
||||
|
||||
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<u8>(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<u8>((opcode & 0x0F00) >> 8)};
|
||||
u8 Y = {static_cast<u8>((opcode & 0x00F0) >> 4)};
|
||||
u16 NN = {static_cast<u16>((opcode & 0x00FF))};
|
||||
u16 NNN = {static_cast<u16>((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<u8>(NN)};
|
||||
break;
|
||||
case 0x7000:
|
||||
v[X] += {static_cast<u8>(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<int>(v[X]) + static_cast<int>(v[Y])) > 255) {
|
||||
tmp = {1};
|
||||
}
|
||||
v[X] += v[Y];
|
||||
v[0xF] = tmp;
|
||||
} break;
|
||||
case 0x0005: {
|
||||
u8 tmp = {static_cast<u8>((v[X] < v[Y]) ? 0 : 1)};
|
||||
v[X] -= v[Y];
|
||||
v[0xF] = tmp;
|
||||
} break;
|
||||
case 0x0006: {
|
||||
u8 tmp = {static_cast<u8>(v[Y] & 0x01)};
|
||||
v[X] = v[Y] >> 1;
|
||||
v[0xF] = tmp;
|
||||
} break;
|
||||
case 0x0007:
|
||||
v[X] = {static_cast<u8>(v[Y] - v[X])};
|
||||
v[0xF] = (v[Y] < v[X]) ? 0 : 1;
|
||||
break;
|
||||
case 0x000E: {
|
||||
u8 tmp = {static_cast<u8>((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<u16>(NNN)};
|
||||
break;
|
||||
case 0xB000:
|
||||
pc = {static_cast<u16>(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<u8>(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<u8>(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<u8>(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_;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "display.hpp"
|
||||
#include "keypad.hpp"
|
||||
#include "screen.hpp"
|
||||
#include "stack.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
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<u8, REGISTER_NUMBER> v;
|
||||
std::array<u8, MEMORY_SIZE> mem;
|
||||
std::size_t rom_size;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "display.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
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};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef DISPLAY
|
||||
|
||||
#define DISPLAY
|
||||
#include "utils.hpp"
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
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
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "keypad.hpp"
|
||||
#include <array>
|
||||
|
||||
Keypad::Keypad() {
|
||||
for (auto i{0}; i < 16; ++i) {
|
||||
keys[i] = {false};
|
||||
}
|
||||
}
|
||||
Keypad::~Keypad() {}
|
||||
|
||||
bool &Keypad::operator[](int i) { return keys[i]; }
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef KEYPAD
|
||||
#define KEYPAD
|
||||
#include <array>
|
||||
constexpr unsigned int KEYS = 16;
|
||||
class Keypad {
|
||||
public:
|
||||
Keypad();
|
||||
~Keypad();
|
||||
bool& operator[](int i);
|
||||
|
||||
private:
|
||||
std::array<bool, KEYS> keys;
|
||||
};
|
||||
#endif // !KEYPAD
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "SDL3/SDL_stdinc.h"
|
||||
#include "SDL3/SDL_timer.h"
|
||||
#include "chip8.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
std::vector<std::string> 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;
|
||||
}
|
||||
@@ -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 <array>
|
||||
#include <cstring>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
#include <array>
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
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<u16> v;
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "utils.hpp"
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::vector<std::string> convert_args(int argc, char **argv) {
|
||||
std::vector<std::string> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <SDL3/SDL_keycode.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<u32, DISPLAY_WIDTH * DISPLAY_HEIGHT>;
|
||||
|
||||
constexpr std::array<u8, FONT_SIZE> 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<u8, 16> 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<std::string> convert_args(int argc, char **argv);
|
||||
u32 *turn_to_pixels(bool *src);
|
||||
void modify_pixels(bool *src, u32 *dst);
|
||||
#endif // !UTILS_H
|
||||
Reference in New Issue
Block a user