mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 05:45:38 +00:00
BIOS: Replace TTY patch with syscall hook
This commit is contained in:
parent
34e4bfdfcd
commit
199c53f3af
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<char>(Truncate8(value));
|
||||
}
|
||||
AddTTYCharacter(static_cast<char>(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);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <bitset>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class StateWrapper;
|
||||
|
@ -180,4 +181,8 @@ u8* GetMemoryRegionPointer(MemoryRegion region);
|
|||
std::optional<PhysicalMemoryAddress> 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
|
||||
|
|
|
@ -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<char>(value));
|
||||
}
|
||||
}
|
||||
|
||||
static void HandlePutcSyscall()
|
||||
{
|
||||
const auto& regs = g_state.regs;
|
||||
if (regs.a0 != 0)
|
||||
Bus::AddTTYCharacter(static_cast<char>(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<char>(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<DebuggerRegisterListEntry, NUM_DEBUGGER_REGISTER_LIST_ENTRIES> 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<PGXPMode pgxp_mode, bool debug>
|
|||
{
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -317,7 +317,7 @@ void Settings::Load(SettingsInterface& si)
|
|||
gpu_fifo_size = static_cast<u32>(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())
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
if (m_dialog->isPerGameSettings() && index == 0)
|
||||
|
|
|
@ -164,9 +164,9 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enableTTYOutput">
|
||||
<widget class="QCheckBox" name="enableTTYLogging">
|
||||
<property name="text">
|
||||
<string>Enable TTY Output</string>
|
||||
<string>Enable TTY Logging</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
Loading…
Reference in a new issue