From 199c53f3af1f499e16b08779b53d93e0a13bbc28 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 29 Aug 2023 21:18:04 +1000 Subject: [PATCH] BIOS: Replace TTY patch with syscall hook --- src/core/bios.cpp | 8 --- src/core/bios.h | 1 - src/core/bus.cpp | 67 ++++++++++++++------ src/core/bus.h | 5 ++ src/core/cpu_core.cpp | 73 +++++++++++++++++++++- src/core/cpu_core_private.h | 4 ++ src/core/cpu_recompiler_code_generator.cpp | 8 +++ src/core/fullscreen_ui.cpp | 6 +- src/core/settings.cpp | 6 +- src/core/settings.h | 2 +- src/core/system.cpp | 14 +---- src/duckstation-qt/biossettingswidget.cpp | 6 +- src/duckstation-qt/biossettingswidget.ui | 4 +- 13 files changed, 152 insertions(+), 52 deletions(-) diff --git a/src/core/bios.cpp b/src/core/bios.cpp index 23d5616b1..921fb8842 100644 --- a/src/core/bios.cpp +++ b/src/core/bios.cpp @@ -183,14 +183,6 @@ void BIOS::PatchBIOS(u8* image, u32 image_size, u32 address, u32 value, u32 mask old_disasm.GetCharArray(), new_value, new_disasm.GetCharArray()); } -bool BIOS::PatchBIOSEnableTTY(u8* image, u32 image_size) -{ - Log_InfoPrintf("Patching BIOS to enable TTY/printf"); - PatchBIOS(image, image_size, 0x1FC06F0C, 0x24010001); - PatchBIOS(image, image_size, 0x1FC06F14, 0xAF81A9C0); - return true; -} - bool BIOS::PatchBIOSFastBoot(u8* image, u32 image_size) { // Replace the shell entry point with a return back to the bootstrap. diff --git a/src/core/bios.h b/src/core/bios.h index caa4d026b..55e38a954 100644 --- a/src/core/bios.h +++ b/src/core/bios.h @@ -67,7 +67,6 @@ bool IsValidBIOSForRegion(ConsoleRegion console_region, ConsoleRegion bios_regio void PatchBIOS(u8* image, u32 image_size, u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF)); -bool PatchBIOSEnableTTY(u8* image, u32 image_size); bool PatchBIOSFastBoot(u8* image, u32 image_size); bool PatchBIOSForEXE(u8* image, u32 image_size, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp); diff --git a/src/core/bus.cpp b/src/core/bus.cpp index a02bedc94..143a3459c 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -20,6 +20,7 @@ #include "settings.h" #include "sio.h" #include "spu.h" +#include "system.h" #include "timers.h" #include "timing_event.h" #include "util/state_wrapper.h" @@ -191,6 +192,35 @@ void Reset() RecalculateMemoryTimings(); } +void AddTTYCharacter(char ch) +{ + if (ch == '\r') + { + } + else if (ch == '\n') + { + if (!m_tty_line_buffer.empty()) + { + Log::Writef("TTY", "", LOGLEVEL_INFO, "\033[1;34m%s\033[0m", m_tty_line_buffer.c_str()); +#ifdef _DEBUG + if (CPU::IsTraceEnabled()) + CPU::WriteToExecutionLog("TTY: %s\n", m_tty_line_buffer.c_str()); +#endif + } + m_tty_line_buffer.clear(); + } + else + { + m_tty_line_buffer += ch; + } +} + +void AddTTYString(const std::string_view& str) +{ + for (char ch : str) + AddTTYCharacter(ch); +} + bool DoState(StateWrapper& sw) { u32 ram_size = g_ram_size; @@ -974,25 +1004,7 @@ static TickCount DoEXP2Access(u32 offset, u32& value) { if (offset == 0x23 || offset == 0x80) { - if (value == '\r') - { - } - else if (value == '\n') - { - if (!m_tty_line_buffer.empty()) - { - Log_InfoPrintf("TTY: %s", m_tty_line_buffer.c_str()); -#ifdef _DEBUG - if (CPU::IsTraceEnabled()) - CPU::WriteToExecutionLog("TTY: %s\n", m_tty_line_buffer.c_str()); -#endif - } - m_tty_line_buffer.clear(); - } - else - { - m_tty_line_buffer += static_cast(Truncate8(value)); - } + AddTTYCharacter(static_cast(value)); } else if (offset == 0x41 || offset == 0x42) { @@ -1002,6 +1014,23 @@ static TickCount DoEXP2Access(u32 offset, u32& value) { Log_DevPrintf("BIOS POST2 status: %02X", value & UINT32_C(0x0F)); } +#if 0 + // TODO: Put behind configuration variable + else if (offset == 0x81) + { + Log_WarningPrintf("pcsx_debugbreak()"); + Host::ReportErrorAsync("Error", "pcsx_debugbreak()"); + System::PauseSystem(true); + CPU::ExitExecution(); + } + else if (offset == 0x82) + { + Log_WarningPrintf("pcsx_exit() with status 0x%02X", value & UINT32_C(0xFF)); + Host::ReportErrorAsync("Error", fmt::format("pcsx_exit() with status 0x{:02X}", value & UINT32_C(0xFF))); + System::ShutdownSystem(false); + CPU::ExitExecution(); + } +#endif else { Log_WarningPrintf("EXP2 write: 0x%08X <- 0x%08X", EXP2_BASE | offset, value); diff --git a/src/core/bus.h b/src/core/bus.h index 09469b48b..1ffa62116 100644 --- a/src/core/bus.h +++ b/src/core/bus.h @@ -9,6 +9,7 @@ #include #include #include +#include #include class StateWrapper; @@ -180,4 +181,8 @@ u8* GetMemoryRegionPointer(MemoryRegion region); std::optional SearchMemory(PhysicalMemoryAddress start_address, const u8* pattern, const u8* mask, u32 pattern_length); +// TTY Logging. +void AddTTYCharacter(char ch); +void AddTTYString(const std::string_view& str); + } // namespace Bus diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index ef3d31b10..2c2620565 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -618,6 +618,70 @@ static void LogInstruction(u32 bits, u32 pc, Registers* regs) WriteToExecutionLog("%08x: %08x %s\n", pc, bits, instr.GetCharArray()); } +static void HandleWriteSyscall() +{ + const auto& regs = g_state.regs; + if (regs.a0 != 1) // stdout + return; + + u32 addr = regs.a1; + const u32 count = regs.a2; + for (u32 i = 0; i < count; i++) + { + u8 value; + if (!SafeReadMemoryByte(addr++, &value) || value == 0) + break; + + Bus::AddTTYCharacter(static_cast(value)); + } +} + +static void HandlePutcSyscall() +{ + const auto& regs = g_state.regs; + if (regs.a0 != 0) + Bus::AddTTYCharacter(static_cast(regs.a0)); +} + +static void HandlePutsSyscall() +{ + const auto& regs = g_state.regs; + + u32 addr = regs.a1; + for (u32 i = 0; i < 1024; i++) + { + u8 value; + if (!SafeReadMemoryByte(addr++, &value) || value == 0) + break; + + Bus::AddTTYCharacter(static_cast(value)); + } +} + +void HandleA0Syscall() +{ + const auto& regs = g_state.regs; + const u32 call = regs.t1; + if (call == 0x03) + HandleWriteSyscall(); + else if (call == 0x09 || call == 0x3c) + HandlePutcSyscall(); + else if (call == 0x3e) + HandlePutsSyscall(); +} + +void HandleB0Syscall() +{ + const auto& regs = g_state.regs; + const u32 call = regs.t1; + if (call == 0x35) + HandleWriteSyscall(); + else if (call == 0x3b || call == 0x3d) + HandlePutcSyscall(); + else if (call == 0x3f) + HandlePutsSyscall(); +} + const std::array g_debugger_register_list = { {{"zero", &CPU::g_state.regs.zero}, {"at", &CPU::g_state.regs.at}, @@ -1764,7 +1828,9 @@ void UpdateDebugDispatcherFlag() const bool has_cop0_breakpoints = dcic.super_master_enable_1 && dcic.super_master_enable_2 && dcic.execution_breakpoint_enable; - const bool use_debug_dispatcher = has_any_breakpoints || has_cop0_breakpoints || s_trace_to_log; + const bool use_debug_dispatcher = + has_any_breakpoints || has_cop0_breakpoints || s_trace_to_log || + (g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter && g_settings.bios_tty_logging); if (use_debug_dispatcher == g_state.use_debug_dispatcher) return; @@ -2056,6 +2122,11 @@ template { if (s_trace_to_log) LogInstruction(g_state.current_instruction.bits, g_state.current_instruction_pc, &g_state.regs); + + if (UNLIKELY(g_state.current_instruction_pc == 0xA0)) + HandleA0Syscall(); + else if (UNLIKELY(g_state.current_instruction_pc == 0xB0)) + HandleB0Syscall(); } #if 0 // GTE flag test debugging diff --git a/src/core/cpu_core_private.h b/src/core/cpu_core_private.h index 6b44713eb..3f779fe8e 100644 --- a/src/core/cpu_core_private.h +++ b/src/core/cpu_core_private.h @@ -126,4 +126,8 @@ ALWAYS_INLINE static void StallUntilGTEComplete() (g_state.gte_completion_tick > g_state.pending_ticks) ? g_state.gte_completion_tick : g_state.pending_ticks; } +// kernel call interception +void HandleA0Syscall(); +void HandleB0Syscall(); + } // namespace CPU \ No newline at end of file diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp index 20f275df4..cc205e714 100644 --- a/src/core/cpu_recompiler_code_generator.cpp +++ b/src/core/cpu_recompiler_code_generator.cpp @@ -969,6 +969,14 @@ void CodeGenerator::BlockPrologue() EmitStoreCPUStructField(offsetof(State, exception_raised), Value::FromConstantU8(0)); + if (g_settings.bios_tty_logging) + { + if (m_pc == 0xa0) + EmitFunctionCall(nullptr, &CPU::HandleA0Syscall); + else if (m_pc == 0xb0) + EmitFunctionCall(nullptr, &CPU::HandleB0Syscall); + } + #if 0 EmitFunctionCall(nullptr, &Thunks::LogPC, Value::FromConstantU32(m_pc)); #endif diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index f32d98ff4..f4698a601 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -2871,9 +2871,9 @@ void FullscreenUI::DrawBIOSSettingsPage() DrawToggleSetting(bsi, FSUI_CSTR("Enable Fast Boot"), FSUI_CSTR("Patches the BIOS to skip the boot animation. Safe to enable."), "BIOS", "PatchFastBoot", Settings::DEFAULT_FAST_BOOT_VALUE); - DrawToggleSetting(bsi, FSUI_CSTR("Enable TTY Output"), - FSUI_CSTR("Patches the BIOS to log calls to printf(). Only use when debugging, can break games."), - "BIOS", "PatchTTYEnable", false); + DrawToggleSetting(bsi, FSUI_CSTR("Enable TTY Logging"), + FSUI_CSTR("Logs BIOS calls to printf(). Not all games contain debugging messages."), + "BIOS", "TTYLogging", false); EndMenuButtons(); } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 9b91bb1fa..2fdaec5ef 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -317,7 +317,7 @@ void Settings::Load(SettingsInterface& si) gpu_fifo_size = static_cast(si.GetIntValue("Hacks", "GPUFIFOSize", DEFAULT_GPU_FIFO_SIZE)); gpu_max_run_ahead = si.GetIntValue("Hacks", "GPUMaxRunAhead", DEFAULT_GPU_MAX_RUN_AHEAD); - bios_patch_tty_enable = si.GetBoolValue("BIOS", "PatchTTYEnable", false); + bios_tty_logging = si.GetBoolValue("BIOS", "TTYLogging", false); bios_patch_fast_boot = si.GetBoolValue("BIOS", "PatchFastBoot", DEFAULT_FAST_BOOT_VALUE); multitap_mode = @@ -530,7 +530,7 @@ void Settings::Save(SettingsInterface& si) const si.SetBoolValue("PCDrv", "EnableWrites", pcdrv_enable_writes); si.SetStringValue("PCDrv", "Root", pcdrv_root.c_str()); - si.SetBoolValue("BIOS", "PatchTTYEnable", bios_patch_tty_enable); + si.SetBoolValue("BIOS", "TTYLogging", bios_tty_logging); si.SetBoolValue("BIOS", "PatchFastBoot", bios_patch_fast_boot); for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) @@ -619,7 +619,7 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages) g_settings.use_old_mdec_routines = false; g_settings.pcdrv_enable = false; g_settings.bios_patch_fast_boot = false; - g_settings.bios_patch_tty_enable = false; + g_settings.bios_tty_logging = false; } if (g_settings.pcdrv_enable && g_settings.pcdrv_root.empty()) diff --git a/src/core/settings.h b/src/core/settings.h index f423dfbe1..9e39f75a9 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -228,7 +228,7 @@ struct Settings } } texture_replacements; - bool bios_patch_tty_enable = false; + bool bios_tty_logging = false; bool bios_patch_fast_boot = DEFAULT_FAST_BOOT_VALUE; bool enable_8mb_ram = false; diff --git a/src/core/system.cpp b/src/core/system.cpp index 0842bc357..9117f5cad 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -339,7 +339,7 @@ bool System::IsValid() bool System::IsExecuting() { - DebugAssert(IsValid()); + DebugAssert(s_state != State::Shutdown); return s_system_executing; } @@ -1389,15 +1389,6 @@ bool System::BootSystem(SystemBootParameters parameters) UpdateMultitaps(); InternalReset(); - // Enable tty by patching bios. - if (g_settings.bios_patch_tty_enable) - { - if (s_bios_image_info && s_bios_image_info->patch_compatible) - BIOS::PatchBIOSEnableTTY(Bus::g_bios, Bus::BIOS_SIZE); - else - Log_ErrorPrintf("Not patching TTY enable, as BIOS is not patch compatible."); - } - // Load EXE late after BIOS. if (!exe_boot.empty() && !LoadEXE(exe_boot.c_str())) { @@ -3556,7 +3547,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings) if (g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler && (g_settings.cpu_recompiler_memory_exceptions != old_settings.cpu_recompiler_memory_exceptions || g_settings.cpu_recompiler_block_linking != old_settings.cpu_recompiler_block_linking || - g_settings.cpu_recompiler_icache != old_settings.cpu_recompiler_icache)) + g_settings.cpu_recompiler_icache != old_settings.cpu_recompiler_icache || + g_settings.bios_tty_logging != old_settings.bios_tty_logging)) { Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Recompiler options changed, flushing all blocks."), 5.0f); diff --git a/src/duckstation-qt/biossettingswidget.cpp b/src/duckstation-qt/biossettingswidget.cpp index 33558effb..4ebc7aa7a 100644 --- a/src/duckstation-qt/biossettingswidget.cpp +++ b/src/duckstation-qt/biossettingswidget.cpp @@ -16,15 +16,15 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent) m_ui.setupUi(this); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableTTYOutput, "BIOS", "PatchTTYEnable", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableTTYLogging, "BIOS", "TTYLogging", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "BIOS", "PatchFastBoot", false); dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"), tr("Patches the BIOS to skip the console's boot animation. Does not work with all games, " "but usually safe to enable.")); dialog->registerWidgetHelp( - m_ui.enableTTYOutput, tr("Enable TTY Output"), tr("Unchecked"), - tr("Patches the BIOS to log calls to printf(). Only use when debugging, can break games.")); + m_ui.enableTTYLogging, tr("Enable TTY Logging"), tr("Unchecked"), + tr("Logs BIOS calls to printf(). Not all games contain debugging messages.")); connect(m_ui.imageNTSCJ, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { if (m_dialog->isPerGameSettings() && index == 0) diff --git a/src/duckstation-qt/biossettingswidget.ui b/src/duckstation-qt/biossettingswidget.ui index c728c0949..8af6a98d4 100644 --- a/src/duckstation-qt/biossettingswidget.ui +++ b/src/duckstation-qt/biossettingswidget.ui @@ -164,9 +164,9 @@ - + - Enable TTY Output + Enable TTY Logging