Add basic checkbox UI for GPI switched, add display of GPO LEDs

Try it with Parappa.
This commit is contained in:
Henrik Rydgård 2024-05-12 18:01:25 +02:00
parent 9dc8c05fae
commit a059750f5a
10 changed files with 77 additions and 10 deletions

View file

@ -852,6 +852,11 @@ void CheckBox::GetContentDimensions(const UIContext &dc, float &w, float &h) con
w = bounds_.w;
}
BitCheckBox::BitCheckBox(uint32_t *bitfield, uint32_t bit, std::string_view text, std::string_view smallText, LayoutParams *layoutParams)
: CheckBox(nullptr, text, smallText, layoutParams), bitfield_(bitfield), bit_(bit) {
_dbg_assert_msg_(bit != 0, "bit is a mask, not a bit index");
}
void BitCheckBox::Toggle() {
if (bitfield_) {
*bitfield_ = *bitfield_ ^ bit_;

View file

@ -916,10 +916,8 @@ private:
class BitCheckBox : public CheckBox {
public:
BitCheckBox(uint32_t *bitfield, uint32_t bit, std::string_view text, std::string_view smallText = "", LayoutParams *layoutParams = nullptr)
: CheckBox(nullptr, text, smallText, layoutParams), bitfield_(bitfield), bit_(bit) {
}
// bit is a bitmask (should only have a single bit set), not a bit index.
BitCheckBox(uint32_t *bitfield, uint32_t bit, std::string_view text, std::string_view smallText = "", LayoutParams *layoutParams = nullptr);
BitCheckBox(int *bitfield, int bit, std::string_view text, std::string_view smallText = "", LayoutParams *layoutParams = nullptr) : BitCheckBox((uint32_t *)bitfield, (uint32_t)bit, text, smallText, layoutParams) {}
void Toggle() override;

View file

@ -306,6 +306,8 @@ static const ConfigSetting generalSettings[] = {
ConfigSetting("IgnoreCompatSettings", &g_Config.sIgnoreCompatSettings, "", CfgFlag::PER_GAME | CfgFlag::REPORT),
ConfigSetting("RunBehindPauseMenu", &g_Config.bRunBehindPauseMenu, false, CfgFlag::DEFAULT),
ConfigSetting("ShowGPOLEDs", &g_Config.bShowGPOLEDs, false, CfgFlag::PER_GAME),
};
static bool DefaultSasThread() {

View file

@ -265,6 +265,9 @@ public:
bool bRenderDuplicateFrames;
bool bRenderMultiThreading;
// HW debug
bool bShowGPOLEDs;
// Sound
bool bEnableSound;
int iAudioBackend;

View file

@ -100,6 +100,8 @@ static bool kernelRunning = false;
KernelObjectPool kernelObjects;
KernelStats kernelStats;
u32 registeredExitCbId;
u32 g_GPOBits; // Really just 8 bits on the real hardware.
u32 g_GPIBits; // Really just 8 bits on the real hardware.
void __KernelInit()
{
@ -161,6 +163,7 @@ void __KernelInit()
__PPGeInit();
kernelRunning = true;
g_GPOBits = 0;
INFO_LOG(SCEKERNEL, "Kernel initialized.");
}
@ -355,18 +358,19 @@ int sceKernelRegisterDefaultExceptionHandler()
return 0;
}
void sceKernelSetGPO(u32 ledAddr)
void sceKernelSetGPO(u32 ledBits)
{
// Sets debug LEDs.
// Not really interesting, and a few games really spam it
// DEBUG_LOG(SCEKERNEL, "sceKernelSetGPO(%02x)", ledAddr);
// Sets debug LEDs. Some games do interesting stuff with this, like a metronome in Parappa.
// Shows up as a vertical strip of LEDs at the side of the screen, if enabled.
g_GPOBits = ledBits;
}
u32 sceKernelGetGPI()
{
// Always returns 0 on production systems.
DEBUG_LOG(SCEKERNEL, "0=sceKernelGetGPI()");
return 0;
// On developer systems, there are 8 switches that control the lower 8 bits of the return value.
DEBUG_LOG(SCEKERNEL, "%d=sceKernelGetGPI()", g_GPIBits);
return g_GPIBits;
}
// #define LOG_CACHE

View file

@ -569,6 +569,9 @@ struct KernelStats {
extern KernelStats kernelStats;
extern u32 registeredExitCbId;
extern u32 g_GPOBits;
extern u32 g_GPIBits;
void Register_ThreadManForUser();
void Register_ThreadManForKernel();
void Register_LoadExecForUser();

View file

@ -56,6 +56,7 @@
#include "Core/System.h"
#include "Core/Reporting.h"
#include "Core/CoreParameter.h"
#include "Core/HLE/sceKernel.h" // GPI/GPO
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
@ -141,6 +142,11 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) {
items->Add(new Choice(dev->T("Reset limited logging")))->OnClick.Handle(this, &DevMenuScreen::OnResetLimitedLogging);
items->Add(new Choice(dev->T("GPI/GPO switches/LEDs")))->OnClick.Add([=](UI::EventParams &e) {
screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs")));
return UI::EVENT_DONE;
});
items->Add(new Choice(dev->T("Create frame dump")))->OnClick.Add([](UI::EventParams &e) {
GPURecord::RecordNextFrame([](const Path &dumpPath) {
NOTICE_LOG(SYSTEM, "Frame dump created at '%s'", dumpPath.c_str());
@ -213,6 +219,16 @@ void DevMenuScreen::dialogFinished(const Screen *dialog, DialogResult result) {
// TriggerFinish(DR_OK);
}
void GPIGPOScreen::CreatePopupContents(UI::ViewGroup *parent) {
using namespace UI;
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
parent->Add(new CheckBox(&g_Config.bShowGPOLEDs, dev->T("Show GPO LEDs")));
for (int i = 0; i < 8; i++) {
std::string name = ApplySafeSubstitutions(dev->T("GPI switch %1"), i);
parent->Add(new BitCheckBox(&g_GPIBits, 1 << i, name));
}
}
void LogScreen::UpdateLog() {
using namespace UI;
RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();

View file

@ -145,6 +145,15 @@ private:
unsigned int addr_;
};
class GPIGPOScreen : public PopupScreen {
public:
GPIGPOScreen(std::string_view title) : PopupScreen(title, "OK") {}
const char *tag() const override { return "GPIGPO"; }
protected:
void CreatePopupContents(UI::ViewGroup *parent) override;
};
class JitCompareScreen : public UIDialogScreenWithBackground {
public:
void CreateViews() override;

View file

@ -1521,6 +1521,8 @@ bool EmuScreen::hasVisibleUI() {
return true;
if (g_Config.bEnableCardboardVR || g_Config.bEnableNetworkChat)
return true;
if (g_Config.bShowGPOLEDs)
return true;
// Debug UI.
if ((DebugOverlay)g_Config.iDebugOverlay != DebugOverlay::OFF || g_Config.bShowDeveloperMenu)
return true;
@ -1571,6 +1573,26 @@ void EmuScreen::renderUI() {
}
#endif
if (g_Config.bShowGPOLEDs) {
// Draw a vertical strip of LEDs at the right side of the screen.
const float ledSize = 24.0f;
const float spacing = 4.0f;
const float height = 8 * ledSize + 7 * spacing;
const float x = ctx->GetBounds().w - spacing - ledSize;
const float y = (ctx->GetBounds().h - height) * 0.5f;
ctx->FillRect(UI::Drawable(0xFF000000), Bounds(x - spacing, y - spacing, ledSize + spacing * 2, height + spacing * 2));
for (int i = 0; i < 8; i++) {
int bit = (g_GPOBits >> i) & 1;
uint32_t color = 0xFF30FF30;
if (!bit) {
color = darkenColor(darkenColor(color));
}
Bounds ledBounds(x, y + (spacing + ledSize) * i, ledSize, ledSize);
ctx->FillRect(UI::Drawable(color), ledBounds);
}
ctx->Flush();
}
if (coreState == CORE_RUNTIME_ERROR || coreState == CORE_STEPPING) {
const MIPSExceptionInfo &info = Core_GetExceptionInfo();
if (info.type != MIPSExceptionType::NONE) {

View file

@ -1843,6 +1843,11 @@ void DeveloperToolsScreen::CreateViews() {
list->Add(new CheckBox(&g_Config.bShowOnScreenMessages, dev->T("Show on-screen messages")));
list->Add(new Choice(dev->T("GPI/GPO switches/LEDs")))->OnClick.Add([=](UI::EventParams &e) {
screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs")));
return UI::EVENT_DONE;
});
#if PPSSPP_PLATFORM(ANDROID)
static const char *framerateModes[] = { "Default", "Request 60Hz", "Force 60Hz" };
PopupMultiChoice *framerateMode = list->Add(new PopupMultiChoice(&g_Config.iDisplayFramerateMode, gr->T("Framerate mode"), framerateModes, 0, ARRAY_SIZE(framerateModes), I18NCat::GRAPHICS, screenManager()));