From 668a061964a49bf5a51ddd41036b3e411cb8d59d Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sat, 23 May 2026 19:19:28 +0100 Subject: [PATCH] implements PLN-0068 --- crates/console/prometeu-drivers/src/asset.rs | 6 +- crates/console/prometeu-drivers/src/gfx.rs | 63 ++++++----- crates/console/prometeu-hal/src/color.rs | 100 ++++++++++-------- crates/console/prometeu-hal/src/glyph_bank.rs | 19 ++-- crates/console/prometeu-hal/src/syscalls.rs | 1 - .../prometeu-hal/src/syscalls/domains/gfx.rs | 4 - .../prometeu-hal/src/syscalls/registry.rs | 2 - .../prometeu-hal/src/syscalls/tests.rs | 4 - .../src/services/vm_runtime/dispatch.rs | 33 +++--- .../prometeu-vm/src/virtual_machine.rs | 6 +- discussion/index.ndjson | 2 +- ...rgba8888-color-type-hal-and-abi-surface.md | 2 +- 12 files changed, 121 insertions(+), 121 deletions(-) diff --git a/crates/console/prometeu-drivers/src/asset.rs b/crates/console/prometeu-drivers/src/asset.rs index 6e67a941..8579038b 100644 --- a/crates/console/prometeu-drivers/src/asset.rs +++ b/crates/console/prometeu-drivers/src/asset.rs @@ -658,7 +658,7 @@ impl AssetManager { let offset = (p * 16 + c) * 2; let color_raw = u16::from_le_bytes([palette_data[offset], palette_data[offset + 1]]); - *slot = Color(color_raw); + *slot = Color::from_rgb565_lossy(color_raw); } } @@ -690,7 +690,7 @@ impl AssetManager { let offset = (p * 16 + c) * 2; let color_raw = u16::from_le_bytes([palette_data[offset], palette_data[offset + 1]]); - *slot = Color(color_raw); + *slot = Color::from_rgb565_lossy(color_raw); } } @@ -1313,7 +1313,7 @@ mod tests { AssetManager::decode_glyph_bank_from_buffer(&entry, &data).expect("glyph decode"); assert_eq!(bank.pixel_indices, vec![1, 0, 2, 3]); - assert_eq!(bank.palettes[0][0], Color(0x1234)); + assert_eq!(bank.palettes[0][0], Color::from_rgb565_lossy(0x1234)); } #[test] diff --git a/crates/console/prometeu-drivers/src/gfx.rs b/crates/console/prometeu-drivers/src/gfx.rs index 6fca0a2f..14313a16 100644 --- a/crates/console/prometeu-drivers/src/gfx.rs +++ b/crates/console/prometeu-drivers/src/gfx.rs @@ -381,7 +381,7 @@ impl Gfx { } pub fn clear(&mut self, color: Color) { - self.back.fill(color.0); + self.back.fill(color.to_rgb565_lossy()); } /// Rectangle with blend mode. @@ -406,7 +406,7 @@ impl Gfx { let x1 = (x + w).clamp(0, fw); let y1 = (y + h).clamp(0, fh); - let src = color.0; + let src = color.to_rgb565_lossy(); for yy in y0..y1 { let row = (yy as usize) * self.w; @@ -429,7 +429,7 @@ impl Gfx { return; } if x >= 0 && x < self.w as i32 && y >= 0 && y < self.h as i32 { - self.back[y as usize * self.w + x as usize] = color.0; + self.back[y as usize * self.w + x as usize] = color.to_rgb565_lossy(); } } @@ -582,7 +582,7 @@ impl Gfx { return; } for x in start..=end { - self.back[y as usize * self.w + x as usize] = color.0; + self.back[y as usize * self.w + x as usize] = color.to_rgb565_lossy(); } } @@ -600,7 +600,7 @@ impl Gfx { return; } for y in start..=end { - self.back[y as usize * self.w + x as usize] = color.0; + self.back[y as usize * self.w + x as usize] = color.to_rgb565_lossy(); } } @@ -657,7 +657,7 @@ impl Gfx { update: &ResolverUpdate, resolved_glyph_slots: &[usize; 4], ) { - self.back.fill(Color::BLACK.raw()); + self.back.fill(Color::BLACK.to_rgb565_lossy()); self.populate_layer_buckets(); for (layer_index, glyph_slot) in @@ -779,7 +779,7 @@ impl Gfx { } let color = tile.bank.resolve_color(tile.entry.palette_id, px_index); - target.back[world_y as usize * target.screen_w + world_x as usize] = color.raw(); + target.back[world_y as usize * target.screen_w + world_x as usize] = color.to_rgb565_lossy(); } } } @@ -834,7 +834,7 @@ impl Gfx { // 3. Resolve color via palette (from the tile inside the sprite) let color = bank.resolve_color(sprite.glyph.palette_id, px_index); - back[world_y as usize * screen_w + world_x as usize] = color.raw(); + back[world_y as usize * screen_w + world_x as usize] = color.to_rgb565_lossy(); } } } @@ -848,17 +848,17 @@ impl Gfx { let weight = level as u16; let inv_weight = 31 - weight; - let (fr, fg, fb) = Color::unpack_to_native(fade_color.0); + let (fr, fg, fb) = unpack_rgb565_native(fade_color.to_rgb565_lossy()); for px in back.iter_mut() { - let (sr, sg, sb) = Color::unpack_to_native(*px); + let (sr, sg, sb) = unpack_rgb565_native(*px); // Formula: (src * weight + fade * inv_weight) / 31 let r = ((sr as u16 * weight + fr as u16 * inv_weight) / 31) as u8; let g = ((sg as u16 * weight + fg as u16 * inv_weight) / 31) as u8; let b = ((sb as u16 * weight + fb as u16 * inv_weight) / 31) as u8; - *px = Color::pack_from_native(r, g, b); + *px = pack_rgb565_native(r, g, b); } } @@ -900,7 +900,7 @@ impl Gfx { } let glyph = glyph_for_char(c); - let raw = color.0; + let raw = color.to_rgb565_lossy(); let row_start = (-y).clamp(0, glyph_h) as usize; let row_end = (screen_h - y).clamp(0, glyph_h) as usize; @@ -932,49 +932,60 @@ fn blend_rgb565(dst: u16, src: u16, mode: BlendMode) -> u16 { BlendMode::None => src, BlendMode::Half => { - let (dr, dg, db) = Color::unpack_to_native(dst); - let (sr, sg, sb) = Color::unpack_to_native(src); + let (dr, dg, db) = unpack_rgb565_native(dst); + let (sr, sg, sb) = unpack_rgb565_native(src); let r = ((dr as u16 + sr as u16) >> 1) as u8; let g = ((dg as u16 + sg as u16) >> 1) as u8; let b = ((db as u16 + sb as u16) >> 1) as u8; - Color::pack_from_native(r, g, b) + pack_rgb565_native(r, g, b) } BlendMode::HalfPlus => { - let (dr, dg, db) = Color::unpack_to_native(dst); - let (sr, sg, sb) = Color::unpack_to_native(src); + let (dr, dg, db) = unpack_rgb565_native(dst); + let (sr, sg, sb) = unpack_rgb565_native(src); let r = (dr as u16 + ((sr as u16) >> 1)).min(31) as u8; let g = (dg as u16 + ((sg as u16) >> 1)).min(63) as u8; let b = (db as u16 + ((sb as u16) >> 1)).min(31) as u8; - Color::pack_from_native(r, g, b) + pack_rgb565_native(r, g, b) } BlendMode::HalfMinus => { - let (dr, dg, db) = Color::unpack_to_native(dst); - let (sr, sg, sb) = Color::unpack_to_native(src); + let (dr, dg, db) = unpack_rgb565_native(dst); + let (sr, sg, sb) = unpack_rgb565_native(src); let r = (dr as i16 - ((sr as i16) >> 1)).max(0) as u8; let g = (dg as i16 - ((sg as i16) >> 1)).max(0) as u8; let b = (db as i16 - ((sb as i16) >> 1)).max(0) as u8; - Color::pack_from_native(r, g, b) + pack_rgb565_native(r, g, b) } BlendMode::Full => { - let (dr, dg, db) = Color::unpack_to_native(dst); - let (sr, sg, sb) = Color::unpack_to_native(src); + let (dr, dg, db) = unpack_rgb565_native(dst); + let (sr, sg, sb) = unpack_rgb565_native(src); let r = (dr as u16 + sr as u16).min(31) as u8; let g = (dg as u16 + sg as u16).min(63) as u8; let b = (db as u16 + sb as u16).min(31) as u8; - Color::pack_from_native(r, g, b) + pack_rgb565_native(r, g, b) } } } +fn unpack_rgb565_native(px: u16) -> (u8, u8, u8) { + let r = ((px >> 11) & 0x1F) as u8; + let g = ((px >> 5) & 0x3F) as u8; + let b = (px & 0x1F) as u8; + (r, g, b) +} + +fn pack_rgb565_native(r: u8, g: u8, b: u8) -> u16 { + ((r as u16 & 0x1F) << 11) | ((g as u16 & 0x3F) << 5) | (b as u16 & 0x1F) +} + #[cfg(test)] mod tests { use super::*; @@ -1133,7 +1144,7 @@ mod tests { gfx.hud_fade_level = 31; gfx.render_scene_from_cache(&cache, &update, &[0, 0, 0, 0]); - assert_eq!(gfx.back[0], Color::RED.raw()); + assert_eq!(gfx.back[0], Color::RED.to_rgb565_lossy()); } #[test] @@ -1184,7 +1195,7 @@ mod tests { gfx.render_scene_from_cache(&cache, &update, &[0, 0, 0, 0]); - assert_eq!(gfx.back[0], Color::BLUE.raw()); + assert_eq!(gfx.back[0], Color::BLUE.to_rgb565_lossy()); } #[test] diff --git a/crates/console/prometeu-hal/src/color.rs b/crates/console/prometeu-hal/src/color.rs index d5b440bc..04f16c9d 100644 --- a/crates/console/prometeu-hal/src/color.rs +++ b/crates/console/prometeu-hal/src/color.rs @@ -1,75 +1,87 @@ -/// Represents a 16-bit color in the RGB565 format. -/// -/// The RGB565 format is a common high-color representation for embedded systems: -/// - **Red**: 5 bits (0..31) -/// - **Green**: 6 bits (0..63) -/// - **Blue**: 5 bits (0..31) -/// -/// Prometeu does not have a hardware alpha channel. Transparency is achieved -/// by using a specific color key (Magenta / 0xF81F) which the GFX engine -/// skips during rendering. +/// Represents a canonical RGBA8888 color in RGBA channel order. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct Color(pub u16); +pub struct Color(pub u32); impl Color { - pub const BLACK: Self = Self::rgb(0, 0, 0); // 0x0000 - pub const WHITE: Self = Self::rgb(255, 255, 255); // 0xFFFF - pub const RED: Self = Self::rgb(255, 0, 0); // 0xF800 - pub const GREEN: Self = Self::rgb(0, 255, 0); // 0x07E0 - pub const BLUE: Self = Self::rgb(0, 0, 255); // 0x001F - pub const YELLOW: Self = Self::rgb(255, 255, 0); // 0xFFE0 + pub const BLACK: Self = Self::rgb(0, 0, 0); + pub const WHITE: Self = Self::rgb(255, 255, 255); + pub const RED: Self = Self::rgb(255, 0, 0); + pub const GREEN: Self = Self::rgb(0, 255, 0); + pub const BLUE: Self = Self::rgb(0, 0, 255); + pub const YELLOW: Self = Self::rgb(255, 255, 0); pub const ORANGE: Self = Self::rgb(255, 128, 0); pub const INDIGO: Self = Self::rgb(75, 0, 130); pub const GRAY: Self = Self::rgb(128, 128, 128); - pub const CYAN: Self = Self::rgb(0, 255, 255); // 0x07FF - pub const MAGENTA: Self = Self::rgb(255, 0, 255); // 0xF81F - pub const COLOR_KEY: Self = Self::MAGENTA; - pub const TRANSPARENT: Self = Self::MAGENTA; + pub const CYAN: Self = Self::rgb(0, 255, 255); + pub const MAGENTA: Self = Self::rgb(255, 0, 255); + pub const TRANSPARENT: Self = Self::rgba(0, 0, 0, 0); + #[deprecated(note = "Use RGBA alpha transparency; COLOR_KEY is temporary migration residue.")] + pub const COLOR_KEY: Self = Self::TRANSPARENT; - /// Extracts channels in the native RGB565 range: - /// R: 0..31, G: 0..63, B: 0..31 + /// Extracts RGBA8888 channels from canonical RGBA raw order. #[inline(always)] - pub const fn unpack_to_native(px: u16) -> (u8, u8, u8) { - let r = ((px >> 11) & 0x1F) as u8; - let g = ((px >> 5) & 0x3F) as u8; - let b = (px & 0x1F) as u8; - (r, g, b) + pub const fn unpack_to_native(px: u32) -> (u8, u8, u8, u8) { + let r = ((px >> 24) & 0xFF) as u8; + let g = ((px >> 16) & 0xFF) as u8; + let b = ((px >> 8) & 0xFF) as u8; + let a = (px & 0xFF) as u8; + (r, g, b, a) } - /// Packs channels from the native RGB565 range into a pixel: - /// R: 0..31, G: 0..63, B: 0..31 + /// Packs RGBA8888 channels into canonical RGBA raw order. #[inline(always)] - pub const fn pack_from_native(r: u8, g: u8, b: u8) -> u16 { - ((r as u16 & 0x1F) << 11) | ((g as u16 & 0x3F) << 5) | (b as u16 & 0x1F) + pub const fn pack_from_native(r: u8, g: u8, b: u8, a: u8) -> u32 { + ((r as u32) << 24) | ((g as u32) << 16) | ((b as u32) << 8) | a as u32 } - /// Creates an RGB565 color from 8-bit components (0..255). + /// Creates an opaque RGBA8888 color from 8-bit RGB components. pub const fn rgb(r: u8, g: u8, b: u8) -> Self { - let r5 = (r as u16 >> 3) & 0x1F; - let g6 = (g as u16 >> 2) & 0x3F; - let b5 = (b as u16 >> 3) & 0x1F; + Self::rgba(r, g, b, 255) + } - Self((r5 << 11) | (g6 << 5) | b5) + pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self { + Self(Self::pack_from_native(r, g, b, a)) } pub const fn gray_scale(c: u8) -> Self { Self::rgb(c, c, c) } - pub const fn from_raw(raw: u16) -> Self { + pub const fn from_raw(raw: u32) -> Self { Self(raw) } - pub const fn raw(self) -> u16 { + pub const fn raw(self) -> u32 { self.0 } pub const fn hex(self) -> i32 { - let (r5, g6, b5) = Self::unpack_to_native(self.0); - let r8 = ((r5 as u32) << 3) | ((r5 as u32) >> 2); - let g8 = ((g6 as u32) << 2) | ((g6 as u32) >> 4); - let b8 = ((b5 as u32) << 3) | ((b5 as u32) >> 2); - let hex = r8 << 16 | g8 << 8 | b8; + let (r, g, b, _) = Self::unpack_to_native(self.0); + let hex = (r as u32) << 16 | (g as u32) << 8 | b as u32; hex as i32 } + + pub const fn alpha(self) -> u8 { + (self.0 & 0xFF) as u8 + } + + #[deprecated(note = "Temporary bridge until the renderer buffer migrates to RGBA8888.")] + pub const fn to_rgb565_lossy(self) -> u16 { + let (r, g, b, _) = Self::unpack_to_native(self.0); + let r5 = (r as u16 >> 3) & 0x1F; + let g6 = (g as u16 >> 2) & 0x3F; + let b5 = (b as u16 >> 3) & 0x1F; + (r5 << 11) | (g6 << 5) | b5 + } + + #[deprecated(note = "Temporary bridge until asset palettes migrate to RGBA8888.")] + pub const fn from_rgb565_lossy(raw: u16) -> Self { + let r5 = ((raw >> 11) & 0x1F) as u32; + let g6 = ((raw >> 5) & 0x3F) as u32; + let b5 = (raw & 0x1F) as u32; + let r = ((r5 << 3) | (r5 >> 2)) as u8; + let g = ((g6 << 2) | (g6 >> 4)) as u8; + let b = ((b5 << 3) | (b5 >> 2)) as u8; + Self::rgba(r, g, b, 255) + } } diff --git a/crates/console/prometeu-hal/src/glyph_bank.rs b/crates/console/prometeu-hal/src/glyph_bank.rs index aa38e700..fe92e131 100644 --- a/crates/console/prometeu-hal/src/glyph_bank.rs +++ b/crates/console/prometeu-hal/src/glyph_bank.rs @@ -32,9 +32,10 @@ pub struct GlyphBank { /// Decoded pixel data stored as one palette index per pixel. /// Serialized payloads are packed; runtime memory is expanded. - /// Index 0 is always reserved for transparency. + /// Palette indices are ordinary indices; transparency is resolved through + /// the RGBA alpha channel of the palette entry. pub pixel_indices: Vec, - /// Runtime-facing v1 palette table: 64 palettes of 16 RGB565 colors each. + /// Runtime-facing v1 palette table: 64 palettes of 16 RGBA8888 colors each. pub palettes: [[Color; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1], } @@ -45,7 +46,7 @@ impl GlyphBank { tile_size, width, height, - pixel_indices: vec![0; width * height], // Index 0 = Transparent + pixel_indices: vec![0; width * height], palettes: [[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1], } } @@ -65,22 +66,16 @@ impl GlyphBank { if pixel_x < self.width && pixel_y < self.height { self.pixel_indices[pixel_y * self.width + pixel_x] } else { - 0 // Default to transparent if out of bounds + 0 } } - /// Maps a 4-bit index to a real RGB565 Color using the specified palette. + /// Maps a 4-bit index to a real RGBA8888 Color using the specified palette. pub fn resolve_color(&self, palette_id: u8, pixel_index: u8) -> Color { - // Hardware Rule: Index 0 is always transparent. - // We use Magenta as the 'transparent' signal color during composition. - if pixel_index == 0 { - return Color::COLOR_KEY; - } - self.palettes .get(palette_id as usize) .and_then(|palette| palette.get(pixel_index as usize)) .copied() - .unwrap_or(Color::COLOR_KEY) + .unwrap_or(Color::TRANSPARENT) } } diff --git a/crates/console/prometeu-hal/src/syscalls.rs b/crates/console/prometeu-hal/src/syscalls.rs index d63173a1..4098ed40 100644 --- a/crates/console/prometeu-hal/src/syscalls.rs +++ b/crates/console/prometeu-hal/src/syscalls.rs @@ -37,7 +37,6 @@ pub enum Syscall { GfxDrawDisc = 0x1005, GfxDrawSquare = 0x1006, GfxDrawText = 0x1008, - GfxClear565 = 0x1010, ComposerBindScene = 0x1101, ComposerUnbindScene = 0x1102, ComposerSetCamera = 0x1103, diff --git a/crates/console/prometeu-hal/src/syscalls/domains/gfx.rs b/crates/console/prometeu-hal/src/syscalls/domains/gfx.rs index f6023957..d45bc619 100644 --- a/crates/console/prometeu-hal/src/syscalls/domains/gfx.rs +++ b/crates/console/prometeu-hal/src/syscalls/domains/gfx.rs @@ -29,8 +29,4 @@ pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[ .args(4) .caps(caps::GFX) .cost(20), - SyscallRegistryEntry::builder(Syscall::GfxClear565, "gfx", "clear_565") - .args(1) - .caps(caps::GFX) - .cost(20), ]; diff --git a/crates/console/prometeu-hal/src/syscalls/registry.rs b/crates/console/prometeu-hal/src/syscalls/registry.rs index 7b353b1a..5f005083 100644 --- a/crates/console/prometeu-hal/src/syscalls/registry.rs +++ b/crates/console/prometeu-hal/src/syscalls/registry.rs @@ -21,7 +21,6 @@ impl Syscall { 0x1005 => Some(Self::GfxDrawDisc), 0x1006 => Some(Self::GfxDrawSquare), 0x1008 => Some(Self::GfxDrawText), - 0x1010 => Some(Self::GfxClear565), 0x1101 => Some(Self::ComposerBindScene), 0x1102 => Some(Self::ComposerUnbindScene), 0x1103 => Some(Self::ComposerSetCamera), @@ -71,7 +70,6 @@ impl Syscall { Self::GfxDrawDisc => "GfxDrawDisc", Self::GfxDrawSquare => "GfxDrawSquare", Self::GfxDrawText => "GfxDrawText", - Self::GfxClear565 => "GfxClear565", Self::ComposerBindScene => "ComposerBindScene", Self::ComposerUnbindScene => "ComposerUnbindScene", Self::ComposerSetCamera => "ComposerSetCamera", diff --git a/crates/console/prometeu-hal/src/syscalls/tests.rs b/crates/console/prometeu-hal/src/syscalls/tests.rs index 362a6687..97677f16 100644 --- a/crates/console/prometeu-hal/src/syscalls/tests.rs +++ b/crates/console/prometeu-hal/src/syscalls/tests.rs @@ -210,10 +210,6 @@ fn status_first_syscall_signatures_are_pinned() { assert_eq!(draw_text.arg_slots, 4); assert_eq!(draw_text.ret_slots, 0); - let clear_565 = meta_for(Syscall::GfxClear565); - assert_eq!(clear_565.arg_slots, 1); - 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); diff --git a/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs b/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs index 6ddf9bb2..7501ced7 100644 --- a/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs @@ -95,8 +95,10 @@ impl VmRuntimeHost<'_> { Ok(()) } - pub(crate) fn get_color(&self, value: i64) -> Color { - Color::from_raw(value as u16) + pub(crate) fn get_color(&self, value: i64) -> Result { + let raw = u32::try_from(value) + .map_err(|_| VmFault::Trap(TRAP_OOB, "Color value out of bounds".into()))?; + Ok(Color::from_raw(raw)) } fn int_arg_to_usize_status(value: i64) -> Result { @@ -126,7 +128,6 @@ impl VmRuntimeHost<'_> { | Syscall::GfxDrawDisc | Syscall::GfxDrawSquare | Syscall::GfxDrawText - | Syscall::GfxClear565 | Syscall::ComposerBindScene | Syscall::ComposerUnbindScene | Syscall::ComposerSetCamera @@ -183,7 +184,7 @@ impl NativeInterface for VmRuntimeHost<'_> { Syscall::SystemHasCart => unreachable!(), Syscall::SystemRunCart => unreachable!(), Syscall::GfxClear => { - let color = self.get_color(expect_int(args, 0)?); + let color = self.get_color(expect_int(args, 0)?)?; hw.gfx_mut().clear(color); Ok(()) } @@ -192,7 +193,7 @@ impl NativeInterface for VmRuntimeHost<'_> { let y = expect_int(args, 1)? as i32; let w = expect_int(args, 2)? as i32; let h = expect_int(args, 3)? as i32; - let color = self.get_color(expect_int(args, 4)?); + let color = self.get_color(expect_int(args, 4)?)?; hw.gfx_mut().fill_rect(x, y, w, h, color); Ok(()) } @@ -201,7 +202,7 @@ impl NativeInterface for VmRuntimeHost<'_> { let y1 = expect_int(args, 1)? as i32; let x2 = expect_int(args, 2)? as i32; let y2 = expect_int(args, 3)? as i32; - let color = self.get_color(expect_int(args, 4)?); + let color = self.get_color(expect_int(args, 4)?)?; hw.gfx_mut().draw_line(x1, y1, x2, y2, color); Ok(()) } @@ -209,7 +210,7 @@ impl NativeInterface for VmRuntimeHost<'_> { let x = expect_int(args, 0)? as i32; let y = expect_int(args, 1)? as i32; let r = expect_int(args, 2)? as i32; - let color = self.get_color(expect_int(args, 3)?); + let color = self.get_color(expect_int(args, 3)?)?; hw.gfx_mut().draw_circle(x, y, r, color); Ok(()) } @@ -217,8 +218,8 @@ impl NativeInterface for VmRuntimeHost<'_> { let x = expect_int(args, 0)? as i32; let y = expect_int(args, 1)? as i32; let r = expect_int(args, 2)? as i32; - let border_color = self.get_color(expect_int(args, 3)?); - let fill_color = self.get_color(expect_int(args, 4)?); + let border_color = self.get_color(expect_int(args, 3)?)?; + let fill_color = self.get_color(expect_int(args, 4)?)?; hw.gfx_mut().draw_disc(x, y, r, border_color, fill_color); Ok(()) } @@ -227,8 +228,8 @@ impl NativeInterface for VmRuntimeHost<'_> { let y = expect_int(args, 1)? as i32; let w = expect_int(args, 2)? as i32; let h = expect_int(args, 3)? as i32; - let border_color = self.get_color(expect_int(args, 4)?); - let fill_color = self.get_color(expect_int(args, 5)?); + let border_color = self.get_color(expect_int(args, 4)?)?; + let fill_color = self.get_color(expect_int(args, 5)?)?; hw.gfx_mut().draw_square(x, y, w, h, border_color, fill_color); Ok(()) } @@ -236,18 +237,10 @@ impl NativeInterface for VmRuntimeHost<'_> { let x = expect_int(args, 0)? as i32; let y = expect_int(args, 1)? as i32; let msg = expect_string(args, 2, "message")?; - let color = self.get_color(expect_int(args, 3)?); + let color = self.get_color(expect_int(args, 3)?)?; hw.gfx_mut().draw_text(x, y, &msg, color); Ok(()) } - Syscall::GfxClear565 => { - let color_val = expect_int(args, 0)? as u32; - if color_val > 0xFFFF { - return Err(VmFault::Trap(TRAP_OOB, "Color value out of bounds".into())); - } - hw.gfx_mut().clear(Color::from_raw(color_val as u16)); - Ok(()) - } Syscall::ComposerBindScene => { let scene_bank_id = match Self::int_arg_to_usize_status(expect_int(args, 0)?) { Ok(id) => id, diff --git a/crates/console/prometeu-vm/src/virtual_machine.rs b/crates/console/prometeu-vm/src/virtual_machine.rs index 5fb65d93..09282fe9 100644 --- a/crates/console/prometeu-vm/src/virtual_machine.rs +++ b/crates/console/prometeu-vm/src/virtual_machine.rs @@ -2545,12 +2545,12 @@ mod tests { #[test] fn test_syscall_results_count_mismatch_panic() { - // GfxClear565 (0x1010) expects 0 results + // GfxClear (0x1001) expects 0 results let rom = vec![ 0x17, 0x00, // PushI32 0x00, 0x00, 0x00, 0x00, // value 0 0x70, 0x00, // Syscall + Reserved - 0x10, 0x10, 0x00, 0x00, // Syscall ID 0x1010 + 0x01, 0x10, 0x00, 0x00, // Syscall ID 0x1001 ]; struct BadNative; @@ -2562,7 +2562,7 @@ mod tests { ret: &mut HostReturn, _ctx: &mut HostContext, ) -> Result<(), VmFault> { - // Wrong: GfxClear565 is void but we push something + // Wrong: GfxClear is void but we push something ret.push_int(42); Ok(()) } diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 68d5cd88..15765a48 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -35,4 +35,4 @@ {"type":"discussion","id":"DSC-0032","status":"done","ticket":"system-os-lifecycle-process-task-contract","title":"Agenda - SystemOS Lifecycle, Process and Task Contract","created_at":"2026-05-14","updated_at":"2026-05-15","tags":["runtime","os","lifecycle","process","task","shell","firmware"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0041","file":"discussion/lessons/DSC-0032-system-os-lifecycle-process-task-contract/LSN-0041-systemos-lifecycle-authority.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]} {"type":"discussion","id":"DSC-0033","status":"done","ticket":"system-os-service-ownership-and-module-layout","title":"Agenda - SystemOS Service Ownership and Module Layout","created_at":"2026-05-14","updated_at":"2026-05-15","tags":["runtime","os","services","module-layout","vm","window-manager","logging"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0042","file":"discussion/lessons/DSC-0033-system-os-service-ownership-and-module-layout/LSN-0042-systemos-service-ownership-boundary.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]} {"type":"discussion","id":"DSC-0036","status":"done","ticket":"prometeu-hub-ui-direction","title":"Agenda - Prometeu Hub UI Direction","created_at":"2026-05-15","updated_at":"2026-05-22","tags":["hub","ui","shell","system-apps","lifecycle","design-system"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0045","file":"discussion/lessons/DSC-0036-prometeu-hub-ui-direction/LSN-0045-hub-ui-slices-should-prove-os-boundaries.md","status":"done","created_at":"2026-05-22","updated_at":"2026-05-22"}]} -{"type":"discussion","id":"DSC-0037","status":"in_progress","ticket":"rgba8888-framebuffer-and-pixel-format-direction","title":"Agenda - RGBA8888 Framebuffer and Pixel Format Direction","created_at":"2026-05-22","updated_at":"2026-05-23","tags":["gfx","framebuffer","rgb565","rgba8888","renderer","assets","host","backend"],"agendas":[{"id":"AGD-0037","file":"AGD-0037-rgba8888-framebuffer-and-pixel-format-direction.md","status":"accepted","created_at":"2026-05-22","updated_at":"2026-05-23"}],"decisions":[{"id":"DEC-0029","file":"DEC-0029-rgba8888-runtime-pixel-format-contract.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23","ref_agenda":"AGD-0037"}],"plans":[{"id":"PLN-0067","file":"PLN-0067-rgba8888-published-contracts-and-specs.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0068","file":"PLN-0068-rgba8888-color-type-hal-and-abi-surface.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0069","file":"PLN-0069-rgba8888-cpu-renderer-and-composition.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0070","file":"PLN-0070-rgba8888-asset-palettes-tooling-and-fixtures.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0071","file":"PLN-0071-rgba8888-host-presentation-path.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0072","file":"PLN-0072-rgba8888-residue-removal-and-end-to-end-validation.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0037","status":"in_progress","ticket":"rgba8888-framebuffer-and-pixel-format-direction","title":"Agenda - RGBA8888 Framebuffer and Pixel Format Direction","created_at":"2026-05-22","updated_at":"2026-05-23","tags":["gfx","framebuffer","rgb565","rgba8888","renderer","assets","host","backend"],"agendas":[{"id":"AGD-0037","file":"AGD-0037-rgba8888-framebuffer-and-pixel-format-direction.md","status":"accepted","created_at":"2026-05-22","updated_at":"2026-05-23"}],"decisions":[{"id":"DEC-0029","file":"DEC-0029-rgba8888-runtime-pixel-format-contract.md","status":"accepted","created_at":"2026-05-23","updated_at":"2026-05-23","ref_agenda":"AGD-0037"}],"plans":[{"id":"PLN-0067","file":"PLN-0067-rgba8888-published-contracts-and-specs.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0068","file":"PLN-0068-rgba8888-color-type-hal-and-abi-surface.md","status":"done","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0069","file":"PLN-0069-rgba8888-cpu-renderer-and-composition.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0070","file":"PLN-0070-rgba8888-asset-palettes-tooling-and-fixtures.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0071","file":"PLN-0071-rgba8888-host-presentation-path.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]},{"id":"PLN-0072","file":"PLN-0072-rgba8888-residue-removal-and-end-to-end-validation.md","status":"open","created_at":"2026-05-23","updated_at":"2026-05-23","ref_decisions":["DEC-0029"]}],"lessons":[]} diff --git a/discussion/workflow/plans/PLN-0068-rgba8888-color-type-hal-and-abi-surface.md b/discussion/workflow/plans/PLN-0068-rgba8888-color-type-hal-and-abi-surface.md index 7ddf5acf..9df110ab 100644 --- a/discussion/workflow/plans/PLN-0068-rgba8888-color-type-hal-and-abi-surface.md +++ b/discussion/workflow/plans/PLN-0068-rgba8888-color-type-hal-and-abi-surface.md @@ -2,7 +2,7 @@ id: PLN-0068 ticket: rgba8888-framebuffer-and-pixel-format-direction title: RGBA8888 Color Type HAL and ABI Surface -status: open +status: done created: 2026-05-23 ref_decisions: [DEC-0029] tags: [gfx, framebuffer, rgb565, rgba8888, renderer, assets, host, backend]