diff --git a/src/nba/CMakeLists.txt b/src/nba/CMakeLists.txt index ce145f93f..b6d044848 100644 --- a/src/nba/CMakeLists.txt +++ b/src/nba/CMakeLists.txt @@ -98,7 +98,6 @@ set(HEADERS_PUBLIC include/nba/common/punning.hpp include/nba/common/scope_exit.hpp include/nba/device/audio_device.hpp - include/nba/device/input_device.hpp include/nba/device/video_device.hpp include/nba/rom/backup/backup.hpp include/nba/rom/backup/backup_file.hpp diff --git a/src/nba/include/nba/config.hpp b/src/nba/include/nba/config.hpp index 0bf838c0e..54029d802 100644 --- a/src/nba/include/nba/config.hpp +++ b/src/nba/include/nba/config.hpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -47,7 +46,6 @@ struct Config { } audio; std::shared_ptr audio_dev = std::make_shared(); - std::shared_ptr input_dev = std::make_shared(); std::shared_ptr video_dev = std::make_shared(); }; diff --git a/src/nba/include/nba/core.hpp b/src/nba/include/nba/core.hpp index 88f688606..853821050 100644 --- a/src/nba/include/nba/core.hpp +++ b/src/nba/include/nba/core.hpp @@ -19,6 +19,20 @@ namespace nba { +enum class Key : u8 { + A = 0, + B = 1, + Select = 2, + Start = 3, + Right = 4, + Left = 5, + Up = 6, + Down = 7, + L = 8, + R = 9, + Count = 10 +}; + struct CoreBase { static constexpr int kCyclesPerFrame = 280896; @@ -32,6 +46,7 @@ struct CoreBase { virtual auto CreateSolarSensor() -> std::unique_ptr = 0; virtual void LoadState(SaveState const& state) = 0; virtual void CopyState(SaveState& state) = 0; + virtual void SetKeyStatus(Key key, bool pressed) = 0; virtual void Run(int cycles) = 0; virtual auto GetROM() -> ROM& = 0; diff --git a/src/nba/include/nba/device/input_device.hpp b/src/nba/include/nba/device/input_device.hpp deleted file mode 100644 index 6dfdd63e6..000000000 --- a/src/nba/include/nba/device/input_device.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2023 fleroviux - * - * Licensed under GPLv3 or any later version. - * Refer to the included LICENSE file. - */ - -#pragma once - -#include - -namespace nba { - -struct InputDevice { - virtual ~InputDevice() = default; - - enum class Key { - Up, - Down, - Left, - Right, - Start, - Select, - A, - B, - L, - R - }; - - static constexpr int kKeyCount = 10; - - virtual auto Poll(Key key) -> bool = 0; - virtual void SetOnChangeCallback(std::function callback) = 0; -}; - -struct NullInputDevice : InputDevice { - auto Poll(Key key) -> bool final { - return false; - } - - void SetOnChangeCallback(std::function callback) final {} -}; - -struct BasicInputDevice : InputDevice { - void SetKeyStatus(Key key, bool pressed) { - key_status[static_cast(key)] = pressed; - keypress_callback(); - } - - auto Poll(Key key) -> bool final { - return key_status[static_cast(key)]; - } - - void SetOnChangeCallback(std::function callback) final { - keypress_callback = callback; - } -private: - std::function keypress_callback; - bool key_status[kKeyCount]; -}; - -} // namespace nba diff --git a/src/nba/include/nba/scheduler.hpp b/src/nba/include/nba/scheduler.hpp index d09dfa195..092d8bec1 100644 --- a/src/nba/include/nba/scheduler.hpp +++ b/src/nba/include/nba/scheduler.hpp @@ -67,8 +67,6 @@ struct Scheduler { SIO_transfer_done, - KeyPad_Poll, - EndOfQueue, Count }; diff --git a/src/nba/src/core.cpp b/src/nba/src/core.cpp index 1368143a6..8db307ebb 100644 --- a/src/nba/src/core.cpp +++ b/src/nba/src/core.cpp @@ -23,7 +23,7 @@ Core::Core(std::shared_ptr config) , apu(scheduler, dma, bus, config) , ppu(scheduler, irq, dma, config) , timer(scheduler, irq, apu) - , keypad(scheduler, irq, config) + , keypad(scheduler, irq) , bus(scheduler, {cpu, irq, dma, apu, ppu, timer, keypad}) { Reset(); } @@ -71,6 +71,10 @@ auto Core::CreateSolarSensor() -> std::unique_ptr { return std::make_unique(); } +void Core::SetKeyStatus(Key key, bool pressed) { + keypad.SetKeyStatus(key, pressed); +} + void Core::Run(int cycles) { using HaltControl = Bus::Hardware::HaltControl; diff --git a/src/nba/src/core.hpp b/src/nba/src/core.hpp index 27a1f64bf..32630f9f4 100644 --- a/src/nba/src/core.hpp +++ b/src/nba/src/core.hpp @@ -30,6 +30,7 @@ struct Core final : CoreBase { auto CreateSolarSensor() -> std::unique_ptr override; void LoadState(SaveState const& state) override; void CopyState(SaveState& state) override; + void SetKeyStatus(Key key, bool pressed) override; void Run(int cycles) override; auto GetROM() -> ROM& override; diff --git a/src/nba/src/hw/keypad/keypad.cpp b/src/nba/src/hw/keypad/keypad.cpp index 8376c641d..48ffe804e 100644 --- a/src/nba/src/hw/keypad/keypad.cpp +++ b/src/nba/src/hw/keypad/keypad.cpp @@ -11,12 +11,9 @@ namespace nba::core { -KeyPad::KeyPad(Scheduler& scheduler, IRQ& irq, std::shared_ptr config) +KeyPad::KeyPad(Scheduler& scheduler, IRQ& irq) : scheduler(scheduler) - , irq(irq) - , config(config) { - scheduler.Register(Scheduler::EventClass::KeyPad_Poll, this, &KeyPad::Poll); - + , irq(irq) { Reset(); } @@ -25,29 +22,18 @@ void KeyPad::Reset() { input.keypad = this; control = {}; control.keypad = this; - config->input_dev->SetOnChangeCallback(std::bind(&KeyPad::UpdateInput, this)); - - input_queue.Reset(); - scheduler.Add(k_poll_interval, Scheduler::EventClass::KeyPad_Poll); } -void KeyPad::UpdateInput() { - auto& input_device = config->input_dev; +void KeyPad::SetKeyStatus(Key key, bool pressed) { + const u16 bit = 1 << (int)key; - u16 input = 0; - - if(!input_device->Poll(Key::A)) input |= 1; - if(!input_device->Poll(Key::B)) input |= 2; - if(!input_device->Poll(Key::Select)) input |= 4; - if(!input_device->Poll(Key::Start)) input |= 8; - if(!input_device->Poll(Key::Right)) input |= 16; - if(!input_device->Poll(Key::Left)) input |= 32; - if(!input_device->Poll(Key::Up)) input |= 64; - if(!input_device->Poll(Key::Down)) input |= 128; - if(!input_device->Poll(Key::R)) input |= 256; - if(!input_device->Poll(Key::L)) input |= 512; + if(pressed) { + input.value &= ~bit; + } else { + input.value |= bit; + } - input_queue.Enqueue(input); + UpdateIRQ(); } void KeyPad::UpdateIRQ() { @@ -64,21 +50,7 @@ void KeyPad::UpdateIRQ() { } } -void KeyPad::Poll() { - PollInternal(); - scheduler.Add(k_poll_interval, Scheduler::EventClass::KeyPad_Poll); -} - -void KeyPad::PollInternal() { - while(input_queue.DataAvailable()) { - input.value = input_queue.Dequeue(); - UpdateIRQ(); - } -} - auto KeyPad::KeyInput::ReadByte(uint offset) -> u8 { - keypad->PollInternal(); - switch(offset) { case 0: return u8(value); @@ -133,38 +105,4 @@ void KeyPad::KeyControl::WriteHalf(u16 value) { keypad->UpdateIRQ(); } -void KeyPad::InputQueue::Reset() { - rd_ptr = 0; - wr_ptr = 0; - size = 0; -} - -bool KeyPad::InputQueue::DataAvailable() { - return size > 0; -} - -void KeyPad::InputQueue::Enqueue(u16 input) { - data[wr_ptr] = input; - wr_ptr = (wr_ptr + 1) % k_buffer_size; - - std::size_t current_size = size.load(); - - while(!size.compare_exchange_weak(current_size, std::min(current_size + 1u, k_buffer_size))) { - current_size = size.load(); - } -} - -auto KeyPad::InputQueue::Dequeue() -> u16 { - const u16 value = data[rd_ptr]; - rd_ptr = (rd_ptr + 1) % k_buffer_size; - - std::size_t current_size = size.load(); - - while(!size.compare_exchange_weak(current_size, current_size - 1)) { - current_size = size.load(); - } - - return value; -} - } // namespace nba::core diff --git a/src/nba/src/hw/keypad/keypad.hpp b/src/nba/src/hw/keypad/keypad.hpp index 19bb7514d..3d2d08130 100644 --- a/src/nba/src/hw/keypad/keypad.hpp +++ b/src/nba/src/hw/keypad/keypad.hpp @@ -7,8 +7,7 @@ #pragma once -#include -#include +#include #include #include #include @@ -18,9 +17,10 @@ namespace nba::core { struct KeyPad { - KeyPad(Scheduler& scheduler, IRQ& irq, std::shared_ptr config); + KeyPad(Scheduler& scheduler, IRQ& irq); void Reset(); + void SetKeyStatus(Key key, bool pressed); struct KeyInput { u16 value = 0x3FF; @@ -50,33 +50,10 @@ struct KeyPad { void CopyState(SaveState& state); private: - static constexpr int k_poll_interval = 16384; - - using Key = InputDevice::Key; - - void UpdateInput(); void UpdateIRQ(); - void Poll(); - void PollInternal(); - Scheduler& scheduler; IRQ& irq; - std::shared_ptr config; - - struct InputQueue { - static constexpr std::size_t k_buffer_size = 16; - - std::atomic data[k_buffer_size]; - std::atomic size = 0; - std::size_t rd_ptr = 0; - std::size_t wr_ptr = 0; - - void Reset(); - bool DataAvailable(); - void Enqueue(u16 input); - auto Dequeue() -> u16; - } input_queue{}; }; } // namespace nba::core diff --git a/src/platform/core/include/platform/emulator_thread.hpp b/src/platform/core/include/platform/emulator_thread.hpp index b548250c5..5f9acc65a 100644 --- a/src/platform/core/include/platform/emulator_thread.hpp +++ b/src/platform/core/include/platform/emulator_thread.hpp @@ -33,19 +33,34 @@ struct EmulatorThread { void Stop(); void Reset(); + void SetKeyStatus(Key key, bool pressed); private: enum class MessageType : u8 { - Reset + Reset, + SetKeyStatus }; struct Message { MessageType type; + union { + struct { + Key key; + u8bool pressed; + } set_key_status; + }; }; void PushMessage(const Message& message); void ProcessMessages(); + static constexpr int k_input_subsample_count = 4; + static constexpr int k_cycles_per_second = 16777216; + static constexpr int k_cycles_per_frame = 280896; + static constexpr int k_cycles_per_subsample = k_cycles_per_frame / k_input_subsample_count; + + static_assert(k_cycles_per_frame % k_input_subsample_count == 0); + std::queue msg_queue; std::mutex msg_queue_mutex; diff --git a/src/platform/core/src/emulator_thread.cpp b/src/platform/core/src/emulator_thread.cpp index 87de106eb..d09a420a1 100644 --- a/src/platform/core/src/emulator_thread.cpp +++ b/src/platform/core/src/emulator_thread.cpp @@ -13,7 +13,7 @@ namespace nba { EmulatorThread::EmulatorThread( std::unique_ptr& core ) : core(core) { - frame_limiter.Reset(59.7275); + frame_limiter.Reset(k_cycles_per_second / (float)k_cycles_per_subsample); } EmulatorThread::~EmulatorThread() { @@ -59,14 +59,16 @@ void EmulatorThread::Start() { frame_limiter.Run([this]() { if(!paused) { + // @todo: decide what to do with the per_frame_cb(). per_frame_cb(); - core->RunForOneFrame(); + core->Run(k_cycles_per_subsample); } }, [this](float fps) { + float real_fps = fps / k_input_subsample_count; if(paused) { - fps = 0; + real_fps = 0; } - frame_rate_cb(fps); + frame_rate_cb(real_fps); }); } }}; @@ -84,6 +86,13 @@ void EmulatorThread::Reset() { PushMessage({.type = MessageType::Reset}); } +void EmulatorThread::SetKeyStatus(Key key, bool pressed) { + PushMessage({ + .type = MessageType::SetKeyStatus, + .set_key_status = {.key = key, .pressed = (u8bool)pressed} + }); +} + void EmulatorThread::PushMessage(const Message& message) { std::lock_guard lock_guard{msg_queue_mutex}; msg_queue.push(message); // @todo: maybe use emplace. @@ -102,6 +111,10 @@ void EmulatorThread::ProcessMessages() { core->Reset(); break; } + case MessageType::SetKeyStatus: { + core->SetKeyStatus(message.set_key_status.key, message.set_key_status.pressed); + break; + } default: Assert(false, "unhandled message type: {}", (int)message.type); } diff --git a/src/platform/qt/rc/config.toml b/src/platform/qt/rc/config.toml index 89d0bf168..79f5c3206 100644 --- a/src/platform/qt/rc/config.toml +++ b/src/platform/qt/rc/config.toml @@ -30,20 +30,20 @@ mp2k_hle_cubic = true mp2k_hle_force_reverb = true [input] -fast_forward = [32, -1, -1, -1, 0] hold_fast_forward = true +fast_forward = [32, -1, -1, -1, 0] controller_guid = "" [input.gba] -b = [83, -1, -1, -1, 0] -a = [65, -1, -1, -1, 0] -select = [16777219, -1, -1, -1, 0] -right = [16777236, -1, -1, -1, 0] -r = [70, -1, -1, -1, 0] -l = [68, -1, -1, -1, 0] -left = [16777234, -1, -1, -1, 0] -start = [16777220, -1, -1, -1, 0] -down = [16777237, -1, -1, -1, 0] -up = [16777235, -1, -1, -1, 0] +b = [16777237,-1,-1,-1,0] +a = [16777235,-1,-1,-1,0] +select = [16777234,-1,-1,-1,0] +right = [16777220-1,-1,-1,0] +r = [70,-1,-1,-1,0] +l = [68,-1,-1,-1,0] +left = [16777219,-1,-1,-1,0] +start = [16777236,-1,-1,-1,0] +down = [83,-1,-1,-1,0] +up = [65,-1,-1,-1,0] [window] fullscreen = false diff --git a/src/platform/qt/src/config.hpp b/src/platform/qt/src/config.hpp index bec62472d..99b8a95a0 100644 --- a/src/platform/qt/src/config.hpp +++ b/src/platform/qt/src/config.hpp @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -49,15 +50,15 @@ struct QtConfig final : nba::PlatformConfig { } }; - Map gba[nba::InputDevice::kKeyCount] { - {Qt::Key_Up}, - {Qt::Key_Down}, - {Qt::Key_Left}, - {Qt::Key_Right}, - {Qt::Key_Return}, - {Qt::Key_Backspace}, + Map gba[(int)nba::Key::Count] { {Qt::Key_A}, {Qt::Key_S}, + {Qt::Key_Backspace}, + {Qt::Key_Return}, + {Qt::Key_Right}, + {Qt::Key_Left}, + {Qt::Key_Up}, + {Qt::Key_Down}, {Qt::Key_D}, {Qt::Key_F} }; diff --git a/src/platform/qt/src/widget/controller_manager.cpp b/src/platform/qt/src/widget/controller_manager.cpp index eb8077b27..6ecd1be0b 100644 --- a/src/platform/qt/src/widget/controller_manager.cpp +++ b/src/platform/qt/src/widget/controller_manager.cpp @@ -119,7 +119,7 @@ void ControllerManager::Open(std::string const& guid) { } void ControllerManager::Close() { - using Key = nba::InputDevice::Key; + using Key = nba::Key; // Unset all keys in case any key is currently pressed. main_window->SetKeyStatus(1, Key::Up, false); @@ -211,7 +211,7 @@ void ControllerManager::ProcessEvents() { } void ControllerManager::UpdateKeyState() { - using Key = nba::InputDevice::Key; + using Key = nba::Key; #if defined(__APPLE__) std::lock_guard guard{lock}; @@ -253,9 +253,9 @@ void ControllerManager::UpdateKeyState() { return pressed; }; - for(int i = 0; i < nba::InputDevice::kKeyCount; i++) { + for(int i = 0; i < (int)nba::Key::Count; i++) { main_window->SetKeyStatus( - 1, static_cast(i), evaluate(input.gba[i])); + 1, static_cast(i), evaluate(input.gba[i])); } bool fast_forward_button = evaluate(input.fast_forward); diff --git a/src/platform/qt/src/widget/controller_manager.hpp b/src/platform/qt/src/widget/controller_manager.hpp index 547e1fc12..39f9a8af8 100644 --- a/src/platform/qt/src/widget/controller_manager.hpp +++ b/src/platform/qt/src/widget/controller_manager.hpp @@ -7,8 +7,8 @@ #pragma once +#include #include -#include #include #include #include diff --git a/src/platform/qt/src/widget/input_window.hpp b/src/platform/qt/src/widget/input_window.hpp index d69c5866e..2f96a99a9 100644 --- a/src/platform/qt/src/widget/input_window.hpp +++ b/src/platform/qt/src/widget/input_window.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -34,7 +34,7 @@ inline auto GetJoystickGUIDStringFromIndex(int device_index) -> std::string { } struct InputWindow : QDialog { - using Key = nba::InputDevice::Key; + using Key = nba::Key; InputWindow( QApplication* app, diff --git a/src/platform/qt/src/widget/main_window.cpp b/src/platform/qt/src/widget/main_window.cpp index 25e133764..1a25955c5 100644 --- a/src/platform/qt/src/widget/main_window.cpp +++ b/src/platform/qt/src/widget/main_window.cpp @@ -55,7 +55,6 @@ MainWindow::MainWindow( config->video_dev = screen; config->audio_dev = std::make_shared(); - config->input_dev = input_device; core = nba::CreateCore(config); emu_thread = std::make_unique(core); @@ -611,9 +610,9 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event) { auto pressed = type == QEvent::KeyPress; auto const& input = config->input; - for(int i = 0; i < nba::InputDevice::kKeyCount; i++) { + for(int i = 0; i < (int)nba::Key::Count; i++) { if(input.gba[i].keyboard == key) { - SetKeyStatus(0, static_cast(i), pressed); + SetKeyStatus(0, static_cast(i), pressed); } } @@ -896,11 +895,10 @@ auto MainWindow::GetSavePath(fs::path const& rom_path, fs::path const& extension return fs::path{rom_path}.replace_extension(extension); } -void MainWindow::SetKeyStatus(int channel, nba::InputDevice::Key key, bool pressed) { - key_input[channel][int(key)] = pressed; +void MainWindow::SetKeyStatus(int channel, nba::Key key, bool pressed) { + key_input[channel][(int)key] = pressed; - input_device->SetKeyStatus(key, - key_input[0][int(key)] || key_input[1][int(key)]); + emu_thread->SetKeyStatus(key, key_input[0][(int)key] || key_input[1][(int)key]); } void MainWindow::SetFastForward(int channel, bool pressed) { diff --git a/src/platform/qt/src/widget/main_window.hpp b/src/platform/qt/src/widget/main_window.hpp index 1a3c66739..a0e0fdaf7 100644 --- a/src/platform/qt/src/widget/main_window.hpp +++ b/src/platform/qt/src/widget/main_window.hpp @@ -117,7 +117,7 @@ private slots: void UpdateMenuBarVisibility(); void UpdateMainWindowActionList(); - void SetKeyStatus(int channel, nba::InputDevice::Key key, bool pressed); + void SetKeyStatus(int channel, nba::Key key, bool pressed); void SetFastForward(int channel, bool pressed); void UpdateWindowSize(); void SetFullscreen(bool value); @@ -130,11 +130,10 @@ private slots: auto GetSavePath(fs::path const& rom_path, fs::path const& extension) -> fs::path; std::shared_ptr screen; - std::shared_ptr input_device = std::make_shared(); std::shared_ptr config = std::make_shared(); std::unique_ptr core; std::unique_ptr emu_thread; - bool key_input[2][nba::InputDevice::kKeyCount] {false}; + bool key_input[2][(int)nba::Key::Count] {false}; bool fast_forward[2] {false}; ControllerManager* controller_manager;