Files
CHIP-8/src/Machine.cpp
2025-06-28 21:14:07 -04:00

292 lines
9.1 KiB
C++

#include "Machine.h"
#include <fstream>
#include <iostream>
#include <iterator>
#include <bits/ostream.tcc>
#include "Graphics/Graphics.h"
#include "Interpreter/Interpreter.h"
#include "SDL3/SDL.h"
#include "UI/Display.h"
#include "UI/UIManager.h"
#include "imgui_impl_sdl3.h"
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)},
ui_manager{
std::make_unique<UIManager>(this->graphics, this->machine_state, this->interpreter, this->callback_manager)
},
ips{60},
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();
this->callback_manager->trigger(this->callback_manager->ips_callback, this->ips);
}
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();
}
bool Machine::on_event(const SDL_Event* event) const
{
ImGui_ImplSDL3_ProcessEvent(event);
if (event->type == SDL_EVENT_QUIT)
{
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;
}
void Machine::execute_interpreter()
{
const auto current_time = SDL_GetTicks();
const auto delta_time = static_cast<double>(current_time - this->last_update_time) / 1000.0;
this->last_update_time = current_time;
this->accumulator += delta_time;
while (this->accumulator >= this->target_cycle_time)
{
this->interpreter->tick();
this->accumulator -= this->target_cycle_time;
}
}
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(
&Machine::on_rom_load,
this,
std::placeholders::_1
));
callback_manager->reset_callback.push_back(std::bind(
&Machine::on_reset,
this
));
callback_manager->stop_callback.push_back(std::bind(
&Machine::on_stop,
this
));
callback_manager->resume_callback.push_back(std::bind(
&Machine::on_resume,
this
));
callback_manager->step_callback.push_back(std::bind(
&Machine::on_step,
this,
std::placeholders::_1
));
callback_manager->ips_callback.push_back(std::bind(
&Machine::on_speed_change,
this,
std::placeholders::_1
));
callback_manager->reload_callback.push_back(std::bind(
&Machine::on_reload,
this
));
}
void Machine::on_rom_load(const std::string& path)
{
std::ifstream file(path, std::ios::binary);
file.unsetf(std::ios::skipws);
file.seekg(0, std::ios::end);
const std::streampos file_size = file.tellg();
file.seekg(0, std::ios::beg);
// La memoria para los rom es desde 0x200 hasta el final de 0x1000
// Por lo que un rom sería muy grande si sobrepasa este tamaño
if (file_size >= 0x1000 - 0x200)
{
std::cout << "File too large!" << file_size << std::endl;
return;
}
this->machine_state->reset();
std::copy(
std::istream_iterator<uint8_t>(file),
std::istream_iterator<uint8_t>(),
this->machine_state->memory.begin() + 0x200
);
this->rom = path;
std::cout << "ROM Loaded: " << path << std::endl;
}
void Machine::on_reset() const
{
this->machine_state->reset();
}
void Machine::on_stop()
{
this->running = false;
this->accumulator = 0;
}
void Machine::on_resume()
{
this->running = true;
this->last_update_time = SDL_GetTicks();
this->accumulator = 0;
}
void Machine::on_step(int steps)
{
for (int i = 0; i < steps; i++)
{
this->interpreter->tick();
}
}
void Machine::on_speed_change(const int ips)
{
this->ips = ips;
if (this->ips > 0)
{
this->target_cycle_time = 1.0 / this->ips;
}
else
{
this->target_cycle_time = std::numeric_limits<double>::infinity();
}
}
void Machine::on_reload()
{
this->on_reset();
this->on_rom_load(this->rom);
}