From 17878e481c7f957c6365535bcd6284cae8962a1d Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 16 Mar 2026 17:07:11 +0000 Subject: [PATCH] assets loaded by bank id instead of name --- crates/console/prometeu-drivers/src/asset.rs | 46 ------------------- .../console/prometeu-hal/src/asset_bridge.rs | 1 - .../src/virtual_machine_runtime/dispatch.rs | 12 +---- .../src/virtual_machine_runtime/tests.rs | 45 ++++++++---------- docs/runtime/specs/04-gfx-peripheral.md | 15 +++++- docs/runtime/specs/05-audio-peripheral.md | 12 ++++- 6 files changed, 44 insertions(+), 87 deletions(-) diff --git a/crates/console/prometeu-drivers/src/asset.rs b/crates/console/prometeu-drivers/src/asset.rs index 236c8a93..3401e1e4 100644 --- a/crates/console/prometeu-drivers/src/asset.rs +++ b/crates/console/prometeu-drivers/src/asset.rs @@ -169,9 +169,6 @@ impl AssetBridge for AssetManager { fn slot_info(&self, slot: SlotRef) -> SlotStats { self.slot_info(slot) } - fn find_slot_by_name(&self, asset_name: &str, kind: BankType) -> Option { - self.find_slot_by_name(asset_name, kind) - } fn shutdown(&self) { self.shutdown() } @@ -824,24 +821,6 @@ impl AssetManager { } } - pub fn find_slot_by_name(&self, asset_name: &str, kind: BankType) -> Option { - let asset_id = { - let name_to_id = self.name_to_id.read().unwrap(); - *name_to_id.get(asset_name)? - }; - - match kind { - BankType::TILES => { - let slots = self.gfx_slots.read().unwrap(); - slots.iter().position(|&s| s == Some(asset_id)).map(|p| p as u8) - } - BankType::SOUNDS => { - let slots = self.sound_slots.read().unwrap(); - slots.iter().position(|&s| s == Some(asset_id)).map(|p| p as u8) - } - } - } - pub fn shutdown(&self) { self.gfx_policy.clear(); self.sound_policy.clear(); @@ -1062,31 +1041,6 @@ mod tests { assert_eq!(am.slot_info(SlotRef::audio(5)).asset_id, Some(2)); } - #[test] - fn test_find_slot_by_name() { - let banks = Arc::new(MemoryBanks::new()); - let gfx_installer = Arc::clone(&banks) as Arc; - let sound_installer = Arc::clone(&banks) as Arc; - - let data = test_tile_asset_data(); - let mut asset_entry = test_tile_asset_entry("my_tiles", data.len()); - asset_entry.asset_id = 10; - - let preload = vec![PreloadEntry { asset_id: 10, slot: 3 }]; - - let am = AssetManager::new( - vec![], - AssetsPayloadSource::empty(), - gfx_installer, - sound_installer, - ); - am.initialize_for_cartridge(vec![asset_entry], preload, AssetsPayloadSource::from_bytes(data)); - - assert_eq!(am.find_slot_by_name("my_tiles", BankType::TILES), Some(3)); - assert_eq!(am.find_slot_by_name("unknown", BankType::TILES), None); - assert_eq!(am.find_slot_by_name("my_tiles", BankType::SOUNDS), None); - } - #[test] fn test_load_returns_asset_not_found() { let banks = Arc::new(MemoryBanks::new()); diff --git a/crates/console/prometeu-hal/src/asset_bridge.rs b/crates/console/prometeu-hal/src/asset_bridge.rs index 1ed7acaa..b94d5d56 100644 --- a/crates/console/prometeu-hal/src/asset_bridge.rs +++ b/crates/console/prometeu-hal/src/asset_bridge.rs @@ -18,6 +18,5 @@ pub trait AssetBridge { fn apply_commits(&self); fn bank_info(&self, kind: BankType) -> BankStats; fn slot_info(&self, slot: SlotRef) -> SlotStats; - fn find_slot_by_name(&self, asset_name: &str, kind: BankType) -> Option; fn shutdown(&self); } diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs index 7f0689ae..21d3203e 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs @@ -135,7 +135,7 @@ impl NativeInterface for VirtualMachineRuntime { Ok(()) } Syscall::GfxSetSprite => { - let asset_name = expect_string(args, 0, "asset_name")?; + let bank_id = expect_int(args, 0)? as u8; let index = expect_int(args, 1)? as usize; let x = expect_int(args, 2)? as i32; let y = expect_int(args, 3)? as i32; @@ -154,10 +154,6 @@ impl NativeInterface for VirtualMachineRuntime { ret.push_int(GfxOpStatus::ArgRangeInvalid as i64); return Ok(()); } - let Some(bank_id) = hw.assets().find_slot_by_name(&asset_name, BankType::TILES) else { - ret.push_int(GfxOpStatus::AssetNotFound as i64); - return Ok(()); - }; *hw.gfx_mut().sprite_mut(index) = Sprite { tile: Tile { id: tile_id, flip_x: false, flip_y: false, palette_id }, @@ -225,7 +221,7 @@ impl NativeInterface for VirtualMachineRuntime { Ok(()) } Syscall::AudioPlay => { - let asset_name = expect_string(args, 0, "asset_name")?; + let bank_id = expect_int(args, 0)? as u8; let sample_id_raw = expect_int(args, 1)?; let voice_id_raw = expect_int(args, 2)?; let volume_raw = expect_int(args, 3)?; @@ -252,10 +248,6 @@ impl NativeInterface for VirtualMachineRuntime { return Ok(()); } - let Some(bank_id) = hw.assets().find_slot_by_name(&asset_name, BankType::SOUNDS) else { - ret.push_int(AudioOpStatus::AssetNotFound as i64); - return Ok(()); - }; let status = hw.audio_mut().play( bank_id, sample_id_raw as u16, diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs index aaf9e4f0..d140e4c1 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs @@ -357,12 +357,11 @@ fn tick_gfx_set_sprite_operational_error_returns_status_not_crash() { let mut hardware = Hardware::new(); let signals = InputSignals::default(); let code = assemble( - "PUSH_CONST 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT", + "PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT", ) .expect("assemble"); - let program = serialized_single_function_module_with_consts( + let program = serialized_single_function_module( code, - vec![ConstantPoolEntry::String("missing_tile_bank".into())], vec![SyscallDecl { module: "gfx".into(), name: "set_sprite".into(), @@ -377,7 +376,7 @@ fn tick_gfx_set_sprite_operational_error_returns_status_not_crash() { let report = runtime.tick(&mut vm, &signals, &mut hardware); assert!(report.is_none(), "operational error must not crash"); assert!(vm.is_halted()); - assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(GfxOpStatus::AssetNotFound as i64)]); + assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(GfxOpStatus::Ok as i64)]); } #[test] @@ -387,12 +386,11 @@ fn tick_gfx_set_sprite_invalid_index_returns_status_not_crash() { let mut hardware = Hardware::new(); let signals = InputSignals::default(); let code = assemble( - "PUSH_CONST 0\nPUSH_I32 512\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT", + "PUSH_I32 0\nPUSH_I32 512\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT", ) .expect("assemble"); - let program = serialized_single_function_module_with_consts( + let program = serialized_single_function_module( code, - vec![ConstantPoolEntry::String("missing_tile_bank".into())], vec![SyscallDecl { module: "gfx".into(), name: "set_sprite".into(), @@ -420,12 +418,11 @@ fn tick_gfx_set_sprite_invalid_range_returns_status_not_crash() { let mut hardware = Hardware::new(); let signals = InputSignals::default(); let code = assemble( - "PUSH_CONST 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 64\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT", + "PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 64\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\nHALT", ) .expect("assemble"); - let program = serialized_single_function_module_with_consts( + let program = serialized_single_function_module( code, - vec![ConstantPoolEntry::String("missing_tile_bank".into())], vec![SyscallDecl { module: "gfx".into(), name: "set_sprite".into(), @@ -477,12 +474,11 @@ fn tick_audio_play_voice_invalid_returns_status_not_crash() { let mut hardware = Hardware::new(); let signals = InputSignals::default(); let code = assemble( - "PUSH_CONST 0\nPUSH_I32 0\nPUSH_I32 16\nPUSH_I32 255\nPUSH_I32 128\nPUSH_I32 1\nPUSH_I32 0\nHOSTCALL 0\nHALT", + "PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 16\nPUSH_I32 255\nPUSH_I32 128\nPUSH_I32 1\nPUSH_I32 0\nHOSTCALL 0\nHALT", ) .expect("assemble"); - let program = serialized_single_function_module_with_consts( + let program = serialized_single_function_module( code, - vec![ConstantPoolEntry::String("missing_sound_bank".into())], vec![SyscallDecl { module: "audio".into(), name: "play".into(), @@ -507,12 +503,11 @@ fn tick_audio_play_missing_asset_returns_status_not_crash() { let mut hardware = Hardware::new(); let signals = InputSignals::default(); let code = assemble( - "PUSH_CONST 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 255\nPUSH_I32 128\nPUSH_I32 1\nPUSH_I32 0\nHOSTCALL 0\nHALT", + "PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 255\nPUSH_I32 128\nPUSH_I32 1\nPUSH_I32 0\nHOSTCALL 0\nHALT", ) .expect("assemble"); - let program = serialized_single_function_module_with_consts( + let program = serialized_single_function_module( code, - vec![ConstantPoolEntry::String("missing_sound_bank".into())], vec![SyscallDecl { module: "audio".into(), name: "play".into(), @@ -527,7 +522,7 @@ fn tick_audio_play_missing_asset_returns_status_not_crash() { let report = runtime.tick(&mut vm, &signals, &mut hardware); assert!(report.is_none(), "missing audio asset must not crash"); assert!(vm.is_halted()); - assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AudioOpStatus::AssetNotFound as i64)]); + assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(AudioOpStatus::SampleNotFound as i64)]); } #[test] @@ -559,7 +554,7 @@ fn tick_audio_play_type_mismatch_surfaces_trap_not_panic() { match report { CrashReport::VmTrap { trap } => { assert_eq!(trap.code, TRAP_TYPE); - assert!(trap.message.contains("Expected string asset_name")); + assert!(trap.message.contains("Expected integer at index 0")); } other => panic!("expected VmTrap crash report, got {:?}", other), } @@ -889,17 +884,15 @@ fn tick_status_first_surface_smoke_across_gfx_audio_and_asset() { let mut hardware = Hardware::new(); let signals = InputSignals::default(); let code = assemble( - "PUSH_CONST 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\n\ - PUSH_CONST 1\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 255\nPUSH_I32 128\nPUSH_I32 1\nPUSH_I32 0\nHOSTCALL 1\n\ - PUSH_CONST 2\nPUSH_I32 0\nPUSH_I32 0\nHOSTCALL 2\n\ + "PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_BOOL 1\nPUSH_BOOL 0\nPUSH_BOOL 0\nPUSH_I32 0\nHOSTCALL 0\n\ + PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 255\nPUSH_I32 128\nPUSH_I32 1\nPUSH_I32 0\nHOSTCALL 1\n\ + PUSH_CONST 0\nPUSH_I32 0\nPUSH_I32 0\nHOSTCALL 2\n\ HALT" ) .expect("assemble"); let program = serialized_single_function_module_with_consts( code, vec![ - ConstantPoolEntry::String("missing_tile_bank".into()), - ConstantPoolEntry::String("missing_sound_bank".into()), ConstantPoolEntry::String("missing_asset".into()), ], vec![ @@ -937,8 +930,8 @@ fn tick_status_first_surface_smoke_across_gfx_audio_and_asset() { vec![ Value::Int64(0), Value::Int64(AssetLoadError::AssetNotFound as i64), - Value::Int64(AudioOpStatus::AssetNotFound as i64), - Value::Int64(GfxOpStatus::AssetNotFound as i64), + Value::Int64(AudioOpStatus::SampleNotFound as i64), + Value::Int64(GfxOpStatus::Ok as i64), ] ); } @@ -972,7 +965,7 @@ fn tick_gfx_set_sprite_type_mismatch_surfaces_trap_not_panic() { match report { CrashReport::VmTrap { trap } => { assert_eq!(trap.code, TRAP_TYPE); - assert!(trap.message.contains("Expected string asset_name")); + assert!(trap.message.contains("Expected integer at index 0")); } other => panic!("expected VmTrap crash report, got {:?}", other), } diff --git a/docs/runtime/specs/04-gfx-peripheral.md b/docs/runtime/specs/04-gfx-peripheral.md index babfa56e..12e1fd81 100644 --- a/docs/runtime/specs/04-gfx-peripheral.md +++ b/docs/runtime/specs/04-gfx-peripheral.md @@ -547,15 +547,26 @@ All other `gfx` syscalls remain `void` unless a future domain revision introduce `gfx.set_sprite` returns `status:int`. +ABI: +1. `bank_id: int` — index of the tile bank +2. `index: int` — sprite index (0..511) +3. `x: int` — x coordinate +4. `y: int` — y coordinate +5. `tile_id: int` — tile index within the bank +6. `palette_id: int` — palette index (0..63) +7. `active: bool` — visibility toggle +8. `flip_x: bool` — horizontal flip +9. `flip_y: bool` — vertical flip +10. `priority: int` — layer priority (0..4) + Minimum status table: - `0` = `OK` -- `1` = `ASSET_NOT_FOUND` - `2` = `INVALID_SPRITE_INDEX` - `3` = `INVALID_ARG_RANGE` Operational notes: -- no fallback to default bank when the sprite asset name cannot be resolved; +- no fallback to default bank when the sprite bank id cannot be resolved; - no silent no-op for invalid index/range; - `palette_id` and `priority` must be validated against runtime-supported ranges. diff --git a/docs/runtime/specs/05-audio-peripheral.md b/docs/runtime/specs/05-audio-peripheral.md index ab95dc80..cd459fec 100644 --- a/docs/runtime/specs/05-audio-peripheral.md +++ b/docs/runtime/specs/05-audio-peripheral.md @@ -192,6 +192,15 @@ In the current MVP: - `audio.play` returns `status:int`; - `audio.play_sample` returns `status:int`. +ABI `audio.play`: +1. `bank_id: int` — index of the sound bank +2. `sample_id: int` — index of the sample within the bank +3. `voice_id: int` — index of the voice to use (0..15) +4. `volume: int` — volume level (0..255) +5. `pan: int` — panning (0..255) +6. `pitch: float` — playback rate +7. `loop_mode: int` — `0` for Off, `1` for On + Return-shape matrix in v1 syscall surface: | Syscall | Return | Policy basis | @@ -205,12 +214,11 @@ Return-shape matrix in v1 syscall surface: - `1` = `VOICE_INVALID` - `2` = `SAMPLE_NOT_FOUND` - `3` = `ARG_RANGE_INVALID` -- `4` = `ASSET_NOT_FOUND` - `5` = `NO_EFFECT` Operational rules: -- no fallback to default bank when an asset cannot be resolved; +- no fallback to default bank when a bank id cannot be resolved; - no silent no-op for invalid `voice_id`; - invalid `voice_id` must return `VOICE_INVALID`, not `ARG_RANGE_INVALID`; - invalid numeric ranges (e.g. `volume`, `pan`, `pitch`) must return explicit status.