292 lines
9.1 KiB
C++
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);
|
|
}
|