implements PLN-0022
This commit is contained in:
parent
240fe65da7
commit
dd90ff812c
10
crates/console/prometeu-hal/src/composer_status.rs
Normal file
10
crates/console/prometeu-hal/src/composer_status.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum ComposerOpStatus {
|
||||||
|
Ok = 0,
|
||||||
|
SceneUnavailable = 1,
|
||||||
|
ArgRangeInvalid = 2,
|
||||||
|
BankInvalid = 3,
|
||||||
|
LayerInvalid = 4,
|
||||||
|
SpriteOverflow = 5,
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ pub mod button;
|
|||||||
pub mod cartridge;
|
pub mod cartridge;
|
||||||
pub mod cartridge_loader;
|
pub mod cartridge_loader;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
|
pub mod composer_status;
|
||||||
pub mod debugger_protocol;
|
pub mod debugger_protocol;
|
||||||
pub mod gfx_bridge;
|
pub mod gfx_bridge;
|
||||||
pub mod glyph;
|
pub mod glyph;
|
||||||
@ -34,6 +35,7 @@ pub mod window;
|
|||||||
|
|
||||||
pub use asset_bridge::AssetBridge;
|
pub use asset_bridge::AssetBridge;
|
||||||
pub use audio_bridge::{AudioBridge, AudioOpStatus, LoopMode};
|
pub use audio_bridge::{AudioBridge, AudioOpStatus, LoopMode};
|
||||||
|
pub use composer_status::ComposerOpStatus;
|
||||||
pub use gfx_bridge::{BlendMode, GfxBridge, GfxOpStatus};
|
pub use gfx_bridge::{BlendMode, GfxBridge, GfxOpStatus};
|
||||||
pub use hardware_bridge::HardwareBridge;
|
pub use hardware_bridge::HardwareBridge;
|
||||||
pub use host_context::{HostContext, HostContextProvider};
|
pub use host_context::{HostContext, HostContextProvider};
|
||||||
|
|||||||
@ -19,6 +19,7 @@ pub use resolver::{
|
|||||||
/// Each Syscall has a unique 32-bit ID. The IDs are grouped by category:
|
/// Each Syscall has a unique 32-bit ID. The IDs are grouped by category:
|
||||||
/// - **0x0xxx**: System & OS Control
|
/// - **0x0xxx**: System & OS Control
|
||||||
/// - **0x1xxx**: Graphics (GFX)
|
/// - **0x1xxx**: Graphics (GFX)
|
||||||
|
/// - **0x11xx**: Frame Composer orchestration
|
||||||
/// - **0x2xxx**: Reserved for legacy input syscalls (disabled for v1 VM-owned input)
|
/// - **0x2xxx**: Reserved for legacy input syscalls (disabled for v1 VM-owned input)
|
||||||
/// - **0x3xxx**: Audio (PCM & Mixing)
|
/// - **0x3xxx**: Audio (PCM & Mixing)
|
||||||
/// - **0x4xxx**: Filesystem (Sandboxed I/O)
|
/// - **0x4xxx**: Filesystem (Sandboxed I/O)
|
||||||
@ -35,9 +36,12 @@ pub enum Syscall {
|
|||||||
GfxDrawCircle = 0x1004,
|
GfxDrawCircle = 0x1004,
|
||||||
GfxDrawDisc = 0x1005,
|
GfxDrawDisc = 0x1005,
|
||||||
GfxDrawSquare = 0x1006,
|
GfxDrawSquare = 0x1006,
|
||||||
GfxSetSprite = 0x1007,
|
|
||||||
GfxDrawText = 0x1008,
|
GfxDrawText = 0x1008,
|
||||||
GfxClear565 = 0x1010,
|
GfxClear565 = 0x1010,
|
||||||
|
ComposerBindScene = 0x1101,
|
||||||
|
ComposerUnbindScene = 0x1102,
|
||||||
|
ComposerSetCamera = 0x1103,
|
||||||
|
ComposerEmitSprite = 0x1104,
|
||||||
AudioPlaySample = 0x3001,
|
AudioPlaySample = 0x3001,
|
||||||
AudioPlay = 0x3002,
|
AudioPlay = 0x3002,
|
||||||
FsOpen = 0x4001,
|
FsOpen = 0x4001,
|
||||||
|
|||||||
22
crates/console/prometeu-hal/src/syscalls/domains/composer.rs
Normal file
22
crates/console/prometeu-hal/src/syscalls/domains/composer.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use crate::syscalls::{Syscall, SyscallRegistryEntry, caps};
|
||||||
|
|
||||||
|
pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[
|
||||||
|
SyscallRegistryEntry::builder(Syscall::ComposerBindScene, "composer", "bind_scene")
|
||||||
|
.args(1)
|
||||||
|
.rets(1)
|
||||||
|
.caps(caps::GFX)
|
||||||
|
.cost(5),
|
||||||
|
SyscallRegistryEntry::builder(Syscall::ComposerUnbindScene, "composer", "unbind_scene")
|
||||||
|
.rets(1)
|
||||||
|
.caps(caps::GFX)
|
||||||
|
.cost(2),
|
||||||
|
SyscallRegistryEntry::builder(Syscall::ComposerSetCamera, "composer", "set_camera")
|
||||||
|
.args(2)
|
||||||
|
.caps(caps::GFX)
|
||||||
|
.cost(2),
|
||||||
|
SyscallRegistryEntry::builder(Syscall::ComposerEmitSprite, "composer", "emit_sprite")
|
||||||
|
.args(9)
|
||||||
|
.rets(1)
|
||||||
|
.caps(caps::GFX)
|
||||||
|
.cost(5),
|
||||||
|
];
|
||||||
@ -25,11 +25,6 @@ pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[
|
|||||||
.args(6)
|
.args(6)
|
||||||
.caps(caps::GFX)
|
.caps(caps::GFX)
|
||||||
.cost(5),
|
.cost(5),
|
||||||
SyscallRegistryEntry::builder(Syscall::GfxSetSprite, "gfx", "set_sprite")
|
|
||||||
.args(10)
|
|
||||||
.rets(1)
|
|
||||||
.caps(caps::GFX)
|
|
||||||
.cost(5),
|
|
||||||
SyscallRegistryEntry::builder(Syscall::GfxDrawText, "gfx", "draw_text")
|
SyscallRegistryEntry::builder(Syscall::GfxDrawText, "gfx", "draw_text")
|
||||||
.args(4)
|
.args(4)
|
||||||
.caps(caps::GFX)
|
.caps(caps::GFX)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
mod asset;
|
mod asset;
|
||||||
mod audio;
|
mod audio;
|
||||||
mod bank;
|
mod bank;
|
||||||
|
mod composer;
|
||||||
mod fs;
|
mod fs;
|
||||||
mod gfx;
|
mod gfx;
|
||||||
mod log;
|
mod log;
|
||||||
@ -12,6 +13,7 @@ pub(crate) fn all_entries() -> impl Iterator<Item = &'static SyscallRegistryEntr
|
|||||||
system::ENTRIES
|
system::ENTRIES
|
||||||
.iter()
|
.iter()
|
||||||
.chain(gfx::ENTRIES.iter())
|
.chain(gfx::ENTRIES.iter())
|
||||||
|
.chain(composer::ENTRIES.iter())
|
||||||
.chain(audio::ENTRIES.iter())
|
.chain(audio::ENTRIES.iter())
|
||||||
.chain(fs::ENTRIES.iter())
|
.chain(fs::ENTRIES.iter())
|
||||||
.chain(log::ENTRIES.iter())
|
.chain(log::ENTRIES.iter())
|
||||||
|
|||||||
@ -20,9 +20,12 @@ impl Syscall {
|
|||||||
0x1004 => Some(Self::GfxDrawCircle),
|
0x1004 => Some(Self::GfxDrawCircle),
|
||||||
0x1005 => Some(Self::GfxDrawDisc),
|
0x1005 => Some(Self::GfxDrawDisc),
|
||||||
0x1006 => Some(Self::GfxDrawSquare),
|
0x1006 => Some(Self::GfxDrawSquare),
|
||||||
0x1007 => Some(Self::GfxSetSprite),
|
|
||||||
0x1008 => Some(Self::GfxDrawText),
|
0x1008 => Some(Self::GfxDrawText),
|
||||||
0x1010 => Some(Self::GfxClear565),
|
0x1010 => Some(Self::GfxClear565),
|
||||||
|
0x1101 => Some(Self::ComposerBindScene),
|
||||||
|
0x1102 => Some(Self::ComposerUnbindScene),
|
||||||
|
0x1103 => Some(Self::ComposerSetCamera),
|
||||||
|
0x1104 => Some(Self::ComposerEmitSprite),
|
||||||
0x3001 => Some(Self::AudioPlaySample),
|
0x3001 => Some(Self::AudioPlaySample),
|
||||||
0x3002 => Some(Self::AudioPlay),
|
0x3002 => Some(Self::AudioPlay),
|
||||||
0x4001 => Some(Self::FsOpen),
|
0x4001 => Some(Self::FsOpen),
|
||||||
@ -68,9 +71,12 @@ impl Syscall {
|
|||||||
Self::GfxDrawCircle => "GfxDrawCircle",
|
Self::GfxDrawCircle => "GfxDrawCircle",
|
||||||
Self::GfxDrawDisc => "GfxDrawDisc",
|
Self::GfxDrawDisc => "GfxDrawDisc",
|
||||||
Self::GfxDrawSquare => "GfxDrawSquare",
|
Self::GfxDrawSquare => "GfxDrawSquare",
|
||||||
Self::GfxSetSprite => "GfxSetSprite",
|
|
||||||
Self::GfxDrawText => "GfxDrawText",
|
Self::GfxDrawText => "GfxDrawText",
|
||||||
Self::GfxClear565 => "GfxClear565",
|
Self::GfxClear565 => "GfxClear565",
|
||||||
|
Self::ComposerBindScene => "ComposerBindScene",
|
||||||
|
Self::ComposerUnbindScene => "ComposerUnbindScene",
|
||||||
|
Self::ComposerSetCamera => "ComposerSetCamera",
|
||||||
|
Self::ComposerEmitSprite => "ComposerEmitSprite",
|
||||||
Self::AudioPlaySample => "AudioPlaySample",
|
Self::AudioPlaySample => "AudioPlaySample",
|
||||||
Self::AudioPlay => "AudioPlay",
|
Self::AudioPlay => "AudioPlay",
|
||||||
Self::FsOpen => "FsOpen",
|
Self::FsOpen => "FsOpen",
|
||||||
|
|||||||
@ -194,10 +194,6 @@ fn status_first_syscall_signatures_are_pinned() {
|
|||||||
assert_eq!(draw_square.arg_slots, 6);
|
assert_eq!(draw_square.arg_slots, 6);
|
||||||
assert_eq!(draw_square.ret_slots, 0);
|
assert_eq!(draw_square.ret_slots, 0);
|
||||||
|
|
||||||
let set_sprite = meta_for(Syscall::GfxSetSprite);
|
|
||||||
assert_eq!(set_sprite.arg_slots, 10);
|
|
||||||
assert_eq!(set_sprite.ret_slots, 1);
|
|
||||||
|
|
||||||
let draw_text = meta_for(Syscall::GfxDrawText);
|
let draw_text = meta_for(Syscall::GfxDrawText);
|
||||||
assert_eq!(draw_text.arg_slots, 4);
|
assert_eq!(draw_text.arg_slots, 4);
|
||||||
assert_eq!(draw_text.ret_slots, 0);
|
assert_eq!(draw_text.ret_slots, 0);
|
||||||
@ -206,6 +202,22 @@ fn status_first_syscall_signatures_are_pinned() {
|
|||||||
assert_eq!(clear_565.arg_slots, 1);
|
assert_eq!(clear_565.arg_slots, 1);
|
||||||
assert_eq!(clear_565.ret_slots, 0);
|
assert_eq!(clear_565.ret_slots, 0);
|
||||||
|
|
||||||
|
let bind_scene = meta_for(Syscall::ComposerBindScene);
|
||||||
|
assert_eq!(bind_scene.arg_slots, 1);
|
||||||
|
assert_eq!(bind_scene.ret_slots, 1);
|
||||||
|
|
||||||
|
let unbind_scene = meta_for(Syscall::ComposerUnbindScene);
|
||||||
|
assert_eq!(unbind_scene.arg_slots, 0);
|
||||||
|
assert_eq!(unbind_scene.ret_slots, 1);
|
||||||
|
|
||||||
|
let set_camera = meta_for(Syscall::ComposerSetCamera);
|
||||||
|
assert_eq!(set_camera.arg_slots, 2);
|
||||||
|
assert_eq!(set_camera.ret_slots, 0);
|
||||||
|
|
||||||
|
let emit_sprite = meta_for(Syscall::ComposerEmitSprite);
|
||||||
|
assert_eq!(emit_sprite.arg_slots, 9);
|
||||||
|
assert_eq!(emit_sprite.ret_slots, 1);
|
||||||
|
|
||||||
let audio_play_sample = meta_for(Syscall::AudioPlaySample);
|
let audio_play_sample = meta_for(Syscall::AudioPlaySample);
|
||||||
assert_eq!(audio_play_sample.arg_slots, 5);
|
assert_eq!(audio_play_sample.arg_slots, 5);
|
||||||
assert_eq!(audio_play_sample.ret_slots, 1);
|
assert_eq!(audio_play_sample.ret_slots, 1);
|
||||||
@ -231,10 +243,10 @@ fn status_first_syscall_signatures_are_pinned() {
|
|||||||
fn declared_resolver_rejects_legacy_status_first_signatures() {
|
fn declared_resolver_rejects_legacy_status_first_signatures() {
|
||||||
let declared = vec![
|
let declared = vec![
|
||||||
prometeu_bytecode::SyscallDecl {
|
prometeu_bytecode::SyscallDecl {
|
||||||
module: "gfx".into(),
|
module: "composer".into(),
|
||||||
name: "set_sprite".into(),
|
name: "bind_scene".into(),
|
||||||
version: 1,
|
version: 1,
|
||||||
arg_slots: 10,
|
arg_slots: 1,
|
||||||
ret_slots: 0,
|
ret_slots: 0,
|
||||||
},
|
},
|
||||||
prometeu_bytecode::SyscallDecl {
|
prometeu_bytecode::SyscallDecl {
|
||||||
@ -306,10 +318,24 @@ fn declared_resolver_rejects_legacy_status_first_signatures() {
|
|||||||
fn declared_resolver_accepts_mixed_status_first_surface_as_a_single_module() {
|
fn declared_resolver_accepts_mixed_status_first_surface_as_a_single_module() {
|
||||||
let declared = vec![
|
let declared = vec![
|
||||||
prometeu_bytecode::SyscallDecl {
|
prometeu_bytecode::SyscallDecl {
|
||||||
module: "gfx".into(),
|
module: "composer".into(),
|
||||||
name: "set_sprite".into(),
|
name: "bind_scene".into(),
|
||||||
version: 1,
|
version: 1,
|
||||||
arg_slots: 10,
|
arg_slots: 1,
|
||||||
|
ret_slots: 1,
|
||||||
|
},
|
||||||
|
prometeu_bytecode::SyscallDecl {
|
||||||
|
module: "composer".into(),
|
||||||
|
name: "unbind_scene".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 0,
|
||||||
|
ret_slots: 1,
|
||||||
|
},
|
||||||
|
prometeu_bytecode::SyscallDecl {
|
||||||
|
module: "composer".into(),
|
||||||
|
name: "emit_sprite".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 9,
|
||||||
ret_slots: 1,
|
ret_slots: 1,
|
||||||
},
|
},
|
||||||
prometeu_bytecode::SyscallDecl {
|
prometeu_bytecode::SyscallDecl {
|
||||||
@ -342,8 +368,10 @@ fn declared_resolver_accepts_mixed_status_first_surface_as_a_single_module() {
|
|||||||
assert_eq!(resolved.len(), declared.len());
|
assert_eq!(resolved.len(), declared.len());
|
||||||
assert_eq!(resolved[0].meta.ret_slots, 1);
|
assert_eq!(resolved[0].meta.ret_slots, 1);
|
||||||
assert_eq!(resolved[1].meta.ret_slots, 1);
|
assert_eq!(resolved[1].meta.ret_slots, 1);
|
||||||
assert_eq!(resolved[2].meta.ret_slots, 2);
|
assert_eq!(resolved[2].meta.ret_slots, 1);
|
||||||
assert_eq!(resolved[3].meta.ret_slots, 1);
|
assert_eq!(resolved[3].meta.ret_slots, 1);
|
||||||
|
assert_eq!(resolved[4].meta.ret_slots, 2);
|
||||||
|
assert_eq!(resolved[5].meta.ret_slots, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -4,14 +4,11 @@ use prometeu_bytecode::{TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_TYPE, Value};
|
|||||||
use prometeu_hal::asset::{AssetId, AssetOpStatus, BankType, SlotRef};
|
use prometeu_hal::asset::{AssetId, AssetOpStatus, BankType, SlotRef};
|
||||||
use prometeu_hal::cartridge::AppMode;
|
use prometeu_hal::cartridge::AppMode;
|
||||||
use prometeu_hal::color::Color;
|
use prometeu_hal::color::Color;
|
||||||
use prometeu_hal::glyph::Glyph;
|
|
||||||
use prometeu_hal::log::{LogLevel, LogSource};
|
use prometeu_hal::log::{LogLevel, LogSource};
|
||||||
use prometeu_hal::sprite::Sprite;
|
|
||||||
use prometeu_hal::syscalls::Syscall;
|
use prometeu_hal::syscalls::Syscall;
|
||||||
use prometeu_hal::vm_fault::VmFault;
|
use prometeu_hal::vm_fault::VmFault;
|
||||||
use prometeu_hal::{
|
use prometeu_hal::{
|
||||||
AudioOpStatus, GfxOpStatus, HostContext, HostReturn, NativeInterface, SyscallId, expect_bool,
|
AudioOpStatus, HostContext, HostReturn, NativeInterface, SyscallId, expect_int,
|
||||||
expect_int,
|
|
||||||
};
|
};
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
@ -135,49 +132,6 @@ impl NativeInterface for VirtualMachineRuntime {
|
|||||||
hw.gfx_mut().draw_square(x, y, w, h, border_color, fill_color);
|
hw.gfx_mut().draw_square(x, y, w, h, border_color, fill_color);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Syscall::GfxSetSprite => {
|
|
||||||
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;
|
|
||||||
let glyph_id = expect_int(args, 4)? as u16;
|
|
||||||
let palette_id = expect_int(args, 5)? as u8;
|
|
||||||
let active = expect_bool(args, 6)?;
|
|
||||||
let flip_x = expect_bool(args, 7)?;
|
|
||||||
let flip_y = expect_bool(args, 8)?;
|
|
||||||
let priority = expect_int(args, 9)? as u8;
|
|
||||||
|
|
||||||
if index >= 512 {
|
|
||||||
ret.push_int(GfxOpStatus::InvalidSpriteIndex as i64);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if hw.assets().slot_info(SlotRef::gfx(bank_id as usize)).asset_id.is_none() {
|
|
||||||
ret.push_int(GfxOpStatus::BankInvalid as i64);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if palette_id >= 64 || priority >= 5 {
|
|
||||||
ret.push_int(GfxOpStatus::ArgRangeInvalid as i64);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if active {
|
|
||||||
hw.emit_sprite(Sprite {
|
|
||||||
glyph: Glyph { glyph_id, palette_id },
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
layer: 0,
|
|
||||||
bank_id,
|
|
||||||
active: false,
|
|
||||||
flip_x,
|
|
||||||
flip_y,
|
|
||||||
priority,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ret.push_int(GfxOpStatus::Ok as i64);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Syscall::GfxDrawText => {
|
Syscall::GfxDrawText => {
|
||||||
let x = expect_int(args, 0)? as i32;
|
let x = expect_int(args, 0)? as i32;
|
||||||
let y = expect_int(args, 1)? as i32;
|
let y = expect_int(args, 1)? as i32;
|
||||||
@ -194,6 +148,13 @@ impl NativeInterface for VirtualMachineRuntime {
|
|||||||
hw.gfx_mut().clear(Color::from_raw(color_val as u16));
|
hw.gfx_mut().clear(Color::from_raw(color_val as u16));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Syscall::ComposerBindScene
|
||||||
|
| Syscall::ComposerUnbindScene
|
||||||
|
| Syscall::ComposerSetCamera
|
||||||
|
| Syscall::ComposerEmitSprite => Err(VmFault::Trap(
|
||||||
|
TRAP_INVALID_SYSCALL,
|
||||||
|
"Composer syscall support is not implemented yet".into(),
|
||||||
|
)),
|
||||||
Syscall::AudioPlaySample => {
|
Syscall::AudioPlaySample => {
|
||||||
let sample_id_raw = expect_int(args, 0)?;
|
let sample_id_raw = expect_int(args, 0)?;
|
||||||
let voice_id_raw = expect_int(args, 1)?;
|
let voice_id_raw = expect_int(args, 1)?;
|
||||||
|
|||||||
@ -2567,11 +2567,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_status_first_syscall_results_count_mismatch_panic() {
|
fn test_status_first_syscall_results_count_mismatch_panic() {
|
||||||
// GfxSetSprite (0x1007) expects 1 result.
|
// ComposerBindScene (0x1101) expects 1 result.
|
||||||
let code = assemble(
|
let code = assemble("PUSH_I32 0\nSYSCALL 0x1101").expect("assemble");
|
||||||
"PUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nPUSH_I32 0\nSYSCALL 0x1007",
|
|
||||||
)
|
|
||||||
.expect("assemble");
|
|
||||||
|
|
||||||
struct BadNativeNoReturn;
|
struct BadNativeNoReturn;
|
||||||
impl NativeInterface for BadNativeNoReturn {
|
impl NativeInterface for BadNativeNoReturn {
|
||||||
@ -2921,10 +2918,24 @@ mod tests {
|
|||||||
fn test_loader_patching_accepts_status_first_signatures() {
|
fn test_loader_patching_accepts_status_first_signatures() {
|
||||||
let cases = vec![
|
let cases = vec![
|
||||||
SyscallDecl {
|
SyscallDecl {
|
||||||
module: "gfx".into(),
|
module: "composer".into(),
|
||||||
name: "set_sprite".into(),
|
name: "bind_scene".into(),
|
||||||
version: 1,
|
version: 1,
|
||||||
arg_slots: 10,
|
arg_slots: 1,
|
||||||
|
ret_slots: 1,
|
||||||
|
},
|
||||||
|
SyscallDecl {
|
||||||
|
module: "composer".into(),
|
||||||
|
name: "unbind_scene".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 0,
|
||||||
|
ret_slots: 1,
|
||||||
|
},
|
||||||
|
SyscallDecl {
|
||||||
|
module: "composer".into(),
|
||||||
|
name: "emit_sprite".into(),
|
||||||
|
version: 1,
|
||||||
|
arg_slots: 9,
|
||||||
ret_slots: 1,
|
ret_slots: 1,
|
||||||
},
|
},
|
||||||
SyscallDecl {
|
SyscallDecl {
|
||||||
@ -2977,10 +2988,10 @@ mod tests {
|
|||||||
fn test_loader_patching_rejects_legacy_status_first_ret_slots() {
|
fn test_loader_patching_rejects_legacy_status_first_ret_slots() {
|
||||||
let cases = vec![
|
let cases = vec![
|
||||||
SyscallDecl {
|
SyscallDecl {
|
||||||
module: "gfx".into(),
|
module: "composer".into(),
|
||||||
name: "set_sprite".into(),
|
name: "bind_scene".into(),
|
||||||
version: 1,
|
version: 1,
|
||||||
arg_slots: 10,
|
arg_slots: 1,
|
||||||
ret_slots: 0,
|
ret_slots: 0,
|
||||||
},
|
},
|
||||||
SyscallDecl {
|
SyscallDecl {
|
||||||
|
|||||||
@ -536,7 +536,12 @@ The system can measure:
|
|||||||
|
|
||||||
## 19. Syscall Return and Fault Policy
|
## 19. Syscall Return and Fault Policy
|
||||||
|
|
||||||
`gfx` follows status-first policy for operations with operational failure modes.
|
Graphics-related public ABI in v1 is split between:
|
||||||
|
|
||||||
|
- `gfx.*` for direct drawing/backend-oriented operations;
|
||||||
|
- `composer.*` for frame orchestration operations.
|
||||||
|
|
||||||
|
Only operations with real operational rejection paths return explicit status values.
|
||||||
|
|
||||||
Fault boundary:
|
Fault boundary:
|
||||||
|
|
||||||
@ -544,50 +549,50 @@ Fault boundary:
|
|||||||
- `status`: operational failure;
|
- `status`: operational failure;
|
||||||
- `Panic`: internal runtime invariant break only.
|
- `Panic`: internal runtime invariant break only.
|
||||||
|
|
||||||
### 19.1 `gfx.set_sprite`
|
### 19.1 Return-shape matrix in v1
|
||||||
|
|
||||||
Return-shape matrix in v1:
|
| Syscall | Return | Policy basis |
|
||||||
|
| ----------------------- | ------------- | --------------------------------------------------- |
|
||||||
|
| `gfx.clear` | `void` | no real operational failure path in v1 |
|
||||||
|
| `gfx.fill_rect` | `void` | no real operational failure path in v1 |
|
||||||
|
| `gfx.draw_line` | `void` | no real operational failure path in v1 |
|
||||||
|
| `gfx.draw_circle` | `void` | no real operational failure path in v1 |
|
||||||
|
| `gfx.draw_disc` | `void` | no real operational failure path in v1 |
|
||||||
|
| `gfx.draw_square` | `void` | no real operational failure path in v1 |
|
||||||
|
| `gfx.draw_text` | `void` | no real operational failure path in v1 |
|
||||||
|
| `gfx.clear_565` | `void` | no real operational failure path in v1 |
|
||||||
|
| `composer.bind_scene` | `status:int` | explicit orchestration-domain operational result |
|
||||||
|
| `composer.unbind_scene` | `status:int` | explicit orchestration-domain operational result |
|
||||||
|
| `composer.set_camera` | `void` | no real operational failure path in v1 |
|
||||||
|
| `composer.emit_sprite` | `status:int` | explicit orchestration-domain operational rejection |
|
||||||
|
|
||||||
| Syscall | Return | Policy basis |
|
### 19.2 `composer.emit_sprite`
|
||||||
| ------------------ | ------------- | ---------------------------------------------------- |
|
|
||||||
| `gfx.clear` | `void` | no real operational failure path in v1 |
|
|
||||||
| `gfx.fill_rect` | `void` | no real operational failure path in v1 |
|
|
||||||
| `gfx.draw_line` | `void` | no real operational failure path in v1 |
|
|
||||||
| `gfx.draw_circle` | `void` | no real operational failure path in v1 |
|
|
||||||
| `gfx.draw_disc` | `void` | no real operational failure path in v1 |
|
|
||||||
| `gfx.draw_square` | `void` | no real operational failure path in v1 |
|
|
||||||
| `gfx.set_sprite` | `status:int` | operational rejection must be explicit |
|
|
||||||
| `gfx.draw_text` | `void` | no real operational failure path in v1 |
|
|
||||||
| `gfx.clear_565` | `void` | no real operational failure path in v1 |
|
|
||||||
|
|
||||||
Only `gfx.set_sprite` is status-returning in v1.
|
`composer.emit_sprite` returns `status:int`.
|
||||||
All other `gfx` syscalls remain `void` unless a future domain revision introduces a real operational failure path.
|
|
||||||
|
|
||||||
### 19.2 `gfx.set_sprite`
|
|
||||||
|
|
||||||
`gfx.set_sprite` returns `status:int`.
|
|
||||||
|
|
||||||
ABI:
|
ABI:
|
||||||
1. `bank_id: int` — index of the tile bank
|
1. `glyph_id: int` — glyph index within the bank
|
||||||
2. `index: int` — sprite index (0..511)
|
2. `palette_id: int` — palette index
|
||||||
3. `x: int` — x coordinate
|
3. `x: int` — x coordinate
|
||||||
4. `y: int` — y coordinate
|
4. `y: int` — y coordinate
|
||||||
5. `tile_id: int` — tile index within the bank
|
5. `layer: int` — composition layer reference
|
||||||
6. `palette_id: int` — palette index (0..63)
|
6. `bank_id: int` — glyph bank index
|
||||||
7. `active: bool` — visibility toggle
|
7. `flip_x: bool` — horizontal flip
|
||||||
8. `flip_x: bool` — horizontal flip
|
8. `flip_y: bool` — vertical flip
|
||||||
9. `flip_y: bool` — vertical flip
|
9. `priority: int` — within-layer ordering priority
|
||||||
10. `priority: int` — layer priority (0..4)
|
|
||||||
|
|
||||||
Minimum status table:
|
Minimum status table:
|
||||||
|
|
||||||
- `0` = `OK`
|
- `0` = `OK`
|
||||||
- `2` = `INVALID_SPRITE_INDEX`
|
- `1` = `SCENE_UNAVAILABLE`
|
||||||
- `3` = `INVALID_ARG_RANGE`
|
- `2` = `INVALID_ARG_RANGE`
|
||||||
- `4` = `BANK_INVALID`
|
- `3` = `BANK_INVALID`
|
||||||
|
- `4` = `LAYER_INVALID`
|
||||||
|
- `5` = `SPRITE_OVERFLOW`
|
||||||
|
|
||||||
Operational notes:
|
Operational notes:
|
||||||
|
|
||||||
- no fallback to default bank when the sprite bank id cannot be resolved;
|
- the canonical public sprite contract is frame-emission based;
|
||||||
- no silent no-op for invalid index/range;
|
- no caller-provided sprite index exists in the v1 canonical ABI;
|
||||||
- `palette_id` and `priority` must be validated against runtime-supported ranges.
|
- no `active` flag exists in the v1 canonical ABI;
|
||||||
|
- overflow remains non-fatal and must not escalate to trap in v1.
|
||||||
|
|||||||
@ -39,6 +39,7 @@ Example:
|
|||||||
```
|
```
|
||||||
("gfx", "present", 1)
|
("gfx", "present", 1)
|
||||||
("audio", "play", 2)
|
("audio", "play", 2)
|
||||||
|
("composer", "emit_sprite", 1)
|
||||||
```
|
```
|
||||||
|
|
||||||
This identity is:
|
This identity is:
|
||||||
@ -198,6 +199,24 @@ For `asset.load`:
|
|||||||
- `slot` is the target slot index;
|
- `slot` is the target slot index;
|
||||||
- bank kind is resolved from `asset_table` by `asset_id`, not supplied by the caller.
|
- bank kind is resolved from `asset_table` by `asset_id`, not supplied by the caller.
|
||||||
|
|
||||||
|
### Composition surface (`composer`, v1)
|
||||||
|
|
||||||
|
The canonical frame-orchestration public ABI uses module `composer`.
|
||||||
|
|
||||||
|
Canonical operations in v1 are:
|
||||||
|
|
||||||
|
- `composer.bind_scene(bank_id) -> (status)`
|
||||||
|
- `composer.unbind_scene() -> (status)`
|
||||||
|
- `composer.set_camera(x, y) -> void`
|
||||||
|
- `composer.emit_sprite(glyph_id, palette_id, x, y, layer, bank_id, flip_x, flip_y, priority) -> (status)`
|
||||||
|
|
||||||
|
For mutating composer operations:
|
||||||
|
|
||||||
|
- `status` is a `ComposerOpStatus` value;
|
||||||
|
- `bind_scene`, `unbind_scene`, and `emit_sprite` are status-returning;
|
||||||
|
- `set_camera` remains `void` in v1;
|
||||||
|
- no caller-provided sprite index or `active` flag is part of the canonical contract.
|
||||||
|
|
||||||
## 7 Syscalls as Callable Entities (Not First-Class)
|
## 7 Syscalls as Callable Entities (Not First-Class)
|
||||||
|
|
||||||
Syscalls behave like call sites, not like first-class guest values.
|
Syscalls behave like call sites, not like first-class guest values.
|
||||||
|
|||||||
@ -85,6 +85,9 @@ Example:
|
|||||||
- `asset.load` currently resolves with `arg_slots = 2` and `ret_slots = 2`.
|
- `asset.load` currently resolves with `arg_slots = 2` and `ret_slots = 2`.
|
||||||
- The canonical stack contract is `asset_id, slot -> status, handle`.
|
- The canonical stack contract is `asset_id, slot -> status, handle`.
|
||||||
- Callers do not provide an explicit asset kind; the runtime derives it from `asset_table`.
|
- Callers do not provide an explicit asset kind; the runtime derives it from `asset_table`.
|
||||||
|
- `composer.bind_scene` resolves with `arg_slots = 1` and `ret_slots = 1`.
|
||||||
|
- The canonical stack contract is `bank_id -> status`.
|
||||||
|
- `composer.emit_sprite` resolves with `arg_slots = 9` and `ret_slots = 1`.
|
||||||
|
|
||||||
#### Canonical Intrinsic Registry Artifact
|
#### Canonical Intrinsic Registry Artifact
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user