Compare commits

..

9 Commits

Author SHA1 Message Date
02b5f6ecaf Agregando sonido 2025-06-28 21:14:07 -04:00
4125e866b4 manejando teclado 2025-06-28 19:35:48 -04:00
437fab8efe Corrigiendo instrucciones segun flags test 2025-06-28 19:02:35 -04:00
4b897f48e0 Corrigiendo instrucciones segun corax+ opcode test 2025-06-28 18:57:29 -04:00
a1a52024c8 Creando Stack viewer 2025-06-28 18:12:04 -04:00
a477a21d7f Creado register view 2025-06-28 17:57:20 -04:00
a803e733da Corrigiendo disassembler para mostrar las instrucciones impares
algunos programas llaman a ubicaciones impares por alguna razon -.-'
2025-06-28 16:01:28 -04:00
b9d2377088 Limpiando warnings 2025-06-28 15:27:24 -04:00
d8e3aa16f8 Optimizando memory viewer para que no cree un dump de una linea no visible 2025-06-28 13:14:11 -04:00
22 changed files with 594 additions and 163 deletions

View File

@@ -7,6 +7,8 @@ target_sources(
main.cpp main.cpp
Machine.cpp Machine.cpp
Machine.h Machine.h
Sound.cpp
Sound.h
Graphics/Graphics.cpp Graphics/Graphics.cpp
Graphics/Graphics.h Graphics/Graphics.h
Graphics/Color.h Graphics/Color.h
@@ -30,8 +32,12 @@ target_sources(
UI/UIManager.h UI/UIManager.h
UI/Disassembler.cpp UI/Disassembler.cpp
UI/Disassembler.h UI/Disassembler.h
UI/RegisterView.cpp
UI/RegisterView.h
UI/StackViewer.cpp
UI/StackViewer.h
) )
add_compile_options(-Wall -Wextra -Wpedantic -Werror) target_link_libraries(${PROJECT_NAME} PRIVATE vendor)
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror)
target_link_libraries(${PROJECT_NAME} PRIVATE vendor) target_link_options(${PROJECT_NAME} PRIVATE)

View File

@@ -18,7 +18,7 @@ void Graphics::create_sdl()
{ {
SDL_SetAppMetadata("CHIP-8 Emulator", "0.0.1", "fun.skrd.chip8"); SDL_SetAppMetadata("CHIP-8 Emulator", "0.0.1", "fun.skrd.chip8");
if (!SDL_Init(SDL_INIT_VIDEO)) if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO))
{ {
throw std::runtime_error(std::format("Couldn't initialize SDL: {}", SDL_GetError())); throw std::runtime_error(std::format("Couldn't initialize SDL: {}", SDL_GetError()));
} }
@@ -96,4 +96,4 @@ void Graphics::end() const
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), this->renderer.get()); ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), this->renderer.get());
SDL_RenderPresent(this->renderer.get()); SDL_RenderPresent(this->renderer.get());
} }

View File

@@ -8,6 +8,7 @@ Interpreter::Interpreter(std::shared_ptr<MachineState> machine_state) :
random_generator{std::random_device{}()} random_generator{std::random_device{}()}
{ {
srand(time(nullptr)); srand(time(nullptr));
// quirks = static_cast<uint8_t>(InterpreterQuirks::COSMAC_STORE_AND_LOAD);
for (bool& display : this->machine_state->display) for (bool& display : this->machine_state->display)
{ {
display = rand() % 100 > 50; display = rand() % 100 > 50;
@@ -219,7 +220,7 @@ std::vector<Instruction> Interpreter::disassembly() const
{ {
std::vector<Instruction> instructions(std::size(machine_state->memory) / 2); std::vector<Instruction> instructions(std::size(machine_state->memory) / 2);
for (auto address = 0; address < std::size(machine_state->memory); address += 2) for (std::size_t address = 0; address < std::size(machine_state->memory); address += 2)
{ {
const auto word = this->get_word(address); const auto word = this->get_word(address);
instructions[address / 2] = (this->decode(word, address)); instructions[address / 2] = (this->decode(word, address));
@@ -287,6 +288,7 @@ void Interpreter::ret() const
{ {
this->machine_state->sp -= 1; this->machine_state->sp -= 1;
this->machine_state->pc = this->machine_state->stack[this->machine_state->sp]; this->machine_state->pc = this->machine_state->stack[this->machine_state->sp];
this->machine_state->stack[this->machine_state->sp] = 0;
} }
void Interpreter::jp_addr(const Instruction& instruction) const void Interpreter::jp_addr(const Instruction& instruction) const
@@ -332,7 +334,8 @@ void Interpreter::ld_vx_byte(const Instruction& instruction) const
void Interpreter::add_vx_byte(const Instruction& instruction) const void Interpreter::add_vx_byte(const Instruction& instruction) const
{ {
this->machine_state->v[instruction.x] += instruction.kk; auto& vx = this->machine_state->v[instruction.x];
vx = (vx + instruction.kk) & 0xFF;
} }
void Interpreter::ld_vx_vy(const Instruction& instruction) const void Interpreter::ld_vx_vy(const Instruction& instruction) const
@@ -343,16 +346,19 @@ void Interpreter::ld_vx_vy(const Instruction& instruction) const
void Interpreter::or_vx_vy(const Instruction& instruction) const void Interpreter::or_vx_vy(const Instruction& instruction) const
{ {
this->machine_state->v[instruction.x] |= this->machine_state->v[instruction.y]; this->machine_state->v[instruction.x] |= this->machine_state->v[instruction.y];
this->machine_state->v[0xF] = 0;
} }
void Interpreter::and_vx_vy(const Instruction& instruction) const void Interpreter::and_vx_vy(const Instruction& instruction) const
{ {
this->machine_state->v[instruction.x] &= this->machine_state->v[instruction.y]; this->machine_state->v[instruction.x] &= this->machine_state->v[instruction.y];
this->machine_state->v[0xF] = 0;
} }
void Interpreter::xor_vx_vy(const Instruction& instruction) const void Interpreter::xor_vx_vy(const Instruction& instruction) const
{ {
this->machine_state->v[instruction.x] ^= this->machine_state->v[instruction.y]; this->machine_state->v[instruction.x] ^= this->machine_state->v[instruction.y];
this->machine_state->v[0xF] = 0;
} }
void Interpreter::add_vx_vy(const Instruction& instruction) const void Interpreter::add_vx_vy(const Instruction& instruction) const
@@ -361,16 +367,9 @@ void Interpreter::add_vx_vy(const Instruction& instruction) const
const auto& vy = this->machine_state->v[instruction.y]; const auto& vy = this->machine_state->v[instruction.y];
auto& vf = this->machine_state->v[0xF]; auto& vf = this->machine_state->v[0xF];
if (vx + vy > 0xFF) const bool carry = vx + vy > 0x100;
{ vx = (vx + vy) & 0xFF;
vf = 1; vf = carry ? 1 : 0;
}
else
{
vf = 0;
}
vx += vy;
} }
void Interpreter::sub_vx_vy(const Instruction& instruction) const void Interpreter::sub_vx_vy(const Instruction& instruction) const
@@ -379,16 +378,9 @@ void Interpreter::sub_vx_vy(const Instruction& instruction) const
const auto& vy = this->machine_state->v[instruction.y]; const auto& vy = this->machine_state->v[instruction.y];
auto& vf = this->machine_state->v[0xF]; auto& vf = this->machine_state->v[0xF];
if (vx > vy) const bool carry = vx >= vy;
{ vx = (vx - vy) & 0xFF;
vf = 1; vf = carry ? 1 : 0;
}
else
{
vf = 0;
}
vx -= vy;
} }
void Interpreter::shr_vx_vy(const Instruction& instruction) const void Interpreter::shr_vx_vy(const Instruction& instruction) const
@@ -402,16 +394,9 @@ void Interpreter::shr_vx_vy(const Instruction& instruction) const
vx = vy; vx = vy;
} }
if (vx & 0x01) const bool carry = vx & 0x01;
{
vf = 1;
}
else
{
vf = 0;
}
vx = vx >> 1; vx = vx >> 1;
vf = carry ? 1 : 0;
} }
void Interpreter::subn_vx_vy(const Instruction& instruction) const void Interpreter::subn_vx_vy(const Instruction& instruction) const
@@ -420,16 +405,9 @@ void Interpreter::subn_vx_vy(const Instruction& instruction) const
const auto& vy = this->machine_state->v[instruction.y]; const auto& vy = this->machine_state->v[instruction.y];
auto& vf = this->machine_state->v[0xF]; auto& vf = this->machine_state->v[0xF];
if (vy > vx) const bool carry = vy >= vx;
{ vx = (vy - vx) & 0xFF;
vf = 1; vf = carry ? 1 : 0;
}
else
{
vf = 0;
}
vx = vy - vx;
} }
void Interpreter::shl_vx_vy(const Instruction& instruction) const void Interpreter::shl_vx_vy(const Instruction& instruction) const
@@ -443,16 +421,9 @@ void Interpreter::shl_vx_vy(const Instruction& instruction) const
vx = vy; vx = vy;
} }
if (vx & 0x80) const bool carry = vx & 0x80;
{ vx = vx << 1 & 0xFF;
vf = 1; vf = carry ? 1 : 0;
}
else
{
vf = 0;
}
vx = vx << 1;
} }
void Interpreter::sne_vx_vy(const Instruction& instruction) const void Interpreter::sne_vx_vy(const Instruction& instruction) const
@@ -495,12 +466,13 @@ void Interpreter::drw_vx_vy_nibble(const Instruction& instruction) const
const auto& vx = this->machine_state->v[instruction.x]; const auto& vx = this->machine_state->v[instruction.x];
const auto& vy = this->machine_state->v[instruction.y]; const auto& vy = this->machine_state->v[instruction.y];
auto& vf = this->machine_state->v[0xF]; auto& vf = this->machine_state->v[0xF];
auto& i = this->machine_state->i; const auto& i = this->machine_state->i;
const uint8_t start_x = vx & 63; const uint8_t start_x = vx & 63;
const uint8_t start_y = vy & 31; const uint8_t start_y = vy & 31;
vf = 0; vf = 0;
bool carry = false;
for (auto row = 0; row < instruction.n; row++) for (auto row = 0; row < instruction.n; row++)
{ {
const auto current_y = start_y + row; const auto current_y = start_y + row;
@@ -529,7 +501,7 @@ void Interpreter::drw_vx_vy_nibble(const Instruction& instruction) const
if (display[index]) if (display[index])
{ {
display[index] = false; display[index] = false;
vf = 1; carry = true;
} }
else else
{ {
@@ -538,11 +510,13 @@ void Interpreter::drw_vx_vy_nibble(const Instruction& instruction) const
} }
} }
} }
vf = carry ? 1 : 0;
} }
void Interpreter::skp_vx(const Instruction& instruction) const void Interpreter::skp_vx(const Instruction& instruction) const
{ {
if (this->machine_state->keyboard & 1 << instruction.x) if (this->machine_state->keyboard & 1 << this->machine_state->v[instruction.x])
{ {
this->machine_state->pc += 2; this->machine_state->pc += 2;
} }
@@ -550,7 +524,7 @@ void Interpreter::skp_vx(const Instruction& instruction) const
void Interpreter::sknp_vx(const Instruction& instruction) const void Interpreter::sknp_vx(const Instruction& instruction) const
{ {
if (!(this->machine_state->keyboard & 1 << instruction.x)) if (!(this->machine_state->keyboard & 1 << this->machine_state->v[instruction.x]))
{ {
this->machine_state->pc += 2; this->machine_state->pc += 2;
} }
@@ -561,22 +535,39 @@ void Interpreter::ld_vx_dt(const Instruction& instruction) const
this->machine_state->v[instruction.x] = this->machine_state->dt; this->machine_state->v[instruction.x] = this->machine_state->dt;
} }
void Interpreter::ld_vx_k(const Instruction& instruction) const void Interpreter::ld_vx_k(const Instruction& instruction)
{ {
if (this->machine_state->keyboard == 0) if (this->machine_state->keyboard == 0 && this->wait_for_key_released == 0)
{ {
this->machine_state->pc -= 2; this->machine_state->pc -= 2;
return; return;
} }
for (auto key = 0; key < 16; key++) if (this->machine_state->keyboard != 0 && this->wait_for_key_released == 0)
{ {
if (this->machine_state->keyboard & 1 << key) this->wait_for_key_released = this->machine_state->keyboard;
this->machine_state->pc -= 2;
return;
}
if (this->wait_for_key_released != 0)
{
const auto released_keys = this->wait_for_key_released & (~this->machine_state->keyboard);
if (released_keys != 0)
{ {
this->machine_state->v[instruction.x] = key; for (auto key = 0; key < 16; key++)
break; {
if (released_keys & 1 << key)
{
this->machine_state->v[instruction.x] = key;
this->wait_for_key_released = 0;
return;
}
}
} }
} }
this->machine_state->pc -= 2;
} }
void Interpreter::ld_dt_vx(const Instruction& instruction) const void Interpreter::ld_dt_vx(const Instruction& instruction) const

View File

@@ -20,8 +20,8 @@ class Interpreter
std::mt19937 random_generator; std::mt19937 random_generator;
uint8_t quirks = 0; uint8_t quirks = 0;
uint16_t wait_for_key_released = false;
Instruction decode(uint16_t word, uint16_t address) const;
void execute_instruction(const Instruction& instruction); void execute_instruction(const Instruction& instruction);
void sys_addr() const; void sys_addr() const;
@@ -52,7 +52,7 @@ class Interpreter
void skp_vx(const Instruction& instruction) const; void skp_vx(const Instruction& instruction) const;
void sknp_vx(const Instruction& instruction) const; void sknp_vx(const Instruction& instruction) const;
void ld_vx_dt(const Instruction& instruction) const; void ld_vx_dt(const Instruction& instruction) const;
void ld_vx_k(const Instruction& instruction) const; void ld_vx_k(const Instruction& instruction);
void ld_dt_vx(const Instruction& instruction) const; void ld_dt_vx(const Instruction& instruction) const;
void ld_st_vx(const Instruction& instruction) const; void ld_st_vx(const Instruction& instruction) const;
void add_i_vx(const Instruction& instruction) const; void add_i_vx(const Instruction& instruction) const;
@@ -64,6 +64,7 @@ class Interpreter
public: public:
explicit Interpreter(std::shared_ptr<MachineState> machine_state); explicit Interpreter(std::shared_ptr<MachineState> machine_state);
uint16_t get_word(uint16_t address) const; uint16_t get_word(uint16_t address) const;
Instruction decode(uint16_t word, uint16_t address) const;
std::vector<Instruction> disassembly() const; std::vector<Instruction> disassembly() const;
void tick(); void tick();

View File

@@ -16,15 +16,21 @@
Machine::Machine() : Machine::Machine() :
machine_state{std::make_shared<MachineState>()}, machine_state{std::make_shared<MachineState>()},
graphics{std::make_shared<Graphics>()}, graphics{std::make_shared<Graphics>()},
sound{std::make_unique<Sound>()},
callback_manager(std::make_shared<CallbackManager>()), callback_manager(std::make_shared<CallbackManager>()),
interpreter{std::make_unique<Interpreter>(this->machine_state)}, interpreter{std::make_shared<Interpreter>(this->machine_state)},
ui_manager{std::make_unique<UIManager>(this->graphics, this->machine_state, this->callback_manager)}, ui_manager{
running{false}, std::make_unique<UIManager>(this->graphics, this->machine_state, this->interpreter, this->callback_manager)
},
ips{60}, ips{60},
last_update_time{0}, last_update_time{0},
accumulator{0}, accumulator{0},
target_cycle_time{1.0 / this->ips} target_cycle_time{1.0 / this->ips},
last_timer_time{0},
timer_accumulator{0},
timer_cycle_time{1.0 / 60},
running{false}
{ {
this->register_callbacks(); this->register_callbacks();
this->callback_manager->trigger(this->callback_manager->ips_callback, this->ips); this->callback_manager->trigger(this->callback_manager->ips_callback, this->ips);
@@ -34,11 +40,14 @@ void Machine::iterate()
{ {
if (running) if (running)
{ {
this->update_timers();
this->execute_interpreter(); this->execute_interpreter();
} }
this->callback_manager->trigger(this->callback_manager->disassembly_callback, this->interpreter->disassembly()); if (machine_state->st > 0)
{
this->sound->square();
}
this->graphics->start(); this->graphics->start();
this->ui_manager->render(); this->ui_manager->render();
this->graphics->end(); this->graphics->end();
@@ -52,6 +61,94 @@ bool Machine::on_event(const SDL_Event* event) const
{ {
return false; return false;
} }
if (event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_KEY_UP)
{
const bool pressed = event->type == SDL_EVENT_KEY_DOWN;
switch (event->key.scancode)
{
case SDL_SCANCODE_1:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x1)
: machine_state->keyboard & ~(1 << 0x1);
break;
case SDL_SCANCODE_2:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x2)
: machine_state->keyboard & ~(1 << 0x2);
break;
case SDL_SCANCODE_3:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x3)
: machine_state->keyboard & ~(1 << 0x3);
break;
case SDL_SCANCODE_4:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0xC)
: machine_state->keyboard & ~(1 << 0xC);
break;
case SDL_SCANCODE_Q:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x4)
: machine_state->keyboard & ~(1 << 0x4);
break;
case SDL_SCANCODE_W:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x5)
: machine_state->keyboard & ~(1 << 0x5);
break;
case SDL_SCANCODE_E:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x6)
: machine_state->keyboard & ~(1 << 0x6);
break;
case SDL_SCANCODE_R:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0xD)
: machine_state->keyboard & ~(1 << 0xD);
break;
case SDL_SCANCODE_A:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x7)
: machine_state->keyboard & ~(1 << 0x7);
break;
case SDL_SCANCODE_S:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x8)
: machine_state->keyboard & ~(1 << 0x8);
break;
case SDL_SCANCODE_D:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x9)
: machine_state->keyboard & ~(1 << 0x9);
break;
case SDL_SCANCODE_F:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0xE)
: machine_state->keyboard & ~(1 << 0xE);
break;
case SDL_SCANCODE_Z:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0xA)
: machine_state->keyboard & ~(1 << 0xA);
break;
case SDL_SCANCODE_X:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0x0)
: machine_state->keyboard & ~(1 << 0x0);
break;
case SDL_SCANCODE_C:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0xB)
: machine_state->keyboard & ~(1 << 0xB);
break;
case SDL_SCANCODE_V:
machine_state->keyboard = pressed
? machine_state->keyboard | (1 << 0xF)
: machine_state->keyboard & ~(1 << 0xF);
break;
default: break;
}
}
return true; return true;
} }
@@ -70,6 +167,21 @@ void Machine::execute_interpreter()
} }
} }
void Machine::update_timers()
{
const auto current_time = SDL_GetTicks();
const auto delta_time = static_cast<double>(current_time - this->last_update_time) / 1000.0;
this->last_timer_time = current_time;
this->timer_accumulator += delta_time;
while (this->timer_accumulator >= this->timer_cycle_time)
{
machine_state->dt = machine_state->dt > 0 ? machine_state->dt - 1 : 0;
machine_state->st = machine_state->st > 0 ? machine_state->st - 1 : 0;
this->timer_accumulator -= this->timer_cycle_time;
}
}
void Machine::register_callbacks() void Machine::register_callbacks()
{ {
callback_manager->rom_load_callback.push_back(std::bind( callback_manager->rom_load_callback.push_back(std::bind(

View File

@@ -1,6 +1,7 @@
#ifndef MACHINE_H #ifndef MACHINE_H
#define MACHINE_H #define MACHINE_H
#include "Sound.h"
#include "Graphics/Graphics.h" #include "Graphics/Graphics.h"
#include "Interpreter/Interpreter.h" #include "Interpreter/Interpreter.h"
#include "SDL3/SDL_events.h" #include "SDL3/SDL_events.h"
@@ -11,9 +12,10 @@ class Machine
{ {
std::shared_ptr<MachineState> machine_state; std::shared_ptr<MachineState> machine_state;
std::shared_ptr<Graphics> graphics; std::shared_ptr<Graphics> graphics;
std::unique_ptr<Sound> sound;
std::shared_ptr<CallbackManager> callback_manager; std::shared_ptr<CallbackManager> callback_manager;
std::unique_ptr<Interpreter> interpreter; std::shared_ptr<Interpreter> interpreter;
std::unique_ptr<UIManager> ui_manager; std::unique_ptr<UIManager> ui_manager;
int ips; int ips;
@@ -21,10 +23,16 @@ class Machine
double accumulator; double accumulator;
double target_cycle_time; double target_cycle_time;
uint64_t last_timer_time;
double timer_accumulator;
double timer_cycle_time;
std::string rom; std::string rom;
bool running; bool running;
void execute_interpreter(); void execute_interpreter();
void update_timers();
void on_rom_load(const std::string& path); void on_rom_load(const std::string& path);
void on_reset() const; void on_reset() const;
void on_stop(); void on_stop();

88
src/Sound.cpp Normal file
View File

@@ -0,0 +1,88 @@
#include "Sound.h"
#include <format>
#include "SDL3/SDL.h"
void SDLAudioStreamDestroyer::operator()(SDL_AudioStream* stream) const
{
SDL_DestroyAudioStream(stream);
}
Sound::Sound()
{
spec.channels = 1;
spec.format = SDL_AUDIO_F32;
spec.freq = 8000;
SDL_AudioStream* raw_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, nullptr, nullptr);
if (!raw_stream)
{
throw std::runtime_error(std::format("Couldn't initialize audio stream: {}", SDL_GetError()));
}
stream = std::unique_ptr<SDL_AudioStream, SDLAudioStreamDestroyer>(raw_stream);
SDL_ResumeAudioStreamDevice(stream.get());
}
void Sound::sine()
{
constexpr int minimum_audio = (8000 * sizeof(float)) / 6;
constexpr int freq = 440;
if (SDL_GetAudioStreamQueued(stream.get()) < minimum_audio)
{
static float samples[512];
for (std::size_t i = 0; i < std::size(samples); i++)
{
const float phase = current_sine_sample * freq / 8000.0f;
samples[i] = SDL_sinf(phase * 2 * SDL_PI_F);
current_sine_sample++;
}
current_sine_sample %= 8000;
SDL_PutAudioStreamData(stream.get(), samples, sizeof (samples));
}
}
void Sound::noise()
{
constexpr int minimum_audio = (8000 * sizeof(float)) / 6;
constexpr int freq = 440;
if (SDL_GetAudioStreamQueued(stream.get()) < minimum_audio)
{
static float samples[512];
for (std::size_t i = 0; i < std::size(samples); i++)
{
samples[i] = SDL_rand(2) * freq;
}
SDL_PutAudioStreamData(stream.get(), samples, sizeof (samples));
}
}
void Sound::square()
{
constexpr int minimum_audio = (8000 * sizeof(float)) / 6;
constexpr int freq = 440;
constexpr float amplitude = 0.5f;
if (SDL_GetAudioStreamQueued(stream.get()) < minimum_audio)
{
static float samples[512];
for (std::size_t i = 0; i < std::size(samples); i++)
{
const float phase = current_sine_sample * freq / 8000.0f;
samples[i] = (SDL_sinf(phase * 2 * SDL_PI_F) >= 0.0f) ? amplitude : -amplitude;
current_sine_sample++;
}
current_sine_sample %= 8000;
SDL_PutAudioStreamData(stream.get(), samples, sizeof(samples));
}
}

28
src/Sound.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef SOUND_H
#define SOUND_H
#include <memory>
#include <SDL3/SDL.h>
struct SDLAudioStreamDestroyer
{
void operator()(SDL_AudioStream* stream) const;
};
class Sound
{
SDL_AudioSpec spec;
std::unique_ptr<SDL_AudioStream, SDLAudioStreamDestroyer> stream;
int current_sine_sample = 0;
public:
Sound();
void sine();
void noise();
void square();
};
#endif //SOUND_H

View File

@@ -16,7 +16,6 @@ struct CallbackManager
std::vector<std::function<void(int)>> step_callback; std::vector<std::function<void(int)>> step_callback;
std::vector<std::function<void(int)>> ips_callback; std::vector<std::function<void(int)>> ips_callback;
std::vector<std::function<void()>> reload_callback; std::vector<std::function<void()>> reload_callback;
std::vector<std::function<void(const std::vector<Instruction>)>> disassembly_callback;
template <typename Func, typename... Args> template <typename Func, typename... Args>
void trigger(const std::vector<Func>& callbacks, Args&&... args) void trigger(const std::vector<Func>& callbacks, Args&&... args)

View File

@@ -49,7 +49,7 @@ void ControlPanel::render()
} }
} }
ImGui::Text("Status: %s", "Stopped"); ImGui::Text("Status: %s", run ? "Running" : "Stopped");
ImGui::SeparatorText("Debug"); ImGui::SeparatorText("Debug");
@@ -122,7 +122,7 @@ void ControlPanel::on_click_load_rom()
); );
} }
void ControlPanel::on_callback_load_rom(void* self, const char* const* filelist, int filter) void ControlPanel::on_callback_load_rom(void* self, const char* const* filelist, int)
{ {
const auto control_panel = static_cast<ControlPanel*>(self); const auto control_panel = static_cast<ControlPanel*>(self);

View File

@@ -9,16 +9,13 @@
Disassembler::Disassembler( Disassembler::Disassembler(
std::shared_ptr<Graphics> graphics, std::shared_ptr<Graphics> graphics,
std::shared_ptr<MachineState> machine_state, std::shared_ptr<MachineState> machine_state,
std::shared_ptr<Interpreter> interpreter,
std::shared_ptr<CallbackManager> callback_manager) : std::shared_ptr<CallbackManager> callback_manager) :
graphis{std::move(graphics)}, graphis{std::move(graphics)},
machine_state{std::move(machine_state)}, machine_state{std::move(machine_state)},
interpreter{std::move(interpreter)},
callback_manager{std::move(callback_manager)} callback_manager{std::move(callback_manager)}
{ {
this->callback_manager->disassembly_callback.push_back(std::bind(
&Disassembler::on_disassembly_callback,
this,
std::placeholders::_1
));
} }
void Disassembler::render() void Disassembler::render()
@@ -26,39 +23,57 @@ void Disassembler::render()
if (ImGui::Begin("Chip-8 - Disassembly")) if (ImGui::Begin("Chip-8 - Disassembly"))
{ {
ImGui::Checkbox("Follow", &this->follow); ImGui::Checkbox("Follow", &this->follow);
ImGui::Checkbox("Show odd instructions", &this->show_odd);
ImGuiListClipper clipper; ImGuiListClipper clipper;
clipper.Begin(this->disassembly.size()); clipper.Begin((machine_state->memory.size() / (this->show_odd ? 1 : 2)) - ((this->show_odd ? 1 : 0)));
ImGui::BeginChild("##instructions"); ImGui::BeginChild("##instructions");
if (this->follow) if (this->follow)
{ {
size_t pc_index = machine_state->pc / 2;
const float line_h = ImGui::GetTextLineHeightWithSpacing(); const float line_h = ImGui::GetTextLineHeightWithSpacing();
if (pc_index < disassembly.size()) const auto current_pc = machine_state->pc / (this->show_odd ? 1 : 2);
{
const float window_h = ImGui::GetWindowHeight();
const float target_y = pc_index * line_h;
const float scroll_y = target_y - (window_h * 0.1f) + (line_h * 0.1f);
ImGui::SetScrollY(scroll_y);
}
}
const float window_h = ImGui::GetWindowHeight();
const float target_y = current_pc * line_h;
const float scroll_y = target_y - (window_h * 0.1f) + (line_h * 0.1f);
ImGui::SetScrollY(scroll_y);
}
while (clipper.Step()) while (clipper.Step())
{ {
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
{ {
const auto& instruction = this->disassembly[i]; const auto address = i * (this->show_odd ? 1 : 2);
auto line = this->build_line(instruction); auto line = this->build_line(
if (instruction.address == machine_state->pc) this->interpreter->decode(
this->interpreter->get_word(address),
address
)
);
if (address == machine_state->pc)
{ {
ImGui::TextColored(ImVec4(1, 1, 0, 1), "%s", line.c_str()); if (address % 2 == 0)
{
ImGui::TextColored(ImVec4(1, 1, 0, 1), "%s", line.c_str());
}
else
{
ImGui::TextColored(ImVec4(1, 1, 0, 1), "%s", line.c_str());
}
} }
else else
{ {
ImGui::TextUnformatted(line.c_str()); if (address % 2 == 0)
{
ImGui::TextUnformatted(line.c_str());
}
else
{
ImGui::TextColored(ImVec4(1, 1, 1, .5), "%s", line.c_str());
}
} }
} }
} }
@@ -67,14 +82,32 @@ void Disassembler::render()
ImGui::End(); ImGui::End();
} }
void Disassembler::on_disassembly_callback(const std::vector<Instruction>& disassembly)
{
this->disassembly = disassembly;
}
std::string Disassembler::build_line(const Instruction instruction) const std::string Disassembler::build_line(const Instruction instruction) const
{ {
const std::string addr_prefix = std::format("0x{:0>4x} | ", instruction.address); std::string addr_prefix;
if (instruction.address == machine_state->pc)
{
if (instruction.address % 2 == 0)
{
addr_prefix = std::format(">0x{:0>4x} | ", instruction.address);
}
else
{
addr_prefix = std::format("> 0x{:0>4x} | ", instruction.address);
}
}
else
{
if (instruction.address % 2 == 0)
{
addr_prefix = std::format(" 0x{:0>4x} | ", instruction.address);
}
else
{
addr_prefix = std::format("- 0x{:0>4x} | ", instruction.address);
}
}
switch (instruction.op_code) switch (instruction.op_code)
{ {

View File

@@ -4,6 +4,7 @@
#include "CallbackManager.h" #include "CallbackManager.h"
#include "../Graphics/Graphics.h" #include "../Graphics/Graphics.h"
#include "../Interpreter/Interpreter.h"
#include "../Interpreter/MachineState.h" #include "../Interpreter/MachineState.h"
@@ -11,21 +12,21 @@ class Disassembler
{ {
std::shared_ptr<Graphics> graphis; std::shared_ptr<Graphics> graphis;
std::shared_ptr<MachineState> machine_state; std::shared_ptr<MachineState> machine_state;
std::shared_ptr<Interpreter> interpreter;
std::shared_ptr<CallbackManager> callback_manager; std::shared_ptr<CallbackManager> callback_manager;
std::vector<Instruction> disassembly; bool follow = true;
bool show_odd = false;
bool follow = false;
public: public:
Disassembler( Disassembler(
std::shared_ptr<Graphics> graphics, std::shared_ptr<Graphics> graphics,
std::shared_ptr<MachineState> machine_state, std::shared_ptr<MachineState> machine_state,
std::shared_ptr<Interpreter> interpreter,
std::shared_ptr<CallbackManager> callback_manager std::shared_ptr<CallbackManager> callback_manager
); );
void render(); void render();
void on_disassembly_callback(const std::vector<Instruction>& disassembly);
std::string build_line(Instruction instruction) const; std::string build_line(Instruction instruction) const;
}; };

View File

@@ -64,7 +64,7 @@ void Display::update_texture() const
) )
); );
for (int i = 0; i < display.size(); i++) for (std::size_t i = 0; i < display.size(); i++)
{ {
if (display[i]) if (display[i])
{ {
@@ -108,4 +108,4 @@ void Display::display_widget() const
ImGui::Image(static_cast<ImTextureID>(reinterpret_cast<intptr_t>(texture.get())), scaled_size); ImGui::Image(static_cast<ImTextureID>(reinterpret_cast<intptr_t>(texture.get())), scaled_size);
ImGui::End(); ImGui::End();
} }

View File

@@ -24,76 +24,78 @@ void MemoryViewer::render()
ImGui::SliderInt("##memory_view_width", &width, 1, 32, "%d", ImGuiSliderFlags_AlwaysClamp); ImGui::SliderInt("##memory_view_width", &width, 1, 32, "%d", ImGuiSliderFlags_AlwaysClamp);
ImGui::PopItemWidth(); ImGui::PopItemWidth();
auto total_lines = (std::size(this->machine_state->memory) + width - 1) / width;
ImGuiListClipper clipper;
clipper.Begin(total_lines);
auto dump_lines = this->get_memory_dump(); ImGui::BeginChild("##memory_view");
for (auto dump_line : dump_lines) while (clipper.Step())
{ {
ImGui::TextUnformatted(dump_line.c_str()); for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
{
ImGui::TextUnformatted(get_memory_line(i).c_str());
}
} }
ImGui::EndChild();
} }
ImGui::End(); ImGui::End();
} }
std::vector<std::string> MemoryViewer::get_memory_dump() const std::string MemoryViewer::get_memory_line(const int line) const
{ {
auto& memory = this->machine_state->memory; auto& memory = this->machine_state->memory;
const auto size = std::size(memory); const auto size = std::size(memory);
const auto i = line * this->width;
std::vector<std::string> dump_lines; std::stringstream text_builder;
text_builder << "0x";
text_builder << std::hex
<< std::setw(4)
<< std::setfill('0')
<< std::uppercase
<< static_cast<unsigned int>(i)
<< " | ";
for (auto i = 0; i < size; i += this->width) for (int j = 0; j < this->width; j++)
{ {
std::stringstream text_builder; if (static_cast<std::size_t>(i + j) >= size)
text_builder << "0x";
text_builder << std::hex
<< std::setw(4)
<< std::setfill('0')
<< std::uppercase
<< static_cast<unsigned int>(i)
<< " | ";
for (auto j = 0; j < this->width; j++)
{ {
if (i + j >= size) text_builder << " ";
}
else
{
text_builder << std::hex
<< std::setw(2)
<< std::setfill('0')
<< std::uppercase
<< static_cast<unsigned int>(memory[i + j])
<< " ";
}
}
text_builder << "| ";
for (int j = 0; j < this->width; j++)
{
if (static_cast<std::size_t>(i + j) >= size)
{
text_builder << ".";
}
else
{
auto byte = memory[i + j];
if (byte >= 32 && byte <= 126)
{ {
text_builder << " "; text_builder << static_cast<char>(memory[i + j]);
} }
else else
{
text_builder << std::hex
<< std::setw(2)
<< std::setfill('0')
<< std::uppercase
<< static_cast<unsigned int>(memory[i + j])
<< " ";
}
}
text_builder << "| ";
for (auto j = 0; j < this->width; j++)
{
if (i + j >= size)
{ {
text_builder << "."; text_builder << ".";
} }
else
{
auto byte = memory[i + j];
if (byte >= 32 && byte <= 126)
{
text_builder << static_cast<char>(memory[i + j]);
}
else
{
text_builder << ".";
}
}
} }
dump_lines.push_back(text_builder.str());
} }
return dump_lines; return text_builder.str();
} }

View File

@@ -22,7 +22,7 @@ public:
); );
void render(); void render();
std::vector<std::string> get_memory_dump() const; std::string get_memory_line(int line) const;
}; };

59
src/UI/RegisterView.cpp Normal file
View File

@@ -0,0 +1,59 @@
#include "RegisterView.h"
#include "imgui.h"
#include "../Interpreter/MachineState.h"
RegisterView::RegisterView(
std::shared_ptr<Graphics> graphics,
std::shared_ptr<MachineState> machine_state,
std::shared_ptr<CallbackManager> callback_manager) :
graphics{std::move(graphics)},
machine_state{std::move(machine_state)},
callback_manager{std::move(callback_manager)}
{
}
void RegisterView::render()
{
if (ImGui::Begin("Chip8 - Registers"))
{
if (ImGui::BeginTable("Registers", 4, ImGuiTableFlags_Borders))
{
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("PC");
ImGui::Text("0x%04X", this->machine_state->pc);
ImGui::TableSetColumnIndex(1);
ImGui::Text("I");
ImGui::Text("0x%02X", this->machine_state->i);
ImGui::TableSetColumnIndex(2);
ImGui::Text("DT");
ImGui::Text("0x%04X", this->machine_state->dt);
ImGui::TableSetColumnIndex(3);
ImGui::Text("ST");
ImGui::Text("0x%04X", this->machine_state->st);
}
ImGui::EndTable();
if (ImGui::BeginTable("Registers", 8, ImGuiTableFlags_Borders))
{
for (int i = 0; i < 2; i++)
{
ImGui::TableNextRow();
for (int j = 0; j < 8; j++)
{
ImGui::TableSetColumnIndex(j);
ImGui::Text("V%i", i * 8 + j);
ImGui::Text("0x%02X", this->machine_state->v[i * 8 + j]);
}
}
}
ImGui::EndTable();
ImGui::Text("Keyboard: 0b%016B", this->machine_state->keyboard);
}
ImGui::End();
}

27
src/UI/RegisterView.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef REGISTERVIEW_H
#define REGISTERVIEW_H
#include <memory>
#include "CallbackManager.h"
#include "../Graphics/Graphics.h"
#include "../Interpreter/MachineState.h"
class RegisterView
{
std::shared_ptr<Graphics> graphics;
std::shared_ptr<MachineState> machine_state;
std::shared_ptr<CallbackManager> callback_manager;
public:
RegisterView(
std::shared_ptr<Graphics> graphics,
std::shared_ptr<MachineState> machine_state,
std::shared_ptr<CallbackManager> callback_manager
);
void render();
};
#endif //REGISTERVIEW_H

34
src/UI/StackViewer.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include "StackViewer.h"
#include "imgui.h"
StackViewer::StackViewer(
std::shared_ptr<Graphics> graphics,
std::shared_ptr<MachineState> machine_state,
std::shared_ptr<CallbackManager> callback_manager
) :
graphics{std::move(graphics)},
machine_state{std::move(machine_state)},
callback_manager{std::move(callback_manager)}
{
}
void StackViewer::render()
{
if (ImGui::Begin("Chip8 - Stack"))
{
if (ImGui::BeginTable("Stack", 2, ImGuiTableFlags_BordersV))
{
for (std::size_t i = 0; i < std::size(machine_state->stack); i++)
{
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("Depth %zu", i);
ImGui::TableSetColumnIndex(1);
ImGui::Text("0x%04X", machine_state->stack[i]);
}
}
ImGui::EndTable();
}
ImGui::End();
}

25
src/UI/StackViewer.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef STACKVIEWER_H
#define STACKVIEWER_H
#include <memory>
#include "CallbackManager.h"
#include "../Graphics/Graphics.h"
#include "../Interpreter/MachineState.h"
class StackViewer
{
std::shared_ptr<Graphics> graphics;
std::shared_ptr<MachineState> machine_state;
std::shared_ptr<CallbackManager> callback_manager;
public:
StackViewer(
std::shared_ptr<Graphics> graphics,
std::shared_ptr<MachineState> machine_state,
std::shared_ptr<CallbackManager> callback_manager);
void render();
};
#endif //STACKVIEWER_H

View File

@@ -2,22 +2,30 @@
#include "Disassembler.h" #include "Disassembler.h"
#include "MemoryViewer.h" #include "MemoryViewer.h"
#include "RegisterView.h"
#include "RomInfo.h" #include "RomInfo.h"
#include "StackViewer.h"
UIManager::UIManager( UIManager::UIManager(
std::shared_ptr<Graphics> graphics, std::shared_ptr<Graphics> graphics,
std::shared_ptr<MachineState> machine_state, std::shared_ptr<MachineState> machine_state,
std::shared_ptr<Interpreter> interpreter,
std::shared_ptr<CallbackManager> callback_manager std::shared_ptr<CallbackManager> callback_manager
) : ) :
graphics{std::move(graphics)}, graphics{std::move(graphics)},
machine_state{std::move(machine_state)}, machine_state{std::move(machine_state)},
interpreter{std::move(interpreter)},
callback_manager{std::move(callback_manager)}, callback_manager{std::move(callback_manager)},
display{std::make_unique<Display>(this->graphics, this->machine_state)}, display{std::make_unique<Display>(this->graphics, this->machine_state)},
control_panel{std::make_unique<ControlPanel>(this->graphics, this->machine_state, this->callback_manager)}, control_panel{std::make_unique<ControlPanel>(this->graphics, this->machine_state, this->callback_manager)},
rom_info{std::make_unique<RomInfo>(this->graphics, this->machine_state, this->callback_manager)}, rom_info{std::make_unique<RomInfo>(this->graphics, this->machine_state, this->callback_manager)},
memory_viewer{std::make_unique<MemoryViewer>(this->graphics, this->machine_state, this->callback_manager)}, memory_viewer{std::make_unique<MemoryViewer>(this->graphics, this->machine_state, this->callback_manager)},
disassembler{std::make_unique<Disassembler>(this->graphics, this->machine_state, this->callback_manager)} disassembler{
std::make_unique<Disassembler>(this->graphics, this->machine_state, this->interpreter, this->callback_manager)
},
register_view{std::make_unique<RegisterView>(this->graphics, this->machine_state, this->callback_manager)},
stack_viewer{std::make_unique<StackViewer>(this->graphics, this->machine_state, this->callback_manager)}
{ {
} }
@@ -28,4 +36,6 @@ void UIManager::render()
this->rom_info->render(); this->rom_info->render();
this->memory_viewer->render(); this->memory_viewer->render();
this->disassembler->render(); this->disassembler->render();
this->register_view->render();
this->stack_viewer->render();
} }

View File

@@ -7,24 +7,31 @@
#include "Disassembler.h" #include "Disassembler.h"
#include "Display.h" #include "Display.h"
#include "MemoryViewer.h" #include "MemoryViewer.h"
#include "RegisterView.h"
#include "RomInfo.h" #include "RomInfo.h"
#include "StackViewer.h"
#include "../Graphics/Graphics.h" #include "../Graphics/Graphics.h"
#include "../Interpreter/Interpreter.h"
class UIManager class UIManager
{ {
std::shared_ptr<Graphics> graphics; std::shared_ptr<Graphics> graphics;
std::shared_ptr<MachineState> machine_state; std::shared_ptr<MachineState> machine_state;
std::shared_ptr<Interpreter> interpreter;
std::shared_ptr<CallbackManager> callback_manager; std::shared_ptr<CallbackManager> callback_manager;
std::unique_ptr<Display> display; std::unique_ptr<Display> display;
std::unique_ptr<ControlPanel> control_panel; std::unique_ptr<ControlPanel> control_panel;
std::unique_ptr<RomInfo> rom_info; std::unique_ptr<RomInfo> rom_info;
std::unique_ptr<MemoryViewer> memory_viewer; std::unique_ptr<MemoryViewer> memory_viewer;
std::unique_ptr<Disassembler> disassembler; std::unique_ptr<Disassembler> disassembler;
std::unique_ptr<RegisterView> register_view;
std::unique_ptr<StackViewer> stack_viewer;
public: public:
UIManager( UIManager(
std::shared_ptr<Graphics> graphics, std::shared_ptr<Graphics> graphics,
std::shared_ptr<MachineState> machine_state, std::shared_ptr<MachineState> machine_state,
std::shared_ptr<Interpreter> interpreter,
std::shared_ptr<CallbackManager> callback_manager std::shared_ptr<CallbackManager> callback_manager
); );

View File

@@ -4,7 +4,7 @@
#include <memory> #include <memory>
#include "Machine.h" #include "Machine.h"
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) SDL_AppResult SDL_AppInit(void** appstate, [[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
{ {
auto machine = std::make_unique<Machine>(); auto machine = std::make_unique<Machine>();
*appstate = machine.release(); *appstate = machine.release();
@@ -33,7 +33,7 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }
void SDL_AppQuit(void* appstate, SDL_AppResult result) void SDL_AppQuit(void* appstate, [[maybe_unused]] SDL_AppResult result)
{ {
std::unique_ptr<Machine> chip8(static_cast<Machine*>(appstate)); std::unique_ptr<Machine> chip8(static_cast<Machine*>(appstate));
} }