From 8d93de4be7ab6faa7bfffd431b0d541f599014b9 Mon Sep 17 00:00:00 2001 From: Sebastian Mihai Pantoc Date: Tue, 12 May 2026 11:15:47 +0200 Subject: [PATCH] test --- .gitignore | 47 +++ CMakeLists.txt | 21 ++ Chip8-Tests/1-chip8-logo.ch8 | Bin 0 -> 260 bytes Chip8-Tests/15 Puzzle [Roger Ivie].ch8 | Bin 0 -> 385 bytes Chip8-Tests/2-ibm-logo.ch8 | Bin 0 -> 132 bytes Chip8-Tests/3-corax+.ch8 | Bin 0 -> 761 bytes Chip8-Tests/4-flags.ch8 | Bin 0 -> 1041 bytes Chip8-Tests/5-quirks.ch8 | Bin 0 -> 3232 bytes Chip8-Tests/6-keypad.ch8 | Bin 0 -> 913 bytes Chip8-Tests/7-beep.ch8 | Bin 0 -> 110 bytes Chip8-Tests/8-scrolling.ch8 | Bin 0 -> 1330 bytes Chip8-Tests/Space Invaders [David Winter].ch8 | Bin 0 -> 1301 bytes README.md | 9 + src/assets/audio.wav | Bin 0 -> 22170 bytes src/chip8.cpp | 280 ++++++++++++++++++ src/chip8.hpp | 40 +++ src/display.cpp | 25 ++ src/display.hpp | 22 ++ src/keypad.cpp | 11 + src/keypad.hpp | 14 + src/main.cpp | 51 ++++ src/screen.cpp | 97 ++++++ src/screen.hpp | 29 ++ src/stack.cpp | 25 ++ src/stack.hpp | 17 ++ src/utils.cpp | 20 ++ src/utils.hpp | 65 ++++ 27 files changed, 773 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Chip8-Tests/1-chip8-logo.ch8 create mode 100644 Chip8-Tests/15 Puzzle [Roger Ivie].ch8 create mode 100644 Chip8-Tests/2-ibm-logo.ch8 create mode 100644 Chip8-Tests/3-corax+.ch8 create mode 100644 Chip8-Tests/4-flags.ch8 create mode 100644 Chip8-Tests/5-quirks.ch8 create mode 100644 Chip8-Tests/6-keypad.ch8 create mode 100644 Chip8-Tests/7-beep.ch8 create mode 100644 Chip8-Tests/8-scrolling.ch8 create mode 100644 Chip8-Tests/Space Invaders [David Winter].ch8 create mode 100644 README.md create mode 100644 src/assets/audio.wav create mode 100644 src/chip8.cpp create mode 100644 src/chip8.hpp create mode 100644 src/display.cpp create mode 100644 src/display.hpp create mode 100644 src/keypad.cpp create mode 100644 src/keypad.hpp create mode 100644 src/main.cpp create mode 100644 src/screen.cpp create mode 100644 src/screen.hpp create mode 100644 src/stack.cpp create mode 100644 src/stack.hpp create mode 100644 src/utils.cpp create mode 100644 src/utils.hpp 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 0000000000000000000000000000000000000000..19c5cf30f469f86fce62351b46c5a68454d93429 GIT binary patch literal 260 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 B8T|kN literal 0 HcmV?d00001 diff --git a/Chip8-Tests/6-keypad.ch8 b/Chip8-Tests/6-keypad.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..4d1ecdccdf6968522529fff4a18492cd31032600 GIT binary patch literal 913 zcmZ9IPfQa*6vk(#jkGJmFblR((rkA9(?bu492&ESg%(92hG3gCdKqAw5VRXYz;IAA z5koM+gv02;G-QjrSL1<$2SfJemBbr~aSq%}npVMhAk>)>FU(};_s#p>yqSGkCjq>M zqYH|SKpjWRVB2UGw_i4k*vKh+*nj{WrA}wFJPUsJ9d-->ocGN=?5Kx*jGR z)F{AH%PrjRbc-E%t7EdP;QqBqM?I)v>VQRMNfFMi=l7 z+yB{SuYY7z=La8O4qor?%-;&$?C%hN`s)nDXnIR~I9((44_o3dcyO~-_HnCN!EPkT ziL=;MgYh5d zsop5c7gx@?7lMdUC6Pv7Lv4240NO8J)BXbPUnQyO_?}ilZcor-R1Mfr4XAZi)ZmhK zT!{tO0Cz>)PoL)f_rci_hF(ZL+vA?cL)+$D4>>0~_qeX}P;4j{F`-372re^2sVU{5 zCuB&44k@7?5theA4WjF!s^?LWBhE?LwnUKH9w*WyvbJs8!Xn}m6q#ZbQWT)P{m#?X zj-HPcq9{^|_CknHzGl7KkxxJ#De9^NN5@p))b|Yz`o1uSYX1Z>ak79oD{L%NTA3jEwqCGGR%Xe{o^6k~qmv;v1bSJL^|LI{*%g*% v7RazD8(=m_CJ;ev6&%-J4+^;XHm!}xsR!f(r<15An`#|N$1w~k3h1BDab&GPl(~kLLrL} z>;?=%It=d;7!qxgxEUUR)P9tBv{1+er03B*As->ELJ=_AMpElU_X#aY E02O^Dc>n+a literal 0 HcmV?d00001 diff --git a/Chip8-Tests/8-scrolling.ch8 b/Chip8-Tests/8-scrolling.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..ae1c9705140afd9c67b8ddf4bbd1501e1b4a1c24 GIT binary patch literal 1330 zcmZvYQD|FL7{|{&NnJ?OHa*F$BP-o)E`rNyX=j|H26nTinsrg;SfLMlI5)Z3O5Mh7 z&1N4IZd8h(4+Eo!47n{dd2;MwsBd1_@L?#st%@)v9HbCY2o0@!n8oXNPTH=n=8*gU z?)Ux9_y4{d2~orrh~Jd7FlrEg5iQ1>6N~RQCrEr$a!DK`^jBZ&G>ZkaE^q8hB9d9E zyTk}?$cFUZsL{Fl1vxW@$eAl1={36GjeepKdPO3=^QVpA>H@J0G%J-PsLai4S3P2# zRmjCzj~wCsANIA5fj9b^MI&sb7e8-KAhcOjaT=k96;|;Hu8N9Bxelo~!&Ou97}wJ( zewiy(@he^|Fk-Hj> zJkos8dCf0ltxcXr{~&hhsmr+fas2wF(B)8dL1=gN%lLKmQ(DzO%u6IJsC__v3)L>5 zeh}JS{bh^#dWQ-E^#@c5P!EN6SG_IjH%?6-dE-DWasSrzNNw=`E!3zTqG)X*G7c)W z6edLxQ^vJJJc`c4l<@gL=EWA8kH9wJ;5e+}{XJO`*Ly0^+;q$p7J!T#146#K{W zT?N0}?e@FFrZkk0Gh*jNp^eY9GW@3Wd`zC)_7OY2st@jQO6ck?r=x;+fVfwB*S<3U zmOEbK@=dYhc%f&<>#%%h%L_g6a~#{TY|Uo2%^Xc*OmP^a3bUD_Fb7^N204f!0|Dct z!(+>0jt!w>LF`!6vMH2o20f^vKv&zgfPs5F6eUPnn$0LLQr=tBG$>K{g8~Up2%&++ z#F(w87U=L*1H^Y@DwjN4nVKA!(+u+*3RFsOPo>OcHaCSrl@E;1MAY8l`>LJz9Kn{7 zW~G$7mI8Ltc)wz%N=LXD7Y7W42#<7%1ftyTa29h5kuYRvw jzE}6W`g*y%&h>VAsm`^&RK8vJEEK%4c;Z-K=$n55{cHs6 literal 0 HcmV?d00001 diff --git a/Chip8-Tests/Space Invaders [David Winter].ch8 b/Chip8-Tests/Space Invaders [David Winter].ch8 new file mode 100644 index 0000000000000000000000000000000000000000..3ada8dfe93a7d95925a9f52a1bf2cf385101bacd GIT binary patch literal 1301 zcmZWnQAk@?82)d1y_piamh`f#L!@1A(Y5IsX0;I_gJ9ar3bKc*%*)hU?J_db zHpya)flW574=cK`vDvNnWm{kNuoto79NUwy4is_bVa)mx!w{k+p1c1hX*=A&s%{wQRRi1nI9zSDv1 zJPLJiw$)+c()-6IejM1JczHl<1M;gJPYIVr@_U<${9*fTlx&tciEOvEkR3Vx2Doc) z3O)2`Rq?%YRUtWY*g(dLwdaA19X80Jse2cRyD{b%E?soEWt@3AL7PtV1uN2{^5bAUzCT&h3Dh;D&&*7BvD{v<+c;@^XuA) zH6HSE=u=q^X*C5})%ghW!|+LNgx~p!T>I8}=qU^;mivcc7~!vTqn!KhH+2OMYKU>} z7WaCs`!3{t3Co(DMbp%@ov?hq#Kw3RavLshi~)QI9XJ3ujyr|`=#Z-D5Jd}sK?w&y zqN{5F0f1Zu=vWYk&Uj5c4zRSe;x}5nN8?3TeX+>%05LyXLL80nN-d8cpE(0?F!?j$ z;AngSBt=?S04Va?lauMaqw#0;@3!nhP8<7Q{AvA*uxJCI#8**rxvYcnpQh&Kp1_lj z=jJ}2-T%9}d9S^_9bT+&KY#9(U$%OC-(FkWf~^-$lE#%uGJz_j>(EFpM&Xr;xN?2m}F*reTjZFfRof zTXCYrXT%-lj7$_oj4%ggWO!B(SbBMx@jOd1^z9a~JBLFMgmS+Om+1SMA3w7KqYs?a zRDua0R5#2%U2W1K5TL{qrcD9~g_vn27^Zq<`?MN015qPv5^98Xb=nNK{^xX9MFEQ0(9n^cvUT~Vjc{JvVlv!A|lx9~5`cIUbP literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..32aa8a31cdb439733b4f9bcf7093b1748acb809f GIT binary patch literal 22170 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 literal 0 HcmV?d00001 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