From d0f0b4c0e054b2177e524fba274dbefa98dfa7b7 Mon Sep 17 00:00:00 2001 From: Bonta <40473493+Bonta0@users.noreply.github.com> Date: Sun, 4 Jul 2021 13:15:37 +0200 Subject: [PATCH] SI: Implement GBAEmu device --- Source/Core/AudioCommon/AudioCommon.cpp | 3 +- Source/Core/Core/CMakeLists.txt | 2 + Source/Core/Core/HW/SI/SI.cpp | 2 +- Source/Core/Core/HW/SI/SI_Device.cpp | 12 ++ Source/Core/Core/HW/SI/SI_Device.h | 1 + Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp | 167 +++++++++++++++++++++ Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h | 49 ++++++ Source/Core/Core/State.cpp | 2 +- Source/Core/DolphinLib.props | 2 + 9 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp create mode 100644 Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h diff --git a/Source/Core/AudioCommon/AudioCommon.cpp b/Source/Core/AudioCommon/AudioCommon.cpp index cfa5383c7c..6623b5ad8b 100644 --- a/Source/Core/AudioCommon/AudioCommon.cpp +++ b/Source/Core/AudioCommon/AudioCommon.cpp @@ -68,7 +68,8 @@ void InitSoundStream() void PostInitSoundStream() { - // This needs to be called after AudioInterface::Init where input sample rates are set + // This needs to be called after AudioInterface::Init and SerialInterface::Init (for GBA devices) + // where input sample rates are set UpdateSoundStream(); SetSoundStreamRunning(true); diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 65eb857982..0ae84e2bdc 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -624,6 +624,8 @@ if(USE_MGBA) target_sources(core PRIVATE HW/GBACore.cpp HW/GBACore.h + HW/SI/SI_DeviceGBAEmu.cpp + HW/SI/SI_DeviceGBAEmu.h ) target_link_libraries(core PUBLIC mGBA::mgba) target_compile_definitions(core PUBLIC -DHAS_LIBMGBA) diff --git a/Source/Core/Core/HW/SI/SI.cpp b/Source/Core/Core/HW/SI/SI.cpp index 14e586fc05..caaa95cbae 100644 --- a/Source/Core/Core/HW/SI/SI.cpp +++ b/Source/Core/Core/HW/SI/SI.cpp @@ -707,7 +707,7 @@ void UpdateDevices() SIDevices GetDeviceType(int channel) { - if (channel < 0 || channel > 3) + if (channel < 0 || channel >= MAX_SI_CHANNELS || !s_channel[channel].device) return SIDEVICE_NONE; return s_channel[channel].device->GetDeviceType(); diff --git a/Source/Core/Core/HW/SI/SI_Device.cpp b/Source/Core/Core/HW/SI/SI_Device.cpp index 401b1178a7..fa823ff8ad 100644 --- a/Source/Core/Core/HW/SI/SI_Device.cpp +++ b/Source/Core/Core/HW/SI/SI_Device.cpp @@ -13,8 +13,12 @@ #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" #include "Core/HW/SI/SI_DeviceDanceMat.h" #include "Core/HW/SI/SI_DeviceGBA.h" +#ifdef HAS_LIBMGBA +#include "Core/HW/SI/SI_DeviceGBAEmu.h" +#endif #include "Core/HW/SI/SI_DeviceGCAdapter.h" #include "Core/HW/SI/SI_DeviceGCController.h" #include "Core/HW/SI/SI_DeviceGCSteeringWheel.h" @@ -187,6 +191,14 @@ std::unique_ptr SIDevice_Create(const SIDevices device, const int por case SIDEVICE_GC_GBA: return std::make_unique(device, port_number); + case SIDEVICE_GC_GBA_EMULATED: +#ifdef HAS_LIBMGBA + return std::make_unique(device, port_number); +#else + PanicAlertT("Error: This build does not support emulated GBA controllers"); + return std::make_unique(device, port_number); +#endif + case SIDEVICE_GC_KEYBOARD: return std::make_unique(device, port_number); diff --git a/Source/Core/Core/HW/SI/SI_Device.h b/Source/Core/Core/HW/SI/SI_Device.h index c2ae9bef87..d109432b90 100644 --- a/Source/Core/Core/HW/SI/SI_Device.h +++ b/Source/Core/Core/HW/SI/SI_Device.h @@ -93,6 +93,7 @@ enum SIDevices : int // It's kept here so that values below will stay constant. SIDEVICE_AM_BASEBOARD, SIDEVICE_WIIU_ADAPTER, + SIDEVICE_GC_GBA_EMULATED, // Not a valid device. Used for checking whether enum values are valid. SIDEVICE_COUNT, }; diff --git a/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp new file mode 100644 index 0000000000..aaf971b45e --- /dev/null +++ b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp @@ -0,0 +1,167 @@ +// Copyright 2021 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "Common/ChunkFile.h" +#include "Common/CommonTypes.h" +#include "Common/Logging/Log.h" +#include "Common/Swap.h" +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/HW/GBACore.h" +#include "Core/HW/GBAPad.h" +#include "Core/HW/SI/SI.h" +#include "Core/HW/SI/SI_DeviceGBAEmu.h" +#include "Core/HW/SI/SI_DeviceGCController.h" +#include "Core/HW/SystemTimers.h" +#include "Core/Host.h" +#include "Core/NetPlayProto.h" + +namespace SerialInterface +{ +static s64 GetSyncInterval() +{ + return SystemTimers::GetTicksPerSecond() / 1000; +} + +CSIDevice_GBAEmu::CSIDevice_GBAEmu(SIDevices device, int device_number) + : ISIDevice(device, device_number) +{ + m_core = std::make_shared(m_device_number); + m_core->Start(CoreTiming::GetTicks()); + m_gbahost = Host_CreateGBAHost(m_core); + m_core->SetHost(m_gbahost); + ScheduleEvent(m_device_number, GetSyncInterval()); +} + +CSIDevice_GBAEmu::~CSIDevice_GBAEmu() +{ + RemoveEvent(m_device_number); + m_core->Stop(); + m_gbahost.reset(); + m_core.reset(); +} + +int CSIDevice_GBAEmu::RunBuffer(u8* buffer, int request_length) +{ + switch (m_next_action) + { + case NextAction::SendCommand: + { +#ifdef _DEBUG + NOTICE_LOG_FMT(SERIALINTERFACE, "{} cmd {:02x} [> {:02x}{:02x}{:02x}{:02x}]", m_device_number, + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); +#endif + m_last_cmd = static_cast(buffer[0]); + m_timestamp_sent = CoreTiming::GetTicks(); + m_core->SendJoybusCommand(m_timestamp_sent, TransferInterval(), buffer, m_keys); + + RemoveEvent(m_device_number); + ScheduleEvent(m_device_number, TransferInterval() + GetSyncInterval()); + for (int i = 0; i < MAX_SI_CHANNELS; ++i) + { + if (i == m_device_number || SerialInterface::GetDeviceType(i) != GetDeviceType()) + continue; + RemoveEvent(i); + ScheduleEvent(i, 0, static_cast(TransferInterval())); + } + + m_next_action = NextAction::WaitTransferTime; + [[fallthrough]]; + } + + case NextAction::WaitTransferTime: + { + int elapsed_time = static_cast(CoreTiming::GetTicks() - m_timestamp_sent); + // Tell SI to ask again after TransferInterval() cycles + if (TransferInterval() > elapsed_time) + return 0; + m_next_action = NextAction::ReceiveResponse; + [[fallthrough]]; + } + + case NextAction::ReceiveResponse: + { + m_next_action = NextAction::SendCommand; + + std::vector response = m_core->GetJoybusResponse(); + if (response.empty()) + return -1; + std::copy(response.begin(), response.end(), buffer); + +#ifdef _DEBUG + const Common::Log::LOG_LEVELS log_level = + (m_last_cmd == EBufferCommands::CMD_STATUS || m_last_cmd == EBufferCommands::CMD_RESET) ? + Common::Log::LERROR : + Common::Log::LWARNING; + GENERIC_LOG_FMT(Common::Log::SERIALINTERFACE, log_level, + "{} [< {:02x}{:02x}{:02x}{:02x}{:02x}] ({})", + m_device_number, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], + response.size()); +#endif + + return static_cast(response.size()); + } + } + + // This should never happen, but appease MSVC which thinks it might. + ERROR_LOG_FMT(SERIALINTERFACE, "Unknown state {}\n", m_next_action); + return -1; +} + +int CSIDevice_GBAEmu::TransferInterval() +{ + return SIDevice_GetGBATransferTime(m_last_cmd); +} + +bool CSIDevice_GBAEmu::GetData(u32& hi, u32& low) +{ + GCPadStatus pad_status{}; + if (!NetPlay::IsNetPlayRunning()) + pad_status = Pad::GetGBAStatus(m_device_number); + SerialInterface::CSIDevice_GCController::HandleMoviePadStatus(m_device_number, &pad_status); + + static constexpr std::array buttons_map = { + PadButton::PAD_BUTTON_A, // A + PadButton::PAD_BUTTON_B, // B + PadButton::PAD_TRIGGER_Z, // Select + PadButton::PAD_BUTTON_START, // Start + PadButton::PAD_BUTTON_RIGHT, // Right + PadButton::PAD_BUTTON_LEFT, // Left + PadButton::PAD_BUTTON_UP, // Up + PadButton::PAD_BUTTON_DOWN, // Down + PadButton::PAD_TRIGGER_R, // R + PadButton::PAD_TRIGGER_L, // L + }; + + m_keys = 0; + for (size_t i = 0; i < buttons_map.size(); ++i) + m_keys |= static_cast(static_cast((pad_status.button & buttons_map[i]))) << i; + + // Use X button as a reset signal for NetPlay/Movies + if (pad_status.button & PadButton::PAD_BUTTON_X) + m_core->Reset(); + + return false; +} + +void CSIDevice_GBAEmu::SendCommand(u32 command, u8 poll) +{ +} + +void CSIDevice_GBAEmu::DoState(PointerWrap& p) +{ + p.Do(m_next_action); + p.Do(m_last_cmd); + p.Do(m_timestamp_sent); + p.Do(m_keys); + m_core->DoState(p); +} + +void CSIDevice_GBAEmu::OnEvent(u64 userdata, s64 cycles_late) +{ + m_core->SendJoybusCommand(CoreTiming::GetTicks() + userdata, 0, nullptr, m_keys); + ScheduleEvent(m_device_number, userdata + GetSyncInterval()); +} +} // namespace SerialInterface diff --git a/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h new file mode 100644 index 0000000000..d19c43e27b --- /dev/null +++ b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h @@ -0,0 +1,49 @@ +// Copyright 2021 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "Common/CommonTypes.h" +#include "Core/HW/SI/SI_Device.h" + +namespace HW::GBA +{ +class Core; +} // namespace HW::GBA + +class GBAHostInterface; + +namespace SerialInterface +{ +class CSIDevice_GBAEmu : public ISIDevice +{ +public: + CSIDevice_GBAEmu(SIDevices device, int device_number); + ~CSIDevice_GBAEmu(); + + int RunBuffer(u8* buffer, int request_length) override; + int TransferInterval() override; + bool GetData(u32& hi, u32& low) override; + void SendCommand(u32 command, u8 poll) override; + void DoState(PointerWrap& p) override; + void OnEvent(u64 userdata, s64 cycles_late) override; + +private: + enum class NextAction + { + SendCommand, + WaitTransferTime, + ReceiveResponse + }; + + NextAction m_next_action = NextAction::SendCommand; + EBufferCommands m_last_cmd{}; + u64 m_timestamp_sent = 0; + u16 m_keys = 0; + + std::shared_ptr m_core; + std::shared_ptr m_gbahost; +}; +} // namespace SerialInterface diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 5d5f6e9d33..77e31e1ac0 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -73,7 +73,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -constexpr u32 STATE_VERSION = 132; // Last changed in PR 9532 +constexpr u32 STATE_VERSION = 133; // Last changed in PR 9600 // Maps savestate versions to Dolphin versions. // Versions after 42 don't need to be added to this list, diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 7835689d55..0f0dfe227b 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -287,6 +287,7 @@ + @@ -868,6 +869,7 @@ +