neser 1.1.0

NESER - Nintendo Emulation Systems Engine (Rust). Desktop and WebAssembly frontends.
Documentation
<!DOCTYPE html>
<html>
<head><title>WASM Frame Benchmark</title></head>
<body>
<h2>WASM Frame Benchmark</h2>
<pre id="out">Loading WASM module...</pre>
<script type="module">
import init, { WasmNes } from "./pkg/neser.js";

const out = document.getElementById("out");
function log(msg) { out.textContent += "\n" + msg; }

async function run() {
    await init("./pkg/neser_bg.wasm");

    // Fetch ROM (require explicit ?rom= parameter, sanitize input)
    const SAFE_ROM_BASENAME = /^[A-Za-z0-9._-]+$/;
    const romName = new URLSearchParams(location.search).get("rom");
    if (!romName) throw new Error("Missing ?rom=<file>. Provide a ROM filename in web/roms/.");
    if (!SAFE_ROM_BASENAME.test(romName)) throw new Error("Invalid ROM name. Only letters, numbers, dot, underscore, or dash allowed.");
    const frames = parseInt(new URLSearchParams(location.search).get("frames") || "600", 10);
    log(`Fetching ROM: ${romName}`);
    const resp = await fetch(`roms/${encodeURIComponent(romName)}`);
    if (!resp.ok) throw new Error(`Failed to fetch ROM: ${resp.status}`);
    const romData = new Uint8Array(await resp.arrayBuffer());

    const nes = new WasmNes();
    nes.load_rom(romData, romName);
    log(`ROM loaded. Running ${frames} frames...`);

    // Warmup
    for (let i = 0; i < 60; i++) nes.render_frame();

    // Mark for Chrome DevTools profiling
    log("Starting timed run (look for 'bench' marker in DevTools Performance tab)...");
    performance.mark("bench-start");

    // Timed run
    const t0 = performance.now();
    for (let i = 0; i < frames; i++) nes.render_frame();
    const elapsed = performance.now() - t0;

    performance.mark("bench-end");
    performance.measure("bench", "bench-start", "bench-end");
    const perFrame = elapsed / frames;
    const fps = 1000 / perFrame;
    log(`Total: ${elapsed.toFixed(1)}ms`);
    log(`Per frame: ${perFrame.toFixed(3)}ms`);
    log(`FPS: ${fps.toFixed(1)}`);

    // Stability runs
    log("\nStability check (5 runs):");
    for (let r = 1; r <= 5; r++) {
        const t = performance.now();
        for (let i = 0; i < frames; i++) nes.render_frame();
        const e = performance.now() - t;
        log(`  Run ${r}: ${(e / frames).toFixed(3)}ms/frame`);
    }

    log("\nDone.");
}

run().catch(e => log("ERROR: " + e));
</script>
</body>
</html>