Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework input to use a newly introduced emulator thread message queue #349

Merged
merged 9 commits into from
Jan 24, 2024
1 change: 0 additions & 1 deletion src/nba/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions src/nba/include/nba/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

#include <memory>
#include <nba/device/audio_device.hpp>
#include <nba/device/input_device.hpp>
#include <nba/device/video_device.hpp>
#include <nba/integer.hpp>
#include <string>
Expand Down Expand Up @@ -47,7 +46,6 @@ struct Config {
} audio;

std::shared_ptr<AudioDevice> audio_dev = std::make_shared<NullAudioDevice>();
std::shared_ptr<InputDevice> input_dev = std::make_shared<NullInputDevice>();
std::shared_ptr<VideoDevice> video_dev = std::make_shared<NullVideoDevice>();
};

Expand Down
15 changes: 15 additions & 0 deletions src/nba/include/nba/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -32,6 +46,7 @@ struct CoreBase {
virtual auto CreateSolarSensor() -> std::unique_ptr<SolarSensor> = 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;
Expand Down
62 changes: 0 additions & 62 deletions src/nba/include/nba/device/input_device.hpp

This file was deleted.

2 changes: 2 additions & 0 deletions src/nba/include/nba/integer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ using u64 = std::uint64_t;
using s64 = std::int64_t;
using uint = unsigned int;

using u8bool = u8;

#ifdef NBA_NO_EXPORT_INT_TYPES
} // namespace nba
#endif
2 changes: 0 additions & 2 deletions src/nba/include/nba/scheduler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ struct Scheduler {

SIO_transfer_done,

KeyPad_Poll,

EndOfQueue,
Count
};
Expand Down
6 changes: 5 additions & 1 deletion src/nba/src/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Core::Core(std::shared_ptr<Config> 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();
}
Expand Down Expand Up @@ -71,6 +71,10 @@ auto Core::CreateSolarSensor() -> std::unique_ptr<SolarSensor> {
return std::make_unique<SolarSensor>();
}

void Core::SetKeyStatus(Key key, bool pressed) {
keypad.SetKeyStatus(key, pressed);
}

void Core::Run(int cycles) {
using HaltControl = Bus::Hardware::HaltControl;

Expand Down
1 change: 1 addition & 0 deletions src/nba/src/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct Core final : CoreBase {
auto CreateSolarSensor() -> std::unique_ptr<SolarSensor> 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;
Expand Down
82 changes: 10 additions & 72 deletions src/nba/src/hw/keypad/keypad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,9 @@

namespace nba::core {

KeyPad::KeyPad(Scheduler& scheduler, IRQ& irq, std::shared_ptr<Config> 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();
}

Expand All @@ -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() {
Expand All @@ -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);
Expand Down Expand Up @@ -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
29 changes: 3 additions & 26 deletions src/nba/src/hw/keypad/keypad.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

#pragma once

#include <atomic>
#include <nba/config.hpp>
#include <nba/core.hpp>
#include <nba/save_state.hpp>
#include <nba/scheduler.hpp>
#include <memory>
Expand All @@ -18,9 +17,10 @@
namespace nba::core {

struct KeyPad {
KeyPad(Scheduler& scheduler, IRQ& irq, std::shared_ptr<Config> config);
KeyPad(Scheduler& scheduler, IRQ& irq);

void Reset();
void SetKeyStatus(Key key, bool pressed);

struct KeyInput {
u16 value = 0x3FF;
Expand Down Expand Up @@ -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> config;

struct InputQueue {
static constexpr std::size_t k_buffer_size = 16;

std::atomic<u16> data[k_buffer_size];
std::atomic<std::size_t> 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
46 changes: 41 additions & 5 deletions src/platform/core/include/platform/emulator_thread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
#include <atomic>
#include <functional>
#include <nba/core.hpp>
#include <nba/integer.hpp>
#include <platform/frame_limiter.hpp>
#include <thread>
#include <thread>
#include <queue>
#include <mutex>

namespace nba {

struct EmulatorThread {
EmulatorThread(std::unique_ptr<CoreBase>& core);
EmulatorThread();
~EmulatorThread();

bool IsRunning() const;
Expand All @@ -26,11 +29,44 @@ struct EmulatorThread {
void SetFastForward(bool enabled);
void SetFrameRateCallback(std::function<void(float)> callback);
void SetPerFrameCallback(std::function<void()> callback);
void Start();
void Stop();

void Start(std::unique_ptr<CoreBase> core);
std::unique_ptr<CoreBase> Stop();

void Reset();
void SetKeyStatus(Key key, bool pressed);

private:
std::unique_ptr<CoreBase>& core;
enum class MessageType : u8 {
Reset,
SetKeyStatus
};

struct Message {
MessageType type;
union {
struct {
Key key;
u8bool pressed;
} set_key_status;
};
};

void PushMessage(const Message& message);
void ProcessMessages();
void ProcessMessage(const Message& message);

static constexpr int k_number_of_input_subframes = 4;
static constexpr int k_cycles_per_second = 16777216;
static constexpr int k_cycles_per_frame = 280896;
static constexpr int k_cycles_per_subframe = k_cycles_per_frame / k_number_of_input_subframes;

static_assert(k_cycles_per_frame % k_number_of_input_subframes == 0);

std::queue<Message> msg_queue;
std::mutex msg_queue_mutex;

std::unique_ptr<CoreBase> core;
FrameLimiter frame_limiter;
std::thread thread;
std::atomic_bool running = false;
Expand Down
Loading
Loading