Compare commits

...

4 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
9 changed files with 292 additions and 61 deletions

View File

@@ -7,6 +7,8 @@ target_sources(
main.cpp
Machine.cpp
Machine.h
Sound.cpp
Sound.h
Graphics/Graphics.cpp
Graphics/Graphics.h
Graphics/Color.h

View File

@@ -18,7 +18,7 @@ void Graphics::create_sdl()
{
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()));
}

View File

@@ -8,6 +8,7 @@ Interpreter::Interpreter(std::shared_ptr<MachineState> machine_state) :
random_generator{std::random_device{}()}
{
srand(time(nullptr));
// quirks = static_cast<uint8_t>(InterpreterQuirks::COSMAC_STORE_AND_LOAD);
for (bool& display : this->machine_state->display)
{
display = rand() % 100 > 50;
@@ -333,7 +334,8 @@ void Interpreter::ld_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
@@ -344,16 +346,19 @@ void Interpreter::ld_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[0xF] = 0;
}
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[0xF] = 0;
}
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[0xF] = 0;
}
void Interpreter::add_vx_vy(const Instruction& instruction) const
@@ -362,16 +367,9 @@ void Interpreter::add_vx_vy(const Instruction& instruction) const
const auto& vy = this->machine_state->v[instruction.y];
auto& vf = this->machine_state->v[0xF];
if (vx + vy > 0xFF)
{
vf = 1;
}
else
{
vf = 0;
}
vx += vy;
const bool carry = vx + vy > 0x100;
vx = (vx + vy) & 0xFF;
vf = carry ? 1 : 0;
}
void Interpreter::sub_vx_vy(const Instruction& instruction) const
@@ -380,16 +378,9 @@ void Interpreter::sub_vx_vy(const Instruction& instruction) const
const auto& vy = this->machine_state->v[instruction.y];
auto& vf = this->machine_state->v[0xF];
if (vx > vy)
{
vf = 1;
}
else
{
vf = 0;
}
vx -= vy;
const bool carry = vx >= vy;
vx = (vx - vy) & 0xFF;
vf = carry ? 1 : 0;
}
void Interpreter::shr_vx_vy(const Instruction& instruction) const
@@ -403,16 +394,9 @@ void Interpreter::shr_vx_vy(const Instruction& instruction) const
vx = vy;
}
if (vx & 0x01)
{
vf = 1;
}
else
{
vf = 0;
}
const bool carry = vx & 0x01;
vx = vx >> 1;
vf = carry ? 1 : 0;
}
void Interpreter::subn_vx_vy(const Instruction& instruction) const
@@ -421,16 +405,9 @@ void Interpreter::subn_vx_vy(const Instruction& instruction) const
const auto& vy = this->machine_state->v[instruction.y];
auto& vf = this->machine_state->v[0xF];
if (vy > vx)
{
vf = 1;
}
else
{
vf = 0;
}
vx = vy - vx;
const bool carry = vy >= vx;
vx = (vy - vx) & 0xFF;
vf = carry ? 1 : 0;
}
void Interpreter::shl_vx_vy(const Instruction& instruction) const
@@ -444,16 +421,9 @@ void Interpreter::shl_vx_vy(const Instruction& instruction) const
vx = vy;
}
if (vx & 0x80)
{
vf = 1;
}
else
{
vf = 0;
}
vx = vx << 1;
const bool carry = vx & 0x80;
vx = vx << 1 & 0xFF;
vf = carry ? 1 : 0;
}
void Interpreter::sne_vx_vy(const Instruction& instruction) const
@@ -502,6 +472,7 @@ void Interpreter::drw_vx_vy_nibble(const Instruction& instruction) const
const uint8_t start_y = vy & 31;
vf = 0;
bool carry = false;
for (auto row = 0; row < instruction.n; row++)
{
const auto current_y = start_y + row;
@@ -530,7 +501,7 @@ void Interpreter::drw_vx_vy_nibble(const Instruction& instruction) const
if (display[index])
{
display[index] = false;
vf = 1;
carry = true;
}
else
{
@@ -539,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
{
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;
}
@@ -551,7 +524,7 @@ void Interpreter::skp_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;
}
@@ -562,22 +535,39 @@ void Interpreter::ld_vx_dt(const Instruction& instruction) const
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;
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;
break;
for (auto key = 0; key < 16; key++)
{
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

View File

@@ -20,6 +20,7 @@ class Interpreter
std::mt19937 random_generator;
uint8_t quirks = 0;
uint16_t wait_for_key_released = false;
void execute_instruction(const Instruction& instruction);
@@ -51,7 +52,7 @@ class Interpreter
void skp_vx(const Instruction& instruction) const;
void sknp_vx(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_st_vx(const Instruction& instruction) const;
void add_i_vx(const Instruction& instruction) const;

View File

@@ -16,6 +16,7 @@
Machine::Machine() :
machine_state{std::make_shared<MachineState>()},
graphics{std::make_shared<Graphics>()},
sound{std::make_unique<Sound>()},
callback_manager(std::make_shared<CallbackManager>()),
interpreter{std::make_shared<Interpreter>(this->machine_state)},
@@ -26,6 +27,9 @@ Machine::Machine() :
last_update_time{0},
accumulator{0},
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();
@@ -36,9 +40,14 @@ void Machine::iterate()
{
if (running)
{
this->update_timers();
this->execute_interpreter();
}
if (machine_state->st > 0)
{
this->sound->square();
}
this->graphics->start();
this->ui_manager->render();
this->graphics->end();
@@ -52,6 +61,94 @@ bool Machine::on_event(const SDL_Event* event) const
{
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;
}
@@ -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()
{
callback_manager->rom_load_callback.push_back(std::bind(

View File

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

@@ -52,6 +52,8 @@ void RegisterView::render()
}
}
ImGui::EndTable();
ImGui::Text("Keyboard: 0b%016B", this->machine_state->keyboard);
}
ImGui::End();
}