From ce52ea35ecd0645b40169c9a2fff3a627f84b207 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Sat, 12 Feb 2022 16:14:05 -0800 Subject: [PATCH 1/7] Jit: Replace magic 32 with GPFifo::GATHER_PIPE_SIZE --- Source/Core/Core/HW/GPFifo.h | 5 +---- Source/Core/Core/PowerPC/Jit64/Jit.cpp | 3 ++- Source/Core/Core/PowerPC/JitArm64/Jit.cpp | 3 ++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/HW/GPFifo.h b/Source/Core/Core/HW/GPFifo.h index 703f2fa1e4..95c100a1eb 100644 --- a/Source/Core/Core/HW/GPFifo.h +++ b/Source/Core/Core/HW/GPFifo.h @@ -9,10 +9,7 @@ class PointerWrap; namespace GPFifo { -enum -{ - GATHER_PIPE_SIZE = 32 -}; +constexpr u32 GATHER_PIPE_SIZE = 32; // Init void Init(); diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/PowerPC/Jit64/Jit.cpp index b54c9ca74a..903bc826fb 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit.cpp @@ -1018,7 +1018,8 @@ bool Jit64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) bool gatherPipeIntCheck = js.fifoWriteAddresses.find(op.address) != js.fifoWriteAddresses.end(); // Gather pipe writes using an immediate address are explicitly tracked. - if (jo.optimizeGatherPipe && (js.fifoBytesSinceCheck >= 32 || js.mustCheckFifo)) + if (jo.optimizeGatherPipe && + (js.fifoBytesSinceCheck >= GPFifo::GATHER_PIPE_SIZE || js.mustCheckFifo)) { js.fifoBytesSinceCheck = 0; js.mustCheckFifo = false; diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp index bdcf40fdfc..113c8e0043 100644 --- a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp @@ -835,7 +835,8 @@ bool JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC) // Gather pipe writes using a non-immediate address are discovered by profiling. bool gatherPipeIntCheck = js.fifoWriteAddresses.find(op.address) != js.fifoWriteAddresses.end(); - if (jo.optimizeGatherPipe && (js.fifoBytesSinceCheck >= 32 || js.mustCheckFifo)) + if (jo.optimizeGatherPipe && + (js.fifoBytesSinceCheck >= GPFifo::GATHER_PIPE_SIZE || js.mustCheckFifo)) { js.fifoBytesSinceCheck = 0; js.mustCheckFifo = false; From 9ffe9d3dc10a87187685f2a6d3dc95db1739332c Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Sat, 12 Feb 2022 16:16:43 -0800 Subject: [PATCH 2/7] CommandProcessor: Remove redundant GATHER_PIPE_SIZE constant --- Source/Core/VideoCommon/CommandProcessor.cpp | 4 ++-- Source/Core/VideoCommon/CommandProcessor.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Core/VideoCommon/CommandProcessor.cpp b/Source/Core/VideoCommon/CommandProcessor.cpp index 51b67a9ead..073b35bdaf 100644 --- a/Source/Core/VideoCommon/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/CommandProcessor.cpp @@ -382,7 +382,7 @@ void GatherPipeBursted() } else { - fifo.CPWritePointer.fetch_add(GATHER_PIPE_SIZE, std::memory_order_relaxed); + fifo.CPWritePointer.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_relaxed); } if (m_CPCtrlReg.GPReadEnable && m_CPCtrlReg.GPLinkEnable) @@ -396,7 +396,7 @@ void GatherPipeBursted() if (fifo.bFF_HiWatermark.load(std::memory_order_relaxed) != 0) CoreTiming::ForceExceptionCheck(0); - fifo.CPReadWriteDistance.fetch_add(GATHER_PIPE_SIZE, std::memory_order_seq_cst); + fifo.CPReadWriteDistance.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_seq_cst); Fifo::RunGpu(); diff --git a/Source/Core/VideoCommon/CommandProcessor.h b/Source/Core/VideoCommon/CommandProcessor.h index 2da7f1c84d..ca2b6fbb28 100644 --- a/Source/Core/VideoCommon/CommandProcessor.h +++ b/Source/Core/VideoCommon/CommandProcessor.h @@ -97,7 +97,6 @@ enum enum { - GATHER_PIPE_SIZE = 32, INT_CAUSE_CP = 0x800 }; From 55f8aa9921bc74a6e4172af5f9d22a4c40a3f887 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Sat, 12 Feb 2022 16:28:24 -0800 Subject: [PATCH 3/7] VideoCommon/Fifo: Use GPFifo::GATHER_PIPE_SIZE instead of magic 32 --- Source/Core/VideoCommon/Fifo.cpp | 44 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/Source/Core/VideoCommon/Fifo.cpp b/Source/Core/VideoCommon/Fifo.cpp index 9d558d056d..ccee18f40e 100644 --- a/Source/Core/VideoCommon/Fifo.cpp +++ b/Source/Core/VideoCommon/Fifo.cpp @@ -17,6 +17,7 @@ #include "Core/Config/MainSettings.h" #include "Core/ConfigManager.h" #include "Core/CoreTiming.h" +#include "Core/HW/GPFifo.h" #include "Core/HW/Memmap.h" #include "Core/Host.h" #include "Core/System.h" @@ -249,13 +250,14 @@ void* PopFifoAuxBuffer(size_t size) // Description: RunGpuLoop() sends data through this function. static void ReadDataFromFifo(u32 readPtr) { - constexpr size_t len = 32; - if (len > static_cast(s_video_buffer + FIFO_SIZE - s_video_buffer_write_ptr)) + if (GPFifo::GATHER_PIPE_SIZE > + static_cast(s_video_buffer + FIFO_SIZE - s_video_buffer_write_ptr)) { const size_t existing_len = s_video_buffer_write_ptr - s_video_buffer_read_ptr; - if (len > static_cast(FIFO_SIZE - existing_len)) + if (GPFifo::GATHER_PIPE_SIZE > static_cast(FIFO_SIZE - existing_len)) { - PanicAlertFmt("FIFO out of bounds (existing {} + new {} > {})", existing_len, len, FIFO_SIZE); + PanicAlertFmt("FIFO out of bounds (existing {} + new {} > {})", existing_len, + GPFifo::GATHER_PIPE_SIZE, FIFO_SIZE); return; } memmove(s_video_buffer, s_video_buffer_read_ptr, existing_len); @@ -263,16 +265,15 @@ static void ReadDataFromFifo(u32 readPtr) s_video_buffer_read_ptr = s_video_buffer; } // Copy new video instructions to s_video_buffer for future use in rendering the new picture - Memory::CopyFromEmu(s_video_buffer_write_ptr, readPtr, len); - s_video_buffer_write_ptr += len; + Memory::CopyFromEmu(s_video_buffer_write_ptr, readPtr, GPFifo::GATHER_PIPE_SIZE); + s_video_buffer_write_ptr += GPFifo::GATHER_PIPE_SIZE; } // The deterministic_gpu_thread version. static void ReadDataFromFifoOnCPU(u32 readPtr) { - constexpr size_t len = 32; u8* write_ptr = s_video_buffer_write_ptr; - if (len > static_cast(s_video_buffer + FIFO_SIZE - write_ptr)) + if (GPFifo::GATHER_PIPE_SIZE > static_cast(s_video_buffer + FIFO_SIZE - write_ptr)) { // We can't wrap around while the GPU is working on the data. // This should be very rare due to the reset in SyncGPU. @@ -290,17 +291,18 @@ static void ReadDataFromFifoOnCPU(u32 readPtr) } write_ptr = s_video_buffer_write_ptr; const size_t existing_len = write_ptr - s_video_buffer_pp_read_ptr; - if (len > static_cast(FIFO_SIZE - existing_len)) + if (GPFifo::GATHER_PIPE_SIZE > static_cast(FIFO_SIZE - existing_len)) { - PanicAlertFmt("FIFO out of bounds (existing {} + new {} > {})", existing_len, len, FIFO_SIZE); + PanicAlertFmt("FIFO out of bounds (existing {} + new {} > {})", existing_len, + GPFifo::GATHER_PIPE_SIZE, FIFO_SIZE); return; } } - Memory::CopyFromEmu(s_video_buffer_write_ptr, readPtr, len); + Memory::CopyFromEmu(s_video_buffer_write_ptr, readPtr, GPFifo::GATHER_PIPE_SIZE); s_video_buffer_pp_read_ptr = OpcodeDecoder::RunFifo( - DataReader(s_video_buffer_pp_read_ptr, write_ptr + len), nullptr); + DataReader(s_video_buffer_pp_read_ptr, write_ptr + GPFifo::GATHER_PIPE_SIZE), nullptr); // This would have to be locked if the GPU thread didn't spin. - s_video_buffer_write_ptr = write_ptr + len; + s_video_buffer_write_ptr = write_ptr + GPFifo::GATHER_PIPE_SIZE; } void ResetVideoBuffer() @@ -362,20 +364,22 @@ void RunGpuLoop() if (readPtr == fifo.CPEnd.load(std::memory_order_relaxed)) readPtr = fifo.CPBase.load(std::memory_order_relaxed); else - readPtr += 32; + readPtr += GPFifo::GATHER_PIPE_SIZE; - ASSERT_MSG(COMMANDPROCESSOR, - (s32)fifo.CPReadWriteDistance.load(std::memory_order_relaxed) - 32 >= 0, + const s32 distance = + static_cast(fifo.CPReadWriteDistance.load(std::memory_order_relaxed)) - + GPFifo::GATHER_PIPE_SIZE; + ASSERT_MSG(COMMANDPROCESSOR, distance >= 0, "Negative fifo.CPReadWriteDistance = {} in FIFO Loop !\nThat can produce " "instability in the game. Please report it.", - fifo.CPReadWriteDistance.load(std::memory_order_relaxed) - 32); + distance); u8* write_ptr = s_video_buffer_write_ptr; s_video_buffer_read_ptr = OpcodeDecoder::RunFifo( DataReader(s_video_buffer_read_ptr, write_ptr), &cyclesExecuted); fifo.CPReadPointer.store(readPtr, std::memory_order_relaxed); - fifo.CPReadWriteDistance.fetch_sub(32, std::memory_order_seq_cst); + fifo.CPReadWriteDistance.fetch_sub(GPFifo::GATHER_PIPE_SIZE, std::memory_order_seq_cst); if ((write_ptr - s_video_buffer_read_ptr) == 0) { fifo.SafeCPReadPointer.store(fifo.CPReadPointer.load(std::memory_order_relaxed), @@ -498,10 +502,10 @@ static int RunGpuOnCpu(int ticks) } else { - fifo.CPReadPointer.fetch_add(32, std::memory_order_relaxed); + fifo.CPReadPointer.fetch_add(GPFifo::GATHER_PIPE_SIZE, std::memory_order_relaxed); } - fifo.CPReadWriteDistance.fetch_sub(32, std::memory_order_relaxed); + fifo.CPReadWriteDistance.fetch_sub(GPFifo::GATHER_PIPE_SIZE, std::memory_order_relaxed); } CommandProcessor::SetCPStatusFromGPU(); From 1c74867c71ffd68dc0712599081153f22229afa3 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Sat, 12 Feb 2022 16:31:04 -0800 Subject: [PATCH 4/7] GPFifo: Make s_gather_pipe size new constant GATHER_PIPE_EXTRA_SIZE --- Source/Core/Core/HW/GPFifo.cpp | 2 +- Source/Core/Core/HW/GPFifo.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Core/Core/HW/GPFifo.cpp b/Source/Core/Core/HW/GPFifo.cpp index a2c568bb38..ef05946c2b 100644 --- a/Source/Core/Core/HW/GPFifo.cpp +++ b/Source/Core/Core/HW/GPFifo.cpp @@ -29,7 +29,7 @@ namespace GPFifo // the same function could use both methods. Compile 2 different versions of each such block? // More room for the fastmodes -alignas(32) static u8 s_gather_pipe[GATHER_PIPE_SIZE * 16]; +alignas(GATHER_PIPE_SIZE) static u8 s_gather_pipe[GATHER_PIPE_EXTRA_SIZE]; static size_t GetGatherPipeCount() { diff --git a/Source/Core/Core/HW/GPFifo.h b/Source/Core/Core/HW/GPFifo.h index 95c100a1eb..2925e82c05 100644 --- a/Source/Core/Core/HW/GPFifo.h +++ b/Source/Core/Core/HW/GPFifo.h @@ -10,6 +10,7 @@ class PointerWrap; namespace GPFifo { constexpr u32 GATHER_PIPE_SIZE = 32; +constexpr u32 GATHER_PIPE_EXTRA_SIZE = GATHER_PIPE_SIZE * 16; // Init void Init(); From 095803d1e965b89bd3010f8b426eec85b17a1f45 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Sat, 12 Feb 2022 17:34:12 -0800 Subject: [PATCH 5/7] CommandProcessor: Move contents of second unknown opcode panic alert to the log This way, the information is always available, and users don't have to deal with a wall of meaningless information. --- Source/Core/VideoCommon/CommandProcessor.cpp | 52 ++++++++------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/Source/Core/VideoCommon/CommandProcessor.cpp b/Source/Core/VideoCommon/CommandProcessor.cpp index 073b35bdaf..81f6118a94 100644 --- a/Source/Core/VideoCommon/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/CommandProcessor.cpp @@ -634,41 +634,29 @@ void HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess) "Further errors will be sent to the Video Backend log and\n" "Dolphin will now likely crash or hang. Enjoy.", cmd_byte, fmt::ptr(buffer), preprocess); - - PanicAlertFmt("Illegal command {:02x}\n" - "CPBase: {:#010x}\n" - "CPEnd: {:#010x}\n" - "CPHiWatermark: {:#010x}\n" - "CPLoWatermark: {:#010x}\n" - "CPReadWriteDistance: {:#010x}\n" - "CPWritePointer: {:#010x}\n" - "CPReadPointer: {:#010x}\n" - "CPBreakpoint: {:#010x}\n" - "bFF_GPReadEnable: {}\n" - "bFF_BPEnable: {}\n" - "bFF_BPInt: {}\n" - "bFF_Breakpoint: {}\n" - "bFF_GPLinkEnable: {}\n" - "bFF_HiWatermarkInt: {}\n" - "bFF_LoWatermarkInt: {}\n", - cmd_byte, fifo.CPBase.load(std::memory_order_relaxed), - fifo.CPEnd.load(std::memory_order_relaxed), fifo.CPHiWatermark, - fifo.CPLoWatermark, fifo.CPReadWriteDistance.load(std::memory_order_relaxed), - fifo.CPWritePointer.load(std::memory_order_relaxed), - fifo.CPReadPointer.load(std::memory_order_relaxed), - fifo.CPBreakpoint.load(std::memory_order_relaxed), - fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) ? "true" : "false", - fifo.bFF_BPEnable.load(std::memory_order_relaxed) ? "true" : "false", - fifo.bFF_BPInt.load(std::memory_order_relaxed) ? "true" : "false", - fifo.bFF_Breakpoint.load(std::memory_order_relaxed) ? "true" : "false", - fifo.bFF_GPLinkEnable.load(std::memory_order_relaxed) ? "true" : "false", - fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false", - fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false"); } // We always generate this log message, though we only generate the panic alerts once. - ERROR_LOG_FMT(VIDEO, "FIFO: Unknown Opcode ({:#04x} @ {}, preprocessing = {})", cmd_byte, - fmt::ptr(buffer), preprocess ? "yes" : "no"); + ERROR_LOG_FMT(VIDEO, + "FIFO: Unknown Opcode {:#04x} @ {}, preprocessing = {}, CPBase: {:#010x}, CPEnd: " + "{:#010x}, CPHiWatermark: {:#010x}, CPLoWatermark: {:#010x}, CPReadWriteDistance: " + "{:#010x}, CPWritePointer: {:#010x}, CPReadPointer: {:#010x}, CPBreakpoint: " + "{:#010x}, bFF_GPReadEnable: {}, bFF_BPEnable: {}, bFF_BPInt: {}, bFF_Breakpoint: " + "{}, bFF_GPLinkEnable: {}, bFF_HiWatermarkInt: {}, bFF_LoWatermarkInt: {}", + cmd_byte, fmt::ptr(buffer), preprocess ? "yes" : "no", + fifo.CPBase.load(std::memory_order_relaxed), + fifo.CPEnd.load(std::memory_order_relaxed), fifo.CPHiWatermark, fifo.CPLoWatermark, + fifo.CPReadWriteDistance.load(std::memory_order_relaxed), + fifo.CPWritePointer.load(std::memory_order_relaxed), + fifo.CPReadPointer.load(std::memory_order_relaxed), + fifo.CPBreakpoint.load(std::memory_order_relaxed), + fifo.bFF_GPReadEnable.load(std::memory_order_relaxed) ? "true" : "false", + fifo.bFF_BPEnable.load(std::memory_order_relaxed) ? "true" : "false", + fifo.bFF_BPInt.load(std::memory_order_relaxed) ? "true" : "false", + fifo.bFF_Breakpoint.load(std::memory_order_relaxed) ? "true" : "false", + fifo.bFF_GPLinkEnable.load(std::memory_order_relaxed) ? "true" : "false", + fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false", + fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false"); } } // namespace CommandProcessor From 68cdceb4bef1b6923c3d3039b2535209dd58db8b Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Sat, 12 Feb 2022 17:44:07 -0800 Subject: [PATCH 6/7] CommandProcessor: Log PC and LR on unknown opcodes --- Source/Core/VideoCommon/CommandProcessor.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoCommon/CommandProcessor.cpp b/Source/Core/VideoCommon/CommandProcessor.cpp index 81f6118a94..451cdcce9a 100644 --- a/Source/Core/VideoCommon/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/CommandProcessor.cpp @@ -17,6 +17,7 @@ #include "Core/HW/GPFifo.h" #include "Core/HW/MMIO.h" #include "Core/HW/ProcessorInterface.h" +#include "Core/PowerPC/PowerPC.h" #include "Core/System.h" #include "VideoCommon/Fifo.h" @@ -637,12 +638,21 @@ void HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess) } // We always generate this log message, though we only generate the panic alerts once. + // + // PC and LR are generally inaccurate in dual-core and are still misleading in single-core + // due to the gather pipe queueing data. Changing GATHER_PIPE_SIZE to 1 and + // GATHER_PIPE_EXTRA_SIZE to 16 * 32 in GPFifo.h, and using the cached interpreter CPU emulation + // engine, can result in more accurate information (though it is still a bit delayed). + // PC and LR are meaningless when using the fifoplayer, and will generally not be helpful if the + // unknown opcode is inside of a display list. Also note that the changes in GPFifo.h are not + // accurate and may introduce timing issues. ERROR_LOG_FMT(VIDEO, "FIFO: Unknown Opcode {:#04x} @ {}, preprocessing = {}, CPBase: {:#010x}, CPEnd: " "{:#010x}, CPHiWatermark: {:#010x}, CPLoWatermark: {:#010x}, CPReadWriteDistance: " "{:#010x}, CPWritePointer: {:#010x}, CPReadPointer: {:#010x}, CPBreakpoint: " "{:#010x}, bFF_GPReadEnable: {}, bFF_BPEnable: {}, bFF_BPInt: {}, bFF_Breakpoint: " - "{}, bFF_GPLinkEnable: {}, bFF_HiWatermarkInt: {}, bFF_LoWatermarkInt: {}", + "{}, bFF_GPLinkEnable: {}, bFF_HiWatermarkInt: {}, bFF_LoWatermarkInt: {}, " + "approximate PC: {:08x}, approximate LR: {:08x}", cmd_byte, fmt::ptr(buffer), preprocess ? "yes" : "no", fifo.CPBase.load(std::memory_order_relaxed), fifo.CPEnd.load(std::memory_order_relaxed), fifo.CPHiWatermark, fifo.CPLoWatermark, @@ -656,7 +666,7 @@ void HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess) fifo.bFF_Breakpoint.load(std::memory_order_relaxed) ? "true" : "false", fifo.bFF_GPLinkEnable.load(std::memory_order_relaxed) ? "true" : "false", fifo.bFF_HiWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false", - fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false"); + fifo.bFF_LoWatermarkInt.load(std::memory_order_relaxed) ? "true" : "false", PC, LR); } } // namespace CommandProcessor From 97482a61c66d2a70bf375985ff27ec1780c0f961 Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Sat, 12 Feb 2022 18:29:59 -0800 Subject: [PATCH 7/7] CommandProcessor: Ignore unknown opcode for 0x3f --- Source/Core/VideoCommon/CommandProcessor.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Source/Core/VideoCommon/CommandProcessor.cpp b/Source/Core/VideoCommon/CommandProcessor.cpp index 451cdcce9a..2203e235a7 100644 --- a/Source/Core/VideoCommon/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/CommandProcessor.cpp @@ -616,12 +616,19 @@ void SetCpClearRegister() void HandleUnknownOpcode(u8 cmd_byte, const u8* buffer, bool preprocess) { - // Datel software uses 0x01 during startup, and Mario Party 5's Wiggler capsule - // accidentally uses 0x01-0x03 due to sending 4 more vertices than intended. - // Hardware testing indicates that 0x01-0x07 do nothing, so to avoid annoying the user with + // Datel software uses 0x01 during startup, and Mario Party 5's Wiggler capsule accidentally uses + // 0x01-0x03 due to sending 4 more vertices than intended (see https://dolp.in/i8104). + // Prince of Persia: Rival Swords sends 0x3f if the home menu is opened during the intro cutscene + // due to a game bug resulting in an incorrect vertex desc that results in the float value 1.0, + // encoded as 0x3f800000, being parsed as an opcode (see https://dolp.in/i9203). + // + // Hardware testing indicates that these opcodes do nothing, so to avoid annoying the user with // spurious popups, we don't create a panic alert in those cases. Other unknown opcodes - // (such as 0x18) seem to result in hangs. - if (!s_is_fifo_error_seen && cmd_byte > 0x07) + // (such as 0x18) seem to result in actual hangs on real hardware, so the alert still is important + // to keep around for unexpected cases. + const bool suppress_panic_alert = (cmd_byte <= 0x7) || (cmd_byte == 0x3f); + + if (!s_is_fifo_error_seen && !suppress_panic_alert) { s_is_fifo_error_seen = true;