Skip to content

Commit

Permalink
BIOS: Replace TTY patch with syscall hook
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Aug 29, 2023
1 parent 34e4bfd commit 199c53f
Show file tree
Hide file tree
Showing 13 changed files with 152 additions and 52 deletions.
8 changes: 0 additions & 8 deletions src/core/bios.cpp
Expand Up @@ -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.
Expand Down
1 change: 0 additions & 1 deletion src/core/bios.h
Expand Up @@ -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);

Expand Down
67 changes: 48 additions & 19 deletions src/core/bus.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/core/bus.h
Expand Up @@ -9,6 +9,7 @@
#include <bitset>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

class StateWrapper;
Expand Down Expand Up @@ -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
73 changes: 72 additions & 1 deletion src/core/cpu_core.cpp
Expand Up @@ -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},
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/core/cpu_core_private.h
Expand Up @@ -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
8 changes: 8 additions & 0 deletions src/core/cpu_recompiler_code_generator.cpp
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/core/fullscreen_ui.cpp
Expand Up @@ -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();
}
Expand Down
6 changes: 3 additions & 3 deletions src/core/settings.cpp
Expand Up @@ -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 =
Expand Down Expand Up @@ -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++)
Expand Down Expand Up @@ -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())
Expand Down
2 changes: 1 addition & 1 deletion src/core/settings.h
Expand Up @@ -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;

Expand Down
14 changes: 3 additions & 11 deletions src/core/system.cpp
Expand Up @@ -339,7 +339,7 @@ bool System::IsValid()

bool System::IsExecuting()
{
DebugAssert(IsValid());
DebugAssert(s_state != State::Shutdown);
return s_system_executing;
}

Expand Down Expand Up @@ -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()))
{
Expand Down Expand Up @@ -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);

Expand Down
6 changes: 3 additions & 3 deletions src/duckstation-qt/biossettingswidget.cpp
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/duckstation-qt/biossettingswidget.ui
Expand Up @@ -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>
Expand Down

0 comments on commit 199c53f

Please sign in to comment.