65816-llvm-mos/demos/wide32CrashRepro.cpp
Scott Duensing da095402ec Updated
2026-06-02 23:17:57 -05:00

156 lines
6.6 KiB
C++

// cxxStreamProbe.cpp - exercise the C++ stream + format + path surface
// (Phase 5.4). Probes the cout-replacement pattern:
//
// 1. etl::string_stream<<int composing into a fixed-capacity buffer.
// 2. etl::format_to(buf, "{}", 42) — the std::format substitute (gated
// behind CXX_STREAM_PROBE_WITH_FORMAT; pulls ~50 KB of format
// machinery — see step 5 size spike below).
// 3. iigs::path::pathJoin + pathSplit + pathNormalize on a ProDOS-style
// colon-separated path.
// 4. Compile-time contract that etl::chrono::*_clock::duration::rep is
// int32_t (the i64-libcall guard from etl_profile.h).
//
// Build flavors:
// default : path + string_stream + chrono
// contract. Single-bank bin.
// GNO_CFLAGS=-DCXX_STREAM_PROBE_WITH_FORMAT=1
// : adds etl::format_to probe.
// Pulls in parse_format_spec /
// vformat_to / per-type
// format_aligned_int /
// format_alternate_form —
// total ~50 KB delta over the
// no-format flavor, crosses
// bank-0 IO window. Requires
// multi-seg or --layer2 link.
// Phase 5.4 step 5 size-spike
// outcome: downgrade scope
// (FP-format / full format
// are layer2-opt-in, not the
// default).
//
// Marker layout (16-bit little-endian at $025xxx):
// $025010 = 0xBEEF main entered
// $025012 = etl::chrono::steady_clock duration rep is i32 sentinel (1/0)
// $025014 = string_stream emitted expected string (1/0)
// $025016 = etl::format_to emitted expected string (1/0; sentinel 1
// when format probe gated off)
// $025018 = pathJoin("USR", "BIN") => "USR:BIN" check (1/0)
// $02501A = pathNormalize("USR::BIN::..::LIB") => "USR:LIB" check (1/0)
// $02501C = pathSplit("USR:BIN:LS") => parent="USR:BIN" + leaf="LS" (1/0)
// $02501E = pathJoin rejects 65-char component (1 = correctly rejected)
// $025020 = pathNormalize rejects 9-deep path (1 = correctly rejected)
// $025000 = 0xC0DE reached end-of-main (sentinel for runInGno --check)
#include <stdint.h>
#include <iigs/path.h>
#include <sstream>
#include "etl/chrono.h"
#include "etl/string.h"
#include "etl/string_stream.h"
#include "etl/string_view.h"
#include "etl/to_string.h"
#ifdef CXX_STREAM_PROBE_WITH_FORMAT
#include "etl/format.h"
#endif
static uint16_t streq(const char *a, const char *b) {
while (*a && *b) {
if (*a != *b) {
return 0;
}
a++;
b++;
}
return (uint16_t)((*a == 0 && *b == 0) ? 1 : 0);
}
int main(void) {
*(volatile uint16_t *)0x025010UL = 0xBEEF;
// Compile-time contract: clock-rep stays i32 (etl_profile.h override).
// Avoids i64 chrono libcalls in stream + format demos.
static_assert(sizeof(etl::chrono::steady_clock::duration::rep) == 4,
"etl::chrono::steady_clock::rep must be i32 -- check "
"ETL_CHRONO_STEADY_CLOCK_DURATION in etl_profile.h");
*(volatile uint16_t *)0x025012UL = 1;
// ---- (1) etl::string_stream << int ------------------------------
// Flattened layout (no nested {}-scopes) — the bracketed-scope form
// tripped a W65816 Wide32->2xi16 lowering bug on three nested
// etl::string<32> stack allocations. Sequential single-string use
// works fine and is the documented cout-replacement idiom.
{
etl::string<32> streamBuf;
etl::string_stream ss(streamBuf);
ss << "x=" << 42;
{
etl::string<32> tmp;
etl::string_stream ssTmp(tmp);
ssTmp << "y=" << 7;
{
etl::string<32> third;
etl::string_stream ss3(third);
ss3 << "z=" << 3;
*(volatile uint16_t *)0x025014UL = streq(ss3.str().c_str(), "z=3");
}
}
}
// ---- (2) etl::format_to(buf, "{}", 42) --------------------------
#ifdef CXX_STREAM_PROBE_WITH_FORMAT
etl::string<32> formatBuf;
etl::format_to(formatBuf, "{}", 42);
*(volatile uint16_t *)0x025016UL = streq(formatBuf.c_str(), "42");
#else
// Sentinel: format probe gated off in single-bank flavor. See
// docs/GAP_CLOSURE_PLAN.md Phase 5.4 step 5 (size spike >10 KB
// delta -- explicit downgrade to layer2-opt-in).
*(volatile uint16_t *)0x025016UL = 1;
#endif
// ---- (3a) pathJoin -----------------------------------------------
char joinOut[64];
bool joinOk = iigs::path::pathJoin("USR", "BIN", joinOut, sizeof(joinOut));
*(volatile uint16_t *)0x025018UL =
(uint16_t)((joinOk && streq(joinOut, "USR:BIN")) ? 1 : 0);
// ---- (3b) pathNormalize collapsing & .. ---------------------------
char normOut[64];
bool normOk = iigs::path::pathNormalize("USR::BIN::..::LIB",
normOut, sizeof(normOut));
*(volatile uint16_t *)0x02501AUL =
(uint16_t)((normOk && streq(normOut, "USR:LIB")) ? 1 : 0);
// ---- (3c) pathSplit -----------------------------------------------
char splitParent[64];
char splitLeaf[64];
bool splitOk = iigs::path::pathSplit("USR:BIN:LS",
splitParent, sizeof(splitParent),
splitLeaf, sizeof(splitLeaf));
*(volatile uint16_t *)0x02501CUL =
(uint16_t)((splitOk && streq(splitParent, "USR:BIN") &&
streq(splitLeaf, "LS")) ? 1 : 0);
// ---- (3d) 65-char component rejection -----------------------------
char bigName[80];
for (uint16_t i = 0; i < 65; i++) {
bigName[i] = 'A';
}
bigName[65] = 0;
char bigOut[128];
bool bigRejected = !iigs::path::pathJoin("USR", bigName, bigOut, sizeof(bigOut));
*(volatile uint16_t *)0x02501EUL = (uint16_t)(bigRejected ? 1 : 0);
// ---- (3e) 9-deep path rejection -----------------------------------
char deepOut[128];
bool deepRejected = !iigs::path::pathNormalize(
"A:B:C:D:E:F:G:H:I", deepOut, sizeof(deepOut));
*(volatile uint16_t *)0x025020UL = (uint16_t)(deepRejected ? 1 : 0);
*(volatile uint16_t *)0x025000UL = 0xC0DE;
return 0;
}