diff --git a/crates/console/prometeu-hal/src/syscalls/domains/gfx.rs b/crates/console/prometeu-hal/src/syscalls/domains/gfx.rs index 280cdff4..bad18763 100644 --- a/crates/console/prometeu-hal/src/syscalls/domains/gfx.rs +++ b/crates/console/prometeu-hal/src/syscalls/domains/gfx.rs @@ -80,7 +80,7 @@ pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[ "set_sprite", 1, 10, - 0, + 1, caps::GFX, Determinism::Deterministic, false, 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 485ac069..160ae61d 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs @@ -10,6 +10,11 @@ use prometeu_hal::vm_fault::VmFault; use prometeu_hal::{HostContext, HostReturn, NativeInterface, SyscallId, expect_bool, expect_int}; impl VirtualMachineRuntime { + const GFX_STATUS_OK: i64 = 0; + const GFX_STATUS_ASSET_NOT_FOUND: i64 = 1; + const GFX_STATUS_INVALID_SPRITE_INDEX: i64 = 2; + const GFX_STATUS_INVALID_ARG_RANGE: i64 = 3; + fn syscall_log_write(&mut self, level_val: i64, tag: u16, msg: String) -> Result<(), VmFault> { let level = match level_val { 0 => LogLevel::Trace, @@ -147,21 +152,30 @@ impl NativeInterface for VirtualMachineRuntime { let flip_y = expect_bool(args, 8)?; let priority = expect_int(args, 9)? as u8; - let bank_id = - hw.assets().find_slot_by_name(&asset_name, BankType::TILES).unwrap_or(0); - - if index < 512 { - *hw.gfx_mut().sprite_mut(index) = Sprite { - tile: Tile { id: tile_id, flip_x: false, flip_y: false, palette_id }, - x, - y, - bank_id, - active, - flip_x, - flip_y, - priority, - }; + if index >= 512 { + ret.push_int(Self::GFX_STATUS_INVALID_SPRITE_INDEX); + return Ok(()); } + if palette_id >= 64 || priority >= 5 { + ret.push_int(Self::GFX_STATUS_INVALID_ARG_RANGE); + return Ok(()); + } + let Some(bank_id) = hw.assets().find_slot_by_name(&asset_name, BankType::TILES) else { + ret.push_int(Self::GFX_STATUS_ASSET_NOT_FOUND); + return Ok(()); + }; + + *hw.gfx_mut().sprite_mut(index) = Sprite { + tile: Tile { id: tile_id, flip_x: false, flip_y: false, palette_id }, + x, + y, + bank_id, + active, + flip_x, + flip_y, + priority, + }; + ret.push_int(Self::GFX_STATUS_OK); Ok(()) } Syscall::GfxDrawText => { diff --git a/docs/runtime/specs/04-gfx-peripheral.md b/docs/runtime/specs/04-gfx-peripheral.md index cb3cd734..072332e7 100644 --- a/docs/runtime/specs/04-gfx-peripheral.md +++ b/docs/runtime/specs/04-gfx-peripheral.md @@ -511,3 +511,32 @@ The system can measure: - `palettes_referenced_this_frame` - `tiles_drawn_by_palette_id` - `sprites_drawn_by_palette_id` + +--- + +## 19. Syscall Return and Fault Policy + +`gfx` follows status-first policy for operations with operational failure modes. + +Fault boundary: + +- `Trap`: structural ABI misuse (type/arity/capability/shape mismatch); +- `status`: operational failure; +- `Panic`: internal runtime invariant break only. + +### 19.1 `gfx.set_sprite` + +`gfx.set_sprite` returns `status:int`. + +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 silent no-op for invalid index/range; +- `palette_id` and `priority` must be validated against runtime-supported ranges.