StarletBootstrapProbe: print the SBR1 ring with --sbr1-record
The probe printed the ring's length only. StarOS's EXP-003 images put lines in the ring that REV_TRACE has no room for (the MEM2 monitor, the measurements), so under Dolphin nothing showed them. After the record's summary, --sbr1-record now prints the ring's text between "SBR1 RING BEGIN" and "SBR1 RING END" on stderr, as StarOS's Broadway loader prints it over the Gecko on the console: at most the ring's 4032 bytes, a byte outside printable ASCII and newline, and the backslash, written \xHH. The formatting is in ProbeRing.h, tested by StarletProbeRingTest. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b457eb18c3
commit
bef2c8cf02
4 files changed
+112
No files matched your search
@@ -22,6 +22,9 @@ target_include_directories(StarletProbePanicCheckTest PRIVATE ${CMAKE_SOURCE_DIR
|
||||
# StarletBootstrapProbe's --cycles budget (Tools/StarletBootstrapProbe/ProbeBudget.h).
|
||||
add_dolphin_test(StarletProbeBudgetTest IOS/Starlet/ProbeBudgetTest.cpp)
|
||||
target_include_directories(StarletProbeBudgetTest PRIVATE ${CMAKE_SOURCE_DIR}/Tools/StarletBootstrapProbe)
|
||||
# StarletBootstrapProbe's SBR1 ring text (Tools/StarletBootstrapProbe/ProbeRing.h).
|
||||
add_dolphin_test(StarletProbeRingTest IOS/Starlet/ProbeRingTest.cpp)
|
||||
target_include_directories(StarletProbeRingTest PRIVATE ${CMAKE_SOURCE_DIR}/Tools/StarletBootstrapProbe)
|
||||
add_dolphin_test(StarletDMATest IOS/Starlet/DMATest.cpp)
|
||||
add_dolphin_test(StarletSRAMReadTest IOS/Starlet/SRAMReadTest.cpp)
|
||||
add_dolphin_test(StarletSRAMWriteTest IOS/Starlet/SRAMWriteTest.cpp)
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2026 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "ProbeRing.h"
|
||||
|
||||
namespace StarletBootstrapProbe
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Record bytes by offset; absent bytes read as zero.
|
||||
class FakeRecord
|
||||
{
|
||||
public:
|
||||
std::map<u32, u8> bytes;
|
||||
void Put(const std::string& text)
|
||||
{
|
||||
for (size_t index = 0; index < text.size(); ++index)
|
||||
bytes[SBR1_RING + static_cast<u32>(index)] = static_cast<u8>(text[index]);
|
||||
}
|
||||
u8 operator()(u32 offset) const
|
||||
{
|
||||
const auto it = bytes.find(offset);
|
||||
return it == bytes.end() ? 0 : it->second;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(StarletProbeRing, PrintsTheTextUpToItsLength)
|
||||
{
|
||||
FakeRecord record;
|
||||
record.Put("REV:BOOT:ENTRY\nEXP3 OWN 23 PAGES 0 CHANGED\nleft over");
|
||||
EXPECT_EQ(RingText(record, 0), "");
|
||||
EXPECT_EQ(RingText(record, 15), "REV:BOOT:ENTRY\n");
|
||||
EXPECT_EQ(RingText(record, 43), "REV:BOOT:ENTRY\nEXP3 OWN 23 PAGES 0 CHANGED\n");
|
||||
}
|
||||
|
||||
TEST(StarletProbeRing, LengthIsClampedToTheRing)
|
||||
{
|
||||
EXPECT_EQ(SBR1_RING_BYTES, 4032u);
|
||||
FakeRecord record;
|
||||
record.Put(std::string(SBR1_RING_BYTES, 'x'));
|
||||
record.bytes[SBR1_SIZE] = 'y';
|
||||
EXPECT_EQ(RingText(record, 0xffffffff), std::string(SBR1_RING_BYTES, 'x'));
|
||||
}
|
||||
|
||||
TEST(StarletProbeRing, EscapesWhatIsNotText)
|
||||
{
|
||||
FakeRecord record;
|
||||
// a, NUL, b, CR, tab, backslash, c, DEL, 0xff, newline: ten bytes.
|
||||
record.Put(std::string("a\0b\r\t\\c\x7f\xff\n", 10));
|
||||
EXPECT_EQ(RingText(record, 10), "a\\x00b\\x0d\\x09\\x5cc\\x7f\\xff\n");
|
||||
}
|
||||
} // namespace
|
||||
} // namespace StarletBootstrapProbe
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2026 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "StarOSPanicCheck.h"
|
||||
|
||||
// The text of StarOS's SBR1 ring (StarOS crates/boot-contract, report.rs), which --sbr1-record
|
||||
// prints after the run, as StarOS's Broadway loader prints it over the Gecko on the console
|
||||
// (SBL:RING BEGIN/END). Bytes come through a callback, so the formatting is tested without a CPU.
|
||||
namespace StarletBootstrapProbe
|
||||
{
|
||||
constexpr u32 SBR1_RING = 0x40;
|
||||
constexpr u32 SBR1_RING_BYTES = SBR1_SIZE - SBR1_RING;
|
||||
|
||||
// The ring's first `length` bytes (at most SBR1_RING_BYTES), read at their record offsets. A
|
||||
// byte outside printable ASCII and '\n', and the backslash itself, is written \xHH, so the text
|
||||
// stays printable and reads back unambiguously.
|
||||
inline std::string RingText(const std::function<u8(u32)>& read_byte, u32 length)
|
||||
{
|
||||
std::string text;
|
||||
const u32 end = std::min(length, SBR1_RING_BYTES);
|
||||
for (u32 index = 0; index < end; ++index)
|
||||
{
|
||||
const u8 byte = read_byte(SBR1_RING + index);
|
||||
if (byte == '\n' || (byte >= 0x20 && byte < 0x7f && byte != '\\'))
|
||||
text += static_cast<char>(byte);
|
||||
else
|
||||
text += fmt::format("\\x{:02x}", byte);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
} // namespace StarletBootstrapProbe
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Core/IOS/Starlet/StarletMemory.h"
|
||||
#include "Core/System.h"
|
||||
#include "ProbeBudget.h"
|
||||
#include "ProbeRing.h"
|
||||
#include "StarOSPanicCheck.h"
|
||||
|
||||
namespace
|
||||
@@ -306,6 +307,16 @@ int main(int argc, char** argv)
|
||||
for (u32 offset = 0; offset < SBR1_SIZE; offset += 4)
|
||||
written += memory.Read_U32(SBR1_BASE + offset) != 0;
|
||||
std::cerr << " nonzero_words " << written << '\n';
|
||||
if (sbr1 == SBR1Mode::Record)
|
||||
{
|
||||
// The ring's text, as StarOS's Broadway loader prints it over the Gecko on the console;
|
||||
// END starts its own line even when the text does not end with one.
|
||||
const std::string ring =
|
||||
RingText([&memory](u32 offset) { return memory.Read_U8(SBR1_BASE + offset); },
|
||||
memory.Read_U32(SBR1_BASE + SBR1_RING_LENGTH));
|
||||
std::cerr << "SBR1 RING BEGIN\n"
|
||||
<< ring << (ring.empty() || ring.back() == '\n' ? "" : "\n") << "SBR1 RING END\n";
|
||||
}
|
||||
}
|
||||
system.GetMemory().Shutdown();
|
||||
return passed ? 0 : 1;
|
||||
|
||||
Reference in new issue
Block a user