stress-test add cartridge

This commit is contained in:
bQUARKz 2026-02-21 00:02:30 +00:00
parent 52502830a3
commit ca40a7b939
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
6 changed files with 200 additions and 0 deletions

View File

@ -0,0 +1,8 @@
[package]
name = "pbxgen-stress"
version = "0.1.0"
edition = "2021"
[dependencies]
prometeu-bytecode = { path = "../../console/prometeu-bytecode" }
anyhow = "1"

View File

@ -0,0 +1,180 @@
use anyhow::Result;
use prometeu_bytecode::assembler::assemble;
use prometeu_bytecode::model::{BytecodeModule, ConstantPoolEntry, DebugInfo, Export, FunctionMeta};
use std::fs;
use std::path::PathBuf;
fn asm(s: &str) -> Vec<u8> { assemble(s).expect("assemble") }
pub fn generate() -> Result<()> {
let mut rom: Vec<u8> = Vec::new();
heavy_load(&mut rom);
//light_load(&mut rom);
let functions = vec![
FunctionMeta {
code_offset: 0,
code_len: rom.len() as u32,
param_slots: 0,
local_slots: 2,
return_slots: 0,
max_stack_slots: 16,
},
];
let module = BytecodeModule {
version: 0,
const_pool: vec![
ConstantPoolEntry::String("stress".into()),
ConstantPoolEntry::String("frame".into()),
],
functions,
code: rom,
debug_info: Some(DebugInfo {
pc_to_span: vec![],
function_names: vec![(0, "main".into())],
}),
exports: vec![Export { symbol: "main".into(), func_idx: 0 }],
};
let bytes = module.serialize();
let mut out_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
out_dir.pop(); // pbxgen-stress
out_dir.pop(); // tools
out_dir.pop(); // crates
out_dir.push("test-cartridges");
out_dir.push("stress-console");
fs::create_dir_all(&out_dir)?;
fs::write(out_dir.join("program.pbx"), bytes)?;
if !out_dir.join("assets.pa").exists() {
fs::write(out_dir.join("assets.pa"), &[] as &[u8])?;
}
if !out_dir.join("manifest.json").exists() {
fs::write(out_dir.join("manifest.json"), b"{\n \"magic\": \"PMTU\",\n \"cartridge_version\": 1,\n \"app_id\": 1,\n \"title\": \"Stress Console\",\n \"app_version\": \"0.1.0\",\n \"app_mode\": \"Game\",\n \"entrypoint\": \"main\"\n}\n")?;
}
Ok(())
}
#[allow(dead_code)]
fn heavy_load(mut rom: &mut Vec<u8>) {
// Single function 0: main
// Everything runs here — no coroutines, no SPAWN, no YIELD.
//
// Global 0 = t (frame counter)
// Local 0 = scratch
// Local 1 = loop counter for discs
//
// Loop:
// t = (t + 1)
// clear screen
// draw 100 discs using t for animation
// draw 20 texts using t for animation
// RET (runtime handles the frame loop)
// --- init locals ---
// local 0: scratch
// local 1: loop counter for discs
rom.extend(asm("PUSH_I32 0\nSET_LOCAL 0\nPUSH_I32 0\nSET_LOCAL 1"));
// --- t = (t + 1) ---
// t is global 0 to persist across prepare_call resets
rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 1\nADD\nSET_GLOBAL 0"));
// --- clear screen ---
rom.extend(asm("PUSH_I32 0\nSYSCALL 0x1010"));
// --- draw 500 discs ---
rom.extend(asm("PUSH_I32 0\nSET_LOCAL 1"));
let disc_loop_start = rom.len() as u32;
rom.extend(asm("GET_LOCAL 1\nPUSH_I32 500\nLT"));
let jif_disc_end_offset = rom.len() + 2;
rom.extend(asm("JMP_IF_FALSE 0"));
// x = (t * (i+7) + i * 13) % 320
rom.extend(asm("GET_GLOBAL 0\nGET_LOCAL 1\nPUSH_I32 7\nADD\nMUL\nGET_LOCAL 1\nPUSH_I32 13\nMUL\nADD\nPUSH_I32 320\nMOD"));
// y = (t * (i+11) + i * 17) % 180
rom.extend(asm("GET_GLOBAL 0\nGET_LOCAL 1\nPUSH_I32 11\nADD\nMUL\nGET_LOCAL 1\nPUSH_I32 17\nMUL\nADD\nPUSH_I32 180\nMOD"));
// r = ( (i*13) % 20 ) + 5
rom.extend(asm("GET_LOCAL 1\nPUSH_I32 13\nMUL\nPUSH_I32 20\nMOD\nPUSH_I32 5\nADD"));
// border color = (i * 1234) & 0xFFFF
rom.extend(asm("GET_LOCAL 1\nPUSH_I32 1234\nMUL\nPUSH_I32 65535\nBIT_AND"));
// fill color = (i * 5678 + t) & 0xFFFF
rom.extend(asm("GET_LOCAL 1\nPUSH_I32 5678\nMUL\nGET_GLOBAL 0\nADD\nPUSH_I32 65535\nBIT_AND"));
// SYSCALL GfxDrawDisc (x, y, r, border, fill)
rom.extend(asm("SYSCALL 0x1005"));
// i++
rom.extend(asm("GET_LOCAL 1\nPUSH_I32 1\nADD\nSET_LOCAL 1"));
let jmp_disc_loop_offset = rom.len() + 2;
rom.extend(asm("JMP 0"));
let disc_loop_end = rom.len() as u32;
// --- draw 20 texts ---
rom.extend(asm("PUSH_I32 0\nSET_LOCAL 1"));
let text_loop_start = rom.len() as u32;
rom.extend(asm("GET_LOCAL 1\nPUSH_I32 20\nLT"));
let jif_text_end_offset = rom.len() + 2;
rom.extend(asm("JMP_IF_FALSE 0"));
// x = (t * 3 + i * 40) % 320
rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 3\nMUL\nGET_LOCAL 1\nPUSH_I32 40\nMUL\nADD\nPUSH_I32 320\nMOD"));
// y = (i * 30 + t) % 180
rom.extend(asm("GET_LOCAL 1\nPUSH_I32 30\nMUL\nGET_GLOBAL 0\nADD\nPUSH_I32 180\nMOD"));
// string (toggle between "stress" and "frame")
rom.extend(asm("GET_LOCAL 1\nPUSH_I32 1\nBIT_AND"));
let jif_text_alt_offset = rom.len() + 2;
rom.extend(asm("JMP_IF_FALSE 0"));
rom.extend(asm("PUSH_CONST 0")); // "stress"
let jmp_text_join_offset = rom.len() + 2;
rom.extend(asm("JMP 0"));
let text_alt_target = rom.len() as u32;
rom.extend(asm("PUSH_CONST 1")); // "frame"
let text_join_target = rom.len() as u32;
// color = (t * 10 + i * 1000) & 0xFFFF
rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 10\nMUL\nGET_LOCAL 1\nPUSH_I32 1000\nMUL\nADD\nPUSH_I32 65535\nBIT_AND"));
// SYSCALL GfxDrawText (x, y, str, color)
rom.extend(asm("SYSCALL 0x1008"));
// i++
rom.extend(asm("GET_LOCAL 1\nPUSH_I32 1\nADD\nSET_LOCAL 1"));
let jmp_text_loop_offset = rom.len() + 2;
rom.extend(asm("JMP 0"));
let text_loop_end = rom.len() as u32;
// --- log every 60 frames ---
rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 60\nMOD\nPUSH_I32 0\nEQ"));
let jif_log_offset = rom.len() + 2;
rom.extend(asm("JMP_IF_FALSE 0"));
rom.extend(asm("PUSH_I32 2\nPUSH_CONST 0\nSYSCALL 0x5001"));
let after_log = rom.len() as u32;
// --- end of function ---
rom.extend(asm("FRAME_SYNC\nRET"));
// --- Patch jump targets ---
let patch = |buf: &mut Vec<u8>, imm_offset: usize, target: u32| {
buf[imm_offset..imm_offset + 4].copy_from_slice(&target.to_le_bytes());
};
patch(&mut rom, jif_disc_end_offset, disc_loop_end);
patch(&mut rom, jmp_disc_loop_offset, disc_loop_start);
patch(&mut rom, jif_text_end_offset, text_loop_end);
patch(&mut rom, jif_text_alt_offset, text_alt_target);
patch(&mut rom, jmp_text_join_offset, text_join_target);
patch(&mut rom, jmp_text_loop_offset, text_loop_start);
patch(&mut rom, jif_log_offset, after_log);
}
// fn light_load(rom: &mut Vec<u8>) {
// // Single function 0: main
// // Only paints Purple: 0x780F once.
// // The runtime handles calling this function repeatedly every tick.
//
// // --- clear screen (Purple 0x780F) ---
// rom.extend(asm("PUSH_I32 30735\nSYSCALL 0x1010\nFRAME_SYNC\nRET")); // 30735 is 0x780F
// }

View File

@ -0,0 +1,3 @@
use anyhow::Result;
fn main() -> Result<()> { pbxgen_stress::generate() }

View File

View File

@ -0,0 +1,9 @@
{
"magic": "PMTU",
"cartridge_version": 1,
"app_id": 1,
"title": "Stress Console",
"app_version": "0.1.0",
"app_mode": "Game",
"entrypoint": "main"
}

Binary file not shown.