implements PLN-0160
This commit is contained in:
parent
3df8227ebf
commit
d6afa6ae6b
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -1469,6 +1469,16 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pbxgen-dummy-boy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prometeu-bytecode",
|
||||
"prometeu-hal",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbxgen-stress"
|
||||
version = "0.1.0"
|
||||
|
||||
@ -8,6 +8,7 @@ members = [
|
||||
"crates/console/prometeu-vm",
|
||||
"crates/host/prometeu-host-desktop-winit",
|
||||
"crates/tools/prometeu-cli",
|
||||
"crates/tools/pbxgen-dummy-boy",
|
||||
"crates/tools/pbxgen-stress",
|
||||
"crates/dev/prometeu-test-support",
|
||||
"crates/dev/prometeu-layer-tests",
|
||||
|
||||
@ -315,4 +315,22 @@ mod tests {
|
||||
assert_eq!(library.diagnostics().len(), 1);
|
||||
assert_eq!(library.diagnostics()[0].reason, GameLibraryDiagnosticReason::RootNotDirectory);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repository_test_cartridges_include_stress_and_dummy_boy() {
|
||||
let games_root =
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../..").join("test-cartridges");
|
||||
|
||||
let library = discover_games_root(games_root);
|
||||
let entries = library.entries();
|
||||
|
||||
assert!(entries.iter().any(|entry| {
|
||||
entry.app_id == 1
|
||||
&& entry.title == "Stress Console"
|
||||
&& entry.path.ends_with("stress-console")
|
||||
}));
|
||||
assert!(entries.iter().any(|entry| {
|
||||
entry.app_id == 2 && entry.title == "Dummy Boy" && entry.path.ends_with("dummy-boy")
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
12
crates/tools/pbxgen-dummy-boy/Cargo.toml
Normal file
12
crates/tools/pbxgen-dummy-boy/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "pbxgen-dummy-boy"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
prometeu-bytecode = { path = "../../console/prometeu-bytecode" }
|
||||
prometeu-hal = { path = "../../console/prometeu-hal" }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
147
crates/tools/pbxgen-dummy-boy/src/lib.rs
Normal file
147
crates/tools/pbxgen-dummy-boy/src/lib.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use anyhow::Result;
|
||||
use prometeu_bytecode::assembler::assemble;
|
||||
use prometeu_bytecode::model::{BytecodeModule, DebugInfo, Export, FunctionMeta, SyscallDecl};
|
||||
use prometeu_hal::color::Color;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const APP_ID: u32 = 2;
|
||||
const TITLE: &str = "Dummy Boy";
|
||||
const APP_VERSION: &str = "0.1.0";
|
||||
|
||||
fn asm(s: &str) -> Vec<u8> {
|
||||
assemble(s).expect("assemble")
|
||||
}
|
||||
|
||||
pub fn generate() -> Result<()> {
|
||||
let out_dir = cartridge_dir();
|
||||
fs::create_dir_all(&out_dir)?;
|
||||
fs::write(out_dir.join("program.pbx"), build_program())?;
|
||||
fs::write(out_dir.join("manifest.json"), manifest_json())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_program() -> Vec<u8> {
|
||||
let mut rom = Vec::new();
|
||||
|
||||
rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 1\nADD\nSET_GLOBAL 0"));
|
||||
rom.extend(asm(&format!("PUSH_I64 {}\nHOSTCALL 0", Color::BLACK.raw())));
|
||||
|
||||
rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 3\nMOD\nPUSH_I32 0\nEQ"));
|
||||
let jump_to_green_case_offset = rom.len() + 2;
|
||||
rom.extend(asm("JMP_IF_FALSE 0"));
|
||||
rom.extend(asm(&format!("PUSH_I64 {}\nSET_LOCAL 0", Color::RED.raw())));
|
||||
let jump_to_draw_after_red_offset = rom.len() + 2;
|
||||
rom.extend(asm("JMP 0"));
|
||||
|
||||
let green_case = rom.len() as u32;
|
||||
rom.extend(asm("GET_GLOBAL 0\nPUSH_I32 3\nMOD\nPUSH_I32 1\nEQ"));
|
||||
let jump_to_blue_case_offset = rom.len() + 2;
|
||||
rom.extend(asm("JMP_IF_FALSE 0"));
|
||||
rom.extend(asm(&format!("PUSH_I64 {}\nSET_LOCAL 0", Color::GREEN.raw())));
|
||||
let jump_to_draw_after_green_offset = rom.len() + 2;
|
||||
rom.extend(asm("JMP 0"));
|
||||
|
||||
let blue_case = rom.len() as u32;
|
||||
rom.extend(asm(&format!("PUSH_I64 {}\nSET_LOCAL 0", Color::BLUE.raw())));
|
||||
|
||||
let draw = rom.len() as u32;
|
||||
rom.extend(asm(&format!(
|
||||
"PUSH_I32 180\n\
|
||||
PUSH_I32 75\n\
|
||||
PUSH_I32 120\n\
|
||||
PUSH_I32 120\n\
|
||||
PUSH_I64 {}\n\
|
||||
GET_LOCAL 0\n\
|
||||
HOSTCALL 1\n\
|
||||
FRAME_SYNC\n\
|
||||
RET",
|
||||
Color::WHITE.raw()
|
||||
)));
|
||||
|
||||
patch_jump(&mut rom, jump_to_green_case_offset, green_case);
|
||||
patch_jump(&mut rom, jump_to_draw_after_red_offset, draw);
|
||||
patch_jump(&mut rom, jump_to_blue_case_offset, blue_case);
|
||||
patch_jump(&mut rom, jump_to_draw_after_green_offset, draw);
|
||||
|
||||
BytecodeModule {
|
||||
version: 0,
|
||||
const_pool: vec![],
|
||||
functions: vec![FunctionMeta {
|
||||
code_offset: 0,
|
||||
code_len: rom.len() as u32,
|
||||
param_slots: 0,
|
||||
local_slots: 1,
|
||||
return_slots: 0,
|
||||
max_stack_slots: 8,
|
||||
}],
|
||||
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: vec![
|
||||
SyscallDecl {
|
||||
module: "gfx2d".into(),
|
||||
name: "clear".into(),
|
||||
version: 1,
|
||||
arg_slots: 1,
|
||||
ret_slots: 0,
|
||||
},
|
||||
SyscallDecl {
|
||||
module: "gfx2d".into(),
|
||||
name: "draw_square".into(),
|
||||
version: 1,
|
||||
arg_slots: 6,
|
||||
ret_slots: 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
.serialize()
|
||||
}
|
||||
|
||||
pub fn manifest_json() -> Vec<u8> {
|
||||
format!(
|
||||
"{{\n \"magic\": \"PMTU\",\n \"cartridge_version\": 1,\n \"app_id\": {APP_ID},\n \"title\": \"{TITLE}\",\n \"app_version\": \"{APP_VERSION}\",\n \"app_mode\": \"Game\",\n \"capabilities\": [\"gfx2d\"]\n}}\n"
|
||||
)
|
||||
.into_bytes()
|
||||
}
|
||||
|
||||
fn cartridge_dir() -> PathBuf {
|
||||
let mut out_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
out_dir.pop();
|
||||
out_dir.pop();
|
||||
out_dir.pop();
|
||||
out_dir.push("test-cartridges");
|
||||
out_dir.push("dummy-boy");
|
||||
out_dir
|
||||
}
|
||||
|
||||
fn patch_jump(buf: &mut [u8], imm_offset: usize, target: u32) {
|
||||
buf[imm_offset..imm_offset + 4].copy_from_slice(&target.to_le_bytes());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn generated_program_is_pbx_module_with_expected_syscalls() {
|
||||
let bytes = build_program();
|
||||
|
||||
assert!(bytes.starts_with(b"PBS\0"));
|
||||
assert!(bytes.len() > 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_declares_distinct_game_identity() {
|
||||
let manifest: serde_json::Value =
|
||||
serde_json::from_slice(&manifest_json()).expect("manifest should parse");
|
||||
|
||||
assert_eq!(manifest["app_id"], APP_ID);
|
||||
assert_eq!(manifest["title"], TITLE);
|
||||
assert_eq!(manifest["app_mode"], "Game");
|
||||
assert_eq!(manifest["capabilities"], serde_json::json!(["gfx2d"]));
|
||||
}
|
||||
}
|
||||
5
crates/tools/pbxgen-dummy-boy/src/main.rs
Normal file
5
crates/tools/pbxgen-dummy-boy/src/main.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use anyhow::Result;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
pbxgen_dummy_boy::generate()
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
{"type":"meta","next_id":{"DSC":44,"AGD":47,"DEC":40,"PLN":163,"LSN":53,"CLSN":1}}
|
||||
{"type":"discussion","id":"DSC-0043","status":"in_progress","ticket":"system-os-cartridge-switch-orchestrator","title":"SystemOS Cartridge Switch Orchestrator","created_at":"2026-07-03","updated_at":"2026-07-05","tags":["runtime","os","lifecycle","game","cartridge","architecture"],"agendas":[{"id":"AGD-0044","file":"AGD-0044-systemos-cartridge-switch-orchestrator.md","status":"accepted","created_at":"2026-07-03","updated_at":"2026-07-05"}],"decisions":[{"id":"DEC-0039","file":"DEC-0039-systemos-game-cartridge-switch-orchestrator.md","status":"accepted","created_at":"2026-07-05","updated_at":"2026-07-05","ref_agenda":"AGD-0044"}],"plans":[{"id":"PLN-0157","file":"PLN-0157-introduce-systemos-game-switch-target-contract.md","status":"done","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0158","file":"PLN-0158-terminate-resident-game-before-switching-cartridges.md","status":"done","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0159","file":"PLN-0159-recover-failed-game-switches-back-to-home.md","status":"done","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0160","file":"PLN-0160-add-dummy-boy-visual-switch-test-cartridge.md","status":"open","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0161","file":"PLN-0161-validate-stress-to-dummy-boy-game-switch-end-to-end.md","status":"open","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0162","file":"PLN-0162-specify-game-switch-contract-and-direct-boot-separation.md","status":"open","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0043","status":"in_progress","ticket":"system-os-cartridge-switch-orchestrator","title":"SystemOS Cartridge Switch Orchestrator","created_at":"2026-07-03","updated_at":"2026-07-05","tags":["runtime","os","lifecycle","game","cartridge","architecture"],"agendas":[{"id":"AGD-0044","file":"AGD-0044-systemos-cartridge-switch-orchestrator.md","status":"accepted","created_at":"2026-07-03","updated_at":"2026-07-05"}],"decisions":[{"id":"DEC-0039","file":"DEC-0039-systemos-game-cartridge-switch-orchestrator.md","status":"accepted","created_at":"2026-07-05","updated_at":"2026-07-05","ref_agenda":"AGD-0044"}],"plans":[{"id":"PLN-0157","file":"PLN-0157-introduce-systemos-game-switch-target-contract.md","status":"done","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0158","file":"PLN-0158-terminate-resident-game-before-switching-cartridges.md","status":"done","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0159","file":"PLN-0159-recover-failed-game-switches-back-to-home.md","status":"done","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0160","file":"PLN-0160-add-dummy-boy-visual-switch-test-cartridge.md","status":"done","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0161","file":"PLN-0161-validate-stress-to-dummy-boy-game-switch-end-to-end.md","status":"open","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]},{"id":"PLN-0162","file":"PLN-0162-specify-game-switch-contract-and-direct-boot-separation.md","status":"open","created_at":"2026-07-05","updated_at":"2026-07-05","ref_decisions":["DEC-0039"]}],"lessons":[]}
|
||||
{"type":"discussion","id":"DSC-0039","status":"abandoned","ticket":"render-pipeline-family-and-future-3d","title":"Render Pipeline Family and Future 3D","created_at":"2026-06-04","updated_at":"2026-06-04","tags":["gfx","renderer","runtime","architecture","pipeline"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-render-pipeline-family-and-future-3d.md","status":"abandoned","created_at":"2026-06-04","updated_at":"2026-06-04","_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."}
|
||||
{"type":"discussion","id":"DSC-0040","status":"done","ticket":"vm-render-parallel-execution-boundary","title":"VM and Render Parallel Execution Boundary","created_at":"2026-06-04","updated_at":"2026-06-06","tags":["runtime","renderer","vm","concurrency","architecture","perf"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0048","file":"discussion/lessons/DSC-0040-vm-render-parallel-execution-boundary/LSN-0048-render-workers-need-a-closed-packet-contract.md","status":"done","created_at":"2026-06-06","updated_at":"2026-06-06"}]}
|
||||
{"type":"discussion","id":"DSC-0041","status":"done","ticket":"foreground-stack-game-pause-shell-vm-backed","title":"Foreground Stack, Game Pause, and VM-Backed Shell Coexistence","created_at":"2026-06-05","updated_at":"2026-07-05","tags":["runtime","os","lifecycle","shell","game","vm","foreground","architecture"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0052","file":"discussion/lessons/DSC-0041-foreground-stack-game-pause-shell-vm-backed/LSN-0052-foreground-ownership-and-vm-session-ownership-are-separate.md","status":"done","created_at":"2026-07-05","updated_at":"2026-07-05"}]}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
id: PLN-0160
|
||||
ticket: system-os-cartridge-switch-orchestrator
|
||||
title: Add Dummy Boy Visual Switch Test Cartridge
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-07-05
|
||||
ref_decisions: [DEC-0039]
|
||||
tags: [runtime, os, lifecycle, game, cartridge, architecture]
|
||||
|
||||
11
test-cartridges/dummy-boy/README.md
Normal file
11
test-cartridges/dummy-boy/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Dummy Boy
|
||||
|
||||
Regenerate this cartridge with:
|
||||
|
||||
```sh
|
||||
cargo run -p pbxgen-dummy-boy
|
||||
```
|
||||
|
||||
Dummy Boy is a minimal visual Game cartridge used by runtime switch tests. It
|
||||
clears the frame and draws a centered square whose fill color cycles
|
||||
deterministically across frames.
|
||||
9
test-cartridges/dummy-boy/manifest.json
Normal file
9
test-cartridges/dummy-boy/manifest.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"magic": "PMTU",
|
||||
"cartridge_version": 1,
|
||||
"app_id": 2,
|
||||
"title": "Dummy Boy",
|
||||
"app_version": "0.1.0",
|
||||
"app_mode": "Game",
|
||||
"capabilities": ["gfx2d"]
|
||||
}
|
||||
BIN
test-cartridges/dummy-boy/program.pbx
Normal file
BIN
test-cartridges/dummy-boy/program.pbx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user