bQUARKz 035e9d5676
All checks were successful
Intrepid/Prometeu/Runtime/pipeline/head This commit looks good
dev/ajustments-asset-entry (#12)
Reviewed-on: #12
Co-authored-by: bQUARKz <bquarkz@gmail.com>
Co-committed-by: bQUARKz <bquarkz@gmail.com>
2026-04-10 05:31:57 +00:00

218 lines
7.4 KiB
Rust

use anyhow::Result;
use prometeu_bytecode::assembler::assemble;
use prometeu_bytecode::model::{
BytecodeModule, ConstantPoolEntry, DebugInfo, Export, FunctionMeta, SyscallDecl,
};
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();
let syscalls = vec![
SyscallDecl {
module: "gfx".into(),
name: "clear_565".into(),
version: 1,
arg_slots: 1,
ret_slots: 0,
},
SyscallDecl {
module: "gfx".into(),
name: "draw_disc".into(),
version: 1,
arg_slots: 5,
ret_slots: 0,
},
SyscallDecl {
module: "gfx".into(),
name: "draw_text".into(),
version: 1,
arg_slots: 4,
ret_slots: 0,
},
SyscallDecl {
module: "log".into(),
name: "write".into(),
version: 1,
arg_slots: 2,
ret_slots: 0,
},
SyscallDecl {
module: "gfx".into(),
name: "set_sprite".into(),
version: 1,
arg_slots: 10,
ret_slots: 1,
},
];
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()),
ConstantPoolEntry::String("missing_glyph_bank".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 }],
syscalls,
};
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)?;
let assets_pa_path = out_dir.join("assets.pa");
if assets_pa_path.exists() {
fs::remove_file(&assets_pa_path)?;
}
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 \"capabilities\": [\"gfx\", \"log\"]\n}\n")?;
Ok(())
}
#[allow(dead_code)]
fn heavy_load(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 500 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\nHOSTCALL 0"));
// --- call status-first syscall path once per frame and drop status ---
rom.extend(asm(
"PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 4\nPOP_N 1",
));
// --- 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"));
// HOSTCALL gfx.draw_disc (x, y, r, border, fill)
rom.extend(asm("HOSTCALL 1"));
// 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\nPUSH_I32 0\nNEQ"));
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"));
// HOSTCALL gfx.draw_text (x, y, str, color)
rom.extend(asm("HOSTCALL 2"));
// 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\nHOSTCALL 3"));
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(rom, jif_disc_end_offset, disc_loop_end);
patch(rom, jmp_disc_loop_offset, disc_loop_start);
patch(rom, jif_text_end_offset, text_loop_end);
patch(rom, jif_text_alt_offset, text_alt_target);
patch(rom, jmp_text_join_offset, text_join_target);
patch(rom, jmp_text_loop_offset, text_loop_start);
patch(rom, jif_log_offset, after_log);
}