pr9.4
This commit is contained in:
parent
1a782cbb5c
commit
a199857280
@ -98,6 +98,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::inherent_to_string)]
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Value::Int32(i) => i.to_string(),
|
Value::Int32(i) => i.to_string(),
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
#![allow(clippy::collapsible_if)]
|
||||||
use crate::memory_banks::{SoundBankPoolInstaller, TileBankPoolInstaller};
|
use crate::memory_banks::{SoundBankPoolInstaller, TileBankPoolInstaller};
|
||||||
use prometeu_hal::AssetBridge;
|
use prometeu_hal::AssetBridge;
|
||||||
use prometeu_hal::asset::{
|
use prometeu_hal::asset::{
|
||||||
@ -486,12 +487,12 @@ impl AssetManager {
|
|||||||
let palette_data = &buffer[pixel_data_size..pixel_data_size + 2048];
|
let palette_data = &buffer[pixel_data_size..pixel_data_size + 2048];
|
||||||
|
|
||||||
let mut palettes = [[Color::BLACK; 16]; 64];
|
let mut palettes = [[Color::BLACK; 16]; 64];
|
||||||
for p in 0..64 {
|
for (p, pal) in palettes.iter_mut().enumerate() {
|
||||||
for c in 0..16 {
|
for (c, slot) in pal.iter_mut().enumerate() {
|
||||||
let offset = (p * 16 + c) * 2;
|
let offset = (p * 16 + c) * 2;
|
||||||
let color_raw =
|
let color_raw =
|
||||||
u16::from_le_bytes([palette_data[offset], palette_data[offset + 1]]);
|
u16::from_le_bytes([palette_data[offset], palette_data[offset + 1]]);
|
||||||
palettes[p][c] = Color(color_raw);
|
*slot = Color(color_raw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,7 +688,7 @@ impl AssetManager {
|
|||||||
match slot.asset_type {
|
match slot.asset_type {
|
||||||
BankType::TILES => {
|
BankType::TILES => {
|
||||||
let slots = self.gfx_slots.read().unwrap();
|
let slots = self.gfx_slots.read().unwrap();
|
||||||
let asset_id = slots.get(slot.index).and_then(|s| s.clone());
|
let asset_id = slots.get(slot.index).and_then(|s| *s);
|
||||||
|
|
||||||
let (bytes, asset_name) = if let Some(id) = &asset_id {
|
let (bytes, asset_name) = if let Some(id) = &asset_id {
|
||||||
let bytes = self
|
let bytes = self
|
||||||
@ -708,7 +709,7 @@ impl AssetManager {
|
|||||||
}
|
}
|
||||||
BankType::SOUNDS => {
|
BankType::SOUNDS => {
|
||||||
let slots = self.sound_slots.read().unwrap();
|
let slots = self.sound_slots.read().unwrap();
|
||||||
let asset_id = slots.get(slot.index).and_then(|s| s.clone());
|
let asset_id = slots.get(slot.index).and_then(|s| *s);
|
||||||
|
|
||||||
let (bytes, asset_name) = if let Some(id) = &asset_id {
|
let (bytes, asset_name) = if let Some(id) = &asset_id {
|
||||||
let bytes = self
|
let bytes = self
|
||||||
|
|||||||
@ -8,6 +8,7 @@ pub enum LoopMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait AudioBridge {
|
pub trait AudioBridge {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn play(
|
fn play(
|
||||||
&mut self,
|
&mut self,
|
||||||
bank_id: u8,
|
bank_id: u8,
|
||||||
@ -19,6 +20,7 @@ pub trait AudioBridge {
|
|||||||
priority: u8,
|
priority: u8,
|
||||||
loop_mode: LoopMode,
|
loop_mode: LoopMode,
|
||||||
);
|
);
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn play_sample(
|
fn play_sample(
|
||||||
&mut self,
|
&mut self,
|
||||||
sample: Arc<Sample>,
|
sample: Arc<Sample>,
|
||||||
|
|||||||
@ -27,7 +27,7 @@ pub struct Button {
|
|||||||
impl Button {
|
impl Button {
|
||||||
pub fn begin_frame(&mut self, is_down_now: bool) {
|
pub fn begin_frame(&mut self, is_down_now: bool) {
|
||||||
let was_down = self.down;
|
let was_down = self.down;
|
||||||
self.down = is_down_now.clone();
|
self.down = is_down_now;
|
||||||
|
|
||||||
self.pressed = !was_down && self.down;
|
self.pressed = !was_down && self.down;
|
||||||
self.released = was_down && !self.down;
|
self.released = was_down && !self.down;
|
||||||
|
|||||||
@ -50,55 +50,55 @@ impl Certifier {
|
|||||||
|
|
||||||
let mut violations = 0;
|
let mut violations = 0;
|
||||||
|
|
||||||
if let Some(budget) = self.config.cycles_budget_per_frame {
|
if let Some(budget) = self.config.cycles_budget_per_frame
|
||||||
if telemetry.cycles_used > budget {
|
&& telemetry.cycles_used > budget
|
||||||
log_service.log(
|
{
|
||||||
ts_ms,
|
log_service.log(
|
||||||
telemetry.frame_index,
|
ts_ms,
|
||||||
LogLevel::Warn,
|
telemetry.frame_index,
|
||||||
LogSource::Pos,
|
LogLevel::Warn,
|
||||||
0xCA01,
|
LogSource::Pos,
|
||||||
format!(
|
0xCA01,
|
||||||
"Cert: cycles_used exceeded budget ({} > {})",
|
format!(
|
||||||
telemetry.cycles_used, budget
|
"Cert: cycles_used exceeded budget ({} > {})",
|
||||||
),
|
telemetry.cycles_used, budget
|
||||||
);
|
),
|
||||||
violations += 1;
|
);
|
||||||
}
|
violations += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(limit) = self.config.max_syscalls_per_frame {
|
if let Some(limit) = self.config.max_syscalls_per_frame
|
||||||
if telemetry.syscalls > limit {
|
&& telemetry.syscalls > limit
|
||||||
log_service.log(
|
{
|
||||||
ts_ms,
|
log_service.log(
|
||||||
telemetry.frame_index,
|
ts_ms,
|
||||||
LogLevel::Warn,
|
telemetry.frame_index,
|
||||||
LogSource::Pos,
|
LogLevel::Warn,
|
||||||
0xCA02,
|
LogSource::Pos,
|
||||||
format!(
|
0xCA02,
|
||||||
"Cert: syscalls per frame exceeded limit ({} > {})",
|
format!(
|
||||||
telemetry.syscalls, limit
|
"Cert: syscalls per frame exceeded limit ({} > {})",
|
||||||
),
|
telemetry.syscalls, limit
|
||||||
);
|
),
|
||||||
violations += 1;
|
);
|
||||||
}
|
violations += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(limit) = self.config.max_host_cpu_us_per_frame {
|
if let Some(limit) = self.config.max_host_cpu_us_per_frame
|
||||||
if telemetry.host_cpu_time_us > limit {
|
&& telemetry.host_cpu_time_us > limit
|
||||||
log_service.log(
|
{
|
||||||
ts_ms,
|
log_service.log(
|
||||||
telemetry.frame_index,
|
ts_ms,
|
||||||
LogLevel::Warn,
|
telemetry.frame_index,
|
||||||
LogSource::Pos,
|
LogLevel::Warn,
|
||||||
0xCA03,
|
LogSource::Pos,
|
||||||
format!(
|
0xCA03,
|
||||||
"Cert: host_cpu_time_us exceeded limit ({} > {})",
|
format!(
|
||||||
telemetry.host_cpu_time_us, limit
|
"Cert: host_cpu_time_us exceeded limit ({} > {})",
|
||||||
),
|
telemetry.host_cpu_time_us, limit
|
||||||
);
|
),
|
||||||
violations += 1;
|
);
|
||||||
}
|
violations += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
violations
|
violations
|
||||||
|
|||||||
@ -54,6 +54,7 @@ impl Heap {
|
|||||||
|
|
||||||
/// Allocate a new object with the given kind and raw payload bytes.
|
/// Allocate a new object with the given kind and raw payload bytes.
|
||||||
/// Returns an opaque `HeapRef` handle.
|
/// Returns an opaque `HeapRef` handle.
|
||||||
|
#[cfg(test)]
|
||||||
pub fn allocate_object(&mut self, kind: ObjectKind, payload: &[u8]) -> HeapRef {
|
pub fn allocate_object(&mut self, kind: ObjectKind, payload: &[u8]) -> HeapRef {
|
||||||
let header = ObjectHeader::new(kind, payload.len() as u32);
|
let header = ObjectHeader::new(kind, payload.len() as u32);
|
||||||
let obj = StoredObject { header, payload: payload.to_vec(), array_elems: None, closure_env: None, coroutine: None };
|
let obj = StoredObject { header, payload: payload.to_vec(), array_elems: None, closure_env: None, coroutine: None };
|
||||||
@ -65,6 +66,7 @@ impl Heap {
|
|||||||
|
|
||||||
/// Allocate a new `Array` object with the given `Value` elements.
|
/// Allocate a new `Array` object with the given `Value` elements.
|
||||||
/// `payload_len` stores the element count; raw `payload` bytes are empty.
|
/// `payload_len` stores the element count; raw `payload` bytes are empty.
|
||||||
|
#[cfg(test)]
|
||||||
pub fn allocate_array(&mut self, elements: Vec<Value>) -> HeapRef {
|
pub fn allocate_array(&mut self, elements: Vec<Value>) -> HeapRef {
|
||||||
let header = ObjectHeader::new(ObjectKind::Array, elements.len() as u32);
|
let header = ObjectHeader::new(ObjectKind::Array, elements.len() as u32);
|
||||||
let obj = StoredObject { header, payload: Vec::new(), array_elems: Some(elements), closure_env: None, coroutine: None };
|
let obj = StoredObject { header, payload: Vec::new(), array_elems: Some(elements), closure_env: None, coroutine: None };
|
||||||
@ -128,6 +130,7 @@ impl Heap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a shared reference to the coroutine data for the given handle, if it is a Coroutine.
|
/// Returns a shared reference to the coroutine data for the given handle, if it is a Coroutine.
|
||||||
|
#[cfg(test)]
|
||||||
pub fn coroutine_data(&self, r: HeapRef) -> Option<&CoroutineData> {
|
pub fn coroutine_data(&self, r: HeapRef) -> Option<&CoroutineData> {
|
||||||
let idx = r.0 as usize;
|
let idx = r.0 as usize;
|
||||||
self.objects.get(idx).and_then(|slot| slot.as_ref()).and_then(|obj| obj.coroutine.as_ref())
|
self.objects.get(idx).and_then(|slot| slot.as_ref()).and_then(|obj| obj.coroutine.as_ref())
|
||||||
@ -158,8 +161,8 @@ impl Heap {
|
|||||||
.map(|o| &mut o.header)
|
.map(|o| &mut o.header)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal: list inner `HeapRef` children of an object without allocating.
|
// Internal: list inner `HeapRef` children of an object without allocating.
|
||||||
/// Note: GC mark no longer uses this helper; kept for potential diagnostics.
|
// Note: GC mark no longer uses this helper; kept for potential diagnostics.
|
||||||
// fn children_of(&self, r: HeapRef) -> Box<dyn Iterator<Item = HeapRef> + '_> {
|
// fn children_of(&self, r: HeapRef) -> Box<dyn Iterator<Item = HeapRef> + '_> {
|
||||||
// let idx = r.0 as usize;
|
// let idx = r.0 as usize;
|
||||||
// if let Some(Some(o)) = self.objects.get(idx) {
|
// if let Some(Some(o)) = self.objects.get(idx) {
|
||||||
@ -221,6 +224,7 @@ impl Heap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the captured environment slice of a closure. Returns None if kind mismatch or invalid ref.
|
/// Get the captured environment slice of a closure. Returns None if kind mismatch or invalid ref.
|
||||||
|
#[cfg(test)]
|
||||||
pub fn closure_env_slice(&self, r: HeapRef) -> Option<&[Value]> {
|
pub fn closure_env_slice(&self, r: HeapRef) -> Option<&[Value]> {
|
||||||
let idx = r.0 as usize;
|
let idx = r.0 as usize;
|
||||||
let slot = self.objects.get(idx)?.as_ref()?;
|
let slot = self.objects.get(idx)?.as_ref()?;
|
||||||
@ -261,14 +265,14 @@ impl Heap {
|
|||||||
ObjectKind::Array => {
|
ObjectKind::Array => {
|
||||||
if let Some(elems) = obj.array_elems.as_ref() {
|
if let Some(elems) = obj.array_elems.as_ref() {
|
||||||
for val in elems.iter() {
|
for val in elems.iter() {
|
||||||
if let Value::HeapRef(child) = val {
|
if let Value::HeapRef(child) = val
|
||||||
if self.is_valid(*child) {
|
&& self.is_valid(*child)
|
||||||
let marked = self
|
{
|
||||||
.header(*child)
|
let marked = self
|
||||||
.map(|h: &ObjectHeader| h.is_marked())
|
.header(*child)
|
||||||
.unwrap_or(false);
|
.map(|h: &ObjectHeader| h.is_marked())
|
||||||
if !marked { stack.push(*child); }
|
.unwrap_or(false);
|
||||||
}
|
if !marked { stack.push(*child); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,14 +285,14 @@ impl Heap {
|
|||||||
if let Some(env) = obj.closure_env.as_ref() {
|
if let Some(env) = obj.closure_env.as_ref() {
|
||||||
debug_assert_eq!(env.len(), env_len, "closure env len must match encoded env_len");
|
debug_assert_eq!(env.len(), env_len, "closure env len must match encoded env_len");
|
||||||
for val in env[..env_len].iter() {
|
for val in env[..env_len].iter() {
|
||||||
if let Value::HeapRef(child) = val {
|
if let Value::HeapRef(child) = val
|
||||||
if self.is_valid(*child) {
|
&& self.is_valid(*child)
|
||||||
let marked = self
|
{
|
||||||
.header(*child)
|
let marked = self
|
||||||
.map(|h: &ObjectHeader| h.is_marked())
|
.header(*child)
|
||||||
.unwrap_or(false);
|
.map(|h: &ObjectHeader| h.is_marked())
|
||||||
if !marked { stack.push(*child); }
|
.unwrap_or(false);
|
||||||
}
|
if !marked { stack.push(*child); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,14 +300,14 @@ impl Heap {
|
|||||||
ObjectKind::Coroutine => {
|
ObjectKind::Coroutine => {
|
||||||
if let Some(co) = obj.coroutine.as_ref() {
|
if let Some(co) = obj.coroutine.as_ref() {
|
||||||
for val in co.stack.iter() {
|
for val in co.stack.iter() {
|
||||||
if let Value::HeapRef(child) = val {
|
if let Value::HeapRef(child) = val
|
||||||
if self.is_valid(*child) {
|
&& self.is_valid(*child)
|
||||||
let marked = self
|
{
|
||||||
.header(*child)
|
let marked = self
|
||||||
.map(|h: &ObjectHeader| h.is_marked())
|
.header(*child)
|
||||||
.unwrap_or(false);
|
.map(|h: &ObjectHeader| h.is_marked())
|
||||||
if !marked { stack.push(*child); }
|
.unwrap_or(false);
|
||||||
}
|
if !marked { stack.push(*child); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -333,7 +337,6 @@ impl Heap {
|
|||||||
|
|
||||||
/// Current number of allocated (live) objects.
|
/// Current number of allocated (live) objects.
|
||||||
pub fn len(&self) -> usize { self.objects.iter().filter(|s| s.is_some()).count() }
|
pub fn len(&self) -> usize { self.objects.iter().filter(|s| s.is_some()).count() }
|
||||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
|
||||||
|
|
||||||
/// Enumerate handles of coroutines that are currently suspended (i.e., not running):
|
/// Enumerate handles of coroutines that are currently suspended (i.e., not running):
|
||||||
/// Ready or Sleeping. These must be treated as GC roots by the runtime so their
|
/// Ready or Sleeping. These must be treated as GC roots by the runtime so their
|
||||||
@ -341,14 +344,12 @@ impl Heap {
|
|||||||
pub fn suspended_coroutine_handles(&self) -> Vec<HeapRef> {
|
pub fn suspended_coroutine_handles(&self) -> Vec<HeapRef> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for (idx, slot) in self.objects.iter().enumerate() {
|
for (idx, slot) in self.objects.iter().enumerate() {
|
||||||
if let Some(obj) = slot {
|
if let Some(obj) = slot
|
||||||
if obj.header.kind == ObjectKind::Coroutine {
|
&& obj.header.kind == ObjectKind::Coroutine
|
||||||
if let Some(co) = &obj.coroutine {
|
&& let Some(co) = &obj.coroutine
|
||||||
if matches!(co.state, CoroutineState::Ready | CoroutineState::Sleeping) {
|
&& matches!(co.state, CoroutineState::Ready | CoroutineState::Sleeping)
|
||||||
out.push(HeapRef(idx as u32));
|
{
|
||||||
}
|
out.push(HeapRef(idx as u32));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
|
|||||||
@ -51,12 +51,15 @@
|
|||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub enum ObjectKind {
|
pub enum ObjectKind {
|
||||||
/// Reserved/unknown kind. Should not appear in valid allocations.
|
/// Reserved/unknown kind. Should not appear in valid allocations.
|
||||||
|
#[allow(dead_code)] // Kept for stable tag layout and persisted images, even if not constructed in this crate yet
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
|
|
||||||
/// UTF-8 string. `payload_len` is the number of bytes.
|
/// UTF-8 string. `payload_len` is the number of bytes.
|
||||||
|
#[allow(dead_code)] // Public/stable tag retained; construction may live in higher layers
|
||||||
String = 1,
|
String = 1,
|
||||||
|
|
||||||
/// Homogeneous array of VM values/handles. `payload_len` is element count.
|
/// Homogeneous array of VM values/handles. `payload_len` is element count.
|
||||||
|
#[allow(dead_code)] // Public/stable tag retained; constructed via Heap helpers in tests
|
||||||
Array = 2,
|
Array = 2,
|
||||||
|
|
||||||
/// Compiled closure/function value.
|
/// Compiled closure/function value.
|
||||||
@ -69,9 +72,11 @@ pub enum ObjectKind {
|
|||||||
Closure = 3,
|
Closure = 3,
|
||||||
|
|
||||||
/// Byte buffer / blob. `payload_len` is the number of bytes.
|
/// Byte buffer / blob. `payload_len` is the number of bytes.
|
||||||
|
#[allow(dead_code)] // Public/stable tag retained for future/host APIs
|
||||||
Bytes = 4,
|
Bytes = 4,
|
||||||
|
|
||||||
/// User-defined/native host object. Payload shape is host-defined.
|
/// User-defined/native host object. Payload shape is host-defined.
|
||||||
|
#[allow(dead_code)] // Reserved for host/native integrations
|
||||||
UserData = 5,
|
UserData = 5,
|
||||||
|
|
||||||
/// Coroutine object: suspended execution context with its own stack/frames.
|
/// Coroutine object: suspended execution context with its own stack/frames.
|
||||||
|
|||||||
@ -42,11 +42,14 @@ impl Scheduler {
|
|||||||
self.ready_queue.pop_front()
|
self.ready_queue.pop_front()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub fn is_ready_empty(&self) -> bool { self.ready_queue.is_empty() }
|
pub fn is_ready_empty(&self) -> bool { self.ready_queue.is_empty() }
|
||||||
|
#[cfg(test)]
|
||||||
pub fn ready_len(&self) -> usize { self.ready_queue.len() }
|
pub fn ready_len(&self) -> usize { self.ready_queue.len() }
|
||||||
|
|
||||||
// ---------- Current tracking (no switching here) ----------
|
// ---------- Current tracking (no switching here) ----------
|
||||||
pub fn set_current(&mut self, coro: Option<HeapRef>) { self.current = coro; }
|
pub fn set_current(&mut self, coro: Option<HeapRef>) { self.current = coro; }
|
||||||
|
#[cfg(test)]
|
||||||
pub fn current(&self) -> Option<HeapRef> { self.current }
|
pub fn current(&self) -> Option<HeapRef> { self.current }
|
||||||
pub fn clear_current(&mut self) { self.current = None; }
|
pub fn clear_current(&mut self) { self.current = None; }
|
||||||
|
|
||||||
|
|||||||
@ -208,10 +208,8 @@ impl Verifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Coroutine safety: forbid YIELD when operand stack is not empty (minimal rule)
|
// Coroutine safety: forbid YIELD when operand stack is not empty (minimal rule)
|
||||||
if let OpCode::Yield = instr.opcode {
|
if let OpCode::Yield = instr.opcode && in_height != 0 {
|
||||||
if in_height != 0 {
|
return Err(VerifierError::InvalidYieldContext { pc: func_start + pc, height: in_height });
|
||||||
return Err(VerifierError::InvalidYieldContext { pc: func_start + pc, height: in_height });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute out types vector with closure-aware rules
|
// Compute out types vector with closure-aware rules
|
||||||
@ -317,7 +315,7 @@ impl Verifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _pushes_final = dynamic_pushes.unwrap_or_else(|| match instr.opcode {
|
let _pushes_final = dynamic_pushes.unwrap_or(match instr.opcode {
|
||||||
OpCode::MakeClosure => 1,
|
OpCode::MakeClosure => 1,
|
||||||
OpCode::CallClosure => {
|
OpCode::CallClosure => {
|
||||||
// If we reached here, we handled it above and set dynamic_pushes
|
// If we reached here, we handled it above and set dynamic_pushes
|
||||||
@ -340,14 +338,12 @@ impl Verifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if instr.opcode == OpCode::Ret {
|
if instr.opcode == OpCode::Ret && in_height != func.return_slots {
|
||||||
if in_height != func.return_slots {
|
return Err(VerifierError::BadRetStackHeight {
|
||||||
return Err(VerifierError::BadRetStackHeight {
|
pc: func_start + pc,
|
||||||
pc: func_start + pc,
|
height: in_height,
|
||||||
height: in_height,
|
expected: func.return_slots,
|
||||||
expected: func.return_slots,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Propagate to successors
|
// Propagate to successors
|
||||||
|
|||||||
@ -228,7 +228,7 @@ impl VirtualMachine {
|
|||||||
|
|
||||||
// Resolve the entrypoint: empty (defaults to func 0), numeric func_idx, or symbol name.
|
// Resolve the entrypoint: empty (defaults to func 0), numeric func_idx, or symbol name.
|
||||||
let pc = if entrypoint.is_empty() {
|
let pc = if entrypoint.is_empty() {
|
||||||
program.functions.get(0).map(|f| f.code_offset as usize).unwrap_or(0)
|
program.functions.first().map(|f| f.code_offset as usize).unwrap_or(0)
|
||||||
} else if let Ok(func_idx) = entrypoint.parse::<usize>() {
|
} else if let Ok(func_idx) = entrypoint.parse::<usize>() {
|
||||||
program
|
program
|
||||||
.functions
|
.functions
|
||||||
@ -525,7 +525,7 @@ impl VirtualMachine {
|
|||||||
.imm_u32()
|
.imm_u32()
|
||||||
.map_err(|e| LogicalFrameEndingReason::Panic(format!("{:?}", e)))?
|
.map_err(|e| LogicalFrameEndingReason::Panic(format!("{:?}", e)))?
|
||||||
as usize;
|
as usize;
|
||||||
let val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
let val = self.pop().map_err(LogicalFrameEndingReason::Panic)?;
|
||||||
match val {
|
match val {
|
||||||
Value::Boolean(false) => {
|
Value::Boolean(false) => {
|
||||||
let func_start = self
|
let func_start = self
|
||||||
@ -551,7 +551,7 @@ impl VirtualMachine {
|
|||||||
.imm_u32()
|
.imm_u32()
|
||||||
.map_err(|e| LogicalFrameEndingReason::Panic(format!("{:?}", e)))?
|
.map_err(|e| LogicalFrameEndingReason::Panic(format!("{:?}", e)))?
|
||||||
as usize;
|
as usize;
|
||||||
let val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
let val = self.pop().map_err(LogicalFrameEndingReason::Panic)?;
|
||||||
match val {
|
match val {
|
||||||
Value::Boolean(true) => {
|
Value::Boolean(true) => {
|
||||||
let func_start = self
|
let func_start = self
|
||||||
@ -622,7 +622,7 @@ impl VirtualMachine {
|
|||||||
// Pop args top-first, then reverse to logical order arg1..argN
|
// Pop args top-first, then reverse to logical order arg1..argN
|
||||||
let mut args: Vec<Value> = Vec::with_capacity(arg_count);
|
let mut args: Vec<Value> = Vec::with_capacity(arg_count);
|
||||||
for _ in 0..arg_count {
|
for _ in 0..arg_count {
|
||||||
args.push(self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?);
|
args.push(self.pop().map_err(LogicalFrameEndingReason::Panic)?);
|
||||||
}
|
}
|
||||||
args.reverse();
|
args.reverse();
|
||||||
|
|
||||||
@ -1040,7 +1040,7 @@ impl VirtualMachine {
|
|||||||
_ => Err(OpError::Panic("Invalid types for MOD".into())),
|
_ => Err(OpError::Panic("Invalid types for MOD".into())),
|
||||||
})?,
|
})?,
|
||||||
OpCode::BoundToInt => {
|
OpCode::BoundToInt => {
|
||||||
let val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
let val = self.pop().map_err(LogicalFrameEndingReason::Panic)?;
|
||||||
if let Value::Bounded(b) = val {
|
if let Value::Bounded(b) = val {
|
||||||
self.push(Value::Int64(b as i64));
|
self.push(Value::Int64(b as i64));
|
||||||
} else {
|
} else {
|
||||||
@ -1050,13 +1050,13 @@ impl VirtualMachine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
OpCode::IntToBoundChecked => {
|
OpCode::IntToBoundChecked => {
|
||||||
let val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
let val = self.pop().map_err(LogicalFrameEndingReason::Panic)?;
|
||||||
let int_val = val.as_integer().ok_or_else(|| {
|
let int_val = val.as_integer().ok_or_else(|| {
|
||||||
LogicalFrameEndingReason::Panic(
|
LogicalFrameEndingReason::Panic(
|
||||||
"Expected integer for INT_TO_BOUND_CHECKED".into(),
|
"Expected integer for INT_TO_BOUND_CHECKED".into(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
if int_val < 0 || int_val > 0xFFFF {
|
if !(0..=0xFFFF).contains(&int_val) {
|
||||||
return Err(self.trap(
|
return Err(self.trap(
|
||||||
TRAP_OOB,
|
TRAP_OOB,
|
||||||
OpCode::IntToBoundChecked as u16,
|
OpCode::IntToBoundChecked as u16,
|
||||||
@ -1101,7 +1101,7 @@ impl VirtualMachine {
|
|||||||
_ => Err(OpError::Panic("Invalid types for OR".into())),
|
_ => Err(OpError::Panic("Invalid types for OR".into())),
|
||||||
})?,
|
})?,
|
||||||
OpCode::Not => {
|
OpCode::Not => {
|
||||||
let val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
let val = self.pop().map_err(LogicalFrameEndingReason::Panic)?;
|
||||||
if let Value::Boolean(b) = val {
|
if let Value::Boolean(b) = val {
|
||||||
self.push(Value::Boolean(!b));
|
self.push(Value::Boolean(!b));
|
||||||
} else {
|
} else {
|
||||||
@ -1148,7 +1148,7 @@ impl VirtualMachine {
|
|||||||
_ => Err(OpError::Panic("Invalid types for Shr".into())),
|
_ => Err(OpError::Panic("Invalid types for Shr".into())),
|
||||||
})?,
|
})?,
|
||||||
OpCode::Neg => {
|
OpCode::Neg => {
|
||||||
let val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
let val = self.pop().map_err(LogicalFrameEndingReason::Panic)?;
|
||||||
match val {
|
match val {
|
||||||
Value::Int32(a) => self.push(Value::Int32(a.wrapping_neg())),
|
Value::Int32(a) => self.push(Value::Int32(a.wrapping_neg())),
|
||||||
Value::Int64(a) => self.push(Value::Int64(a.wrapping_neg())),
|
Value::Int64(a) => self.push(Value::Int64(a.wrapping_neg())),
|
||||||
|
|||||||
@ -22,7 +22,7 @@ pub fn rng_from_seed(seed: u64) -> StdRng {
|
|||||||
|
|
||||||
/// Convenience helper that returns a RNG with a fixed well-known seed.
|
/// Convenience helper that returns a RNG with a fixed well-known seed.
|
||||||
pub fn deterministic_rng() -> StdRng {
|
pub fn deterministic_rng() -> StdRng {
|
||||||
rng_from_seed(0xC0FFEE_5EED)
|
rng_from_seed(0x00C0_FFEE_5EED)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the next u64 from the provided RNG.
|
/// Returns the next u64 from the provided RNG.
|
||||||
|
|||||||
@ -1,56 +0,0 @@
|
|||||||
# PR-9.4 — Final Cleanup & Quality Sweep
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
This PR performs the final cleanup pass.
|
|
||||||
|
|
||||||
Goal: Zero warnings. No dead code. No outdated examples.
|
|
||||||
|
|
||||||
## Target
|
|
||||||
|
|
||||||
1. Remove dead modules/files.
|
|
||||||
2. Remove unused imports and code.
|
|
||||||
3. Eliminate compiler warnings.
|
|
||||||
4. Update outdated examples.
|
|
||||||
5. Remove stale TODOs referencing removed architecture.
|
|
||||||
|
|
||||||
Optional (if present):
|
|
||||||
|
|
||||||
* Enforce `cargo clippy` clean baseline.
|
|
||||||
* Ensure `rustfmt` compliance.
|
|
||||||
|
|
||||||
## Acceptance Checklist
|
|
||||||
|
|
||||||
* [ ] No dead code.
|
|
||||||
* [ ] Zero compiler warnings.
|
|
||||||
* [ ] Clippy clean (if configured).
|
|
||||||
* [ ] Examples reflect new baseline.
|
|
||||||
* [ ] No TODO referencing RC/HIP.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
* Full test suite passes.
|
|
||||||
* Clean build with warnings denied (if configured).
|
|
||||||
|
|
||||||
## Junie Instructions
|
|
||||||
|
|
||||||
You MAY:
|
|
||||||
|
|
||||||
* Remove unused code.
|
|
||||||
* Refactor minor clarity issues.
|
|
||||||
|
|
||||||
You MUST NOT:
|
|
||||||
|
|
||||||
* Introduce new features.
|
|
||||||
* Change runtime semantics.
|
|
||||||
|
|
||||||
If cleanup requires semantic change, STOP and split into new PR.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Final Definition of Done
|
|
||||||
|
|
||||||
* Architecture documented clearly.
|
|
||||||
* Public API minimal and controlled.
|
|
||||||
* No temporary flags remain.
|
|
||||||
* Codebase clean, warning-free, and aligned with the new VM baseline.
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
# PROMETEU Test Cartridges
|
|
||||||
|
|
||||||
This directory contains example cartridges and test suites to validate the behavior of the PROMETEU runtime and development tools.
|
|
||||||
|
|
||||||
## Available Cartridges
|
|
||||||
|
|
||||||
### 🟩 [color-square-ts](color-square-ts)
|
|
||||||
A simple cartridge that demonstrates:
|
|
||||||
- System initialization.
|
|
||||||
- Rendering a colored square in the framebuffer.
|
|
||||||
- Basic execution loop.
|
|
||||||
|
|
||||||
## Cartridge Structure
|
|
||||||
|
|
||||||
A PROMETEU cartridge (in its unpacked form) generally consists of:
|
|
||||||
- `manifest.json`: Application metadata (ID, title, version, mode).
|
|
||||||
- `program.pbc`: Compiled bytecode for the PROMETEU VM.
|
|
||||||
- `assets/`: Resources such as tiles, sprites, and audio samples.
|
|
||||||
|
|
||||||
## How to use
|
|
||||||
|
|
||||||
You can run any of these cartridges using the main CLI:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
prometeu run test-cartridges/<cartridge_name>
|
|
||||||
```
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"kind": "File",
|
|
||||||
"imports": [],
|
|
||||||
"decls": [
|
|
||||||
30,
|
|
||||||
40
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
0000 PushConst U32(1)
|
|
||||||
0006 SetLocal U32(0)
|
|
||||||
000C GetLocal U32(0)
|
|
||||||
0012 GetLocal U32(0)
|
|
||||||
0018 SetLocal U32(1)
|
|
||||||
001E SetLocal U32(0)
|
|
||||||
0024 GetLocal U32(0)
|
|
||||||
002A GetLocal U32(1)
|
|
||||||
0030 Call U32(1)
|
|
||||||
0036 SetLocal U32(2)
|
|
||||||
003C FrameSync
|
|
||||||
003E Ret
|
|
||||||
0040 GetLocal U32(0)
|
|
||||||
0046 GetLocal U32(0)
|
|
||||||
004C GetLocal U32(1)
|
|
||||||
0052 Add
|
|
||||||
0054 Mul
|
|
||||||
0056 GetLocal U32(1)
|
|
||||||
005C Mul
|
|
||||||
005E Ret
|
|
||||||
Binary file not shown.
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "canonical",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"script_fe": "pbs",
|
|
||||||
"entry": "src/main/modules/main.pbs"
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
declare struct Vec2(x: int, y: int)
|
|
||||||
[
|
|
||||||
(x: int, y: int): (x, y) as default
|
|
||||||
(s: int): (s, s) as square
|
|
||||||
]
|
|
||||||
[[
|
|
||||||
ZERO: square(0)
|
|
||||||
]]
|
|
||||||
{
|
|
||||||
pub fn len(self: this): int {
|
|
||||||
return x * x + y * y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frame(): void {
|
|
||||||
let zero = Vec2.ZERO;
|
|
||||||
let zz = zero.len();
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "sdk",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"script_fe": "pbs",
|
|
||||||
"kind": "lib"
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
pub declare struct Color(raw: bounded)
|
|
||||||
[[
|
|
||||||
BLACK: Color(0b),
|
|
||||||
WHITE: Color(65535b),
|
|
||||||
RED: Color(63488b),
|
|
||||||
GREEN: Color(2016b),
|
|
||||||
BLUE: Color(31b)
|
|
||||||
]]
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
pub declare contract Gfx host {
|
|
||||||
fn clear(color: Color): void;
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
pub declare struct Button(
|
|
||||||
pressed: bool,
|
|
||||||
released: bool,
|
|
||||||
down: bool,
|
|
||||||
hold_frames: bounded
|
|
||||||
)
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
pub declare contract Pad host {
|
|
||||||
fn up(): Button;
|
|
||||||
fn down(): Button;
|
|
||||||
fn left(): Button;
|
|
||||||
fn right(): Button;
|
|
||||||
fn a(): Button;
|
|
||||||
fn b(): Button;
|
|
||||||
fn x(): Button;
|
|
||||||
fn y(): Button;
|
|
||||||
fn l(): Button;
|
|
||||||
fn r(): Button;
|
|
||||||
fn start(): Button;
|
|
||||||
fn select(): Button;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub declare contract Touch host {
|
|
||||||
fn screen_x(): int;
|
|
||||||
fn screen_y(): int;
|
|
||||||
fn finger(): Button;
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
// Injeção pelo hardware
|
|
||||||
declare contract LogHost host {
|
|
||||||
fn write(level: int, msg: string): void;
|
|
||||||
fn writeTag(level: int, tag: int, msg: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// exposicao do modulo, com sugar sintatico, nada injetado aqui
|
|
||||||
pub service Log {
|
|
||||||
fn trace(msg: string): void {
|
|
||||||
LogHost.write(0, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trace(tag: int, msg: string): void {
|
|
||||||
LogHost.writeTag(0, tag, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug(msg: string): void {
|
|
||||||
LogHost.write(1, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug(tag: int, msg: string): void {
|
|
||||||
LogHost.writeTag(1, tag, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(msg: string): void {
|
|
||||||
LogHost.write(2, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(tag: int, msg: string): void {
|
|
||||||
LogHost.writeTag(2, tag, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn warn(msg: string): void {
|
|
||||||
LogHost.write(3, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn warn(tag: int, msg: string): void {
|
|
||||||
LogHost.writeTag(3, tag, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn err(msg: string): void {
|
|
||||||
LogHost.write(4, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn err(tag: int, msg: string): void {
|
|
||||||
LogHost.writeTag(4, tag, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"magic": "PMTU",
|
|
||||||
"cartridge_version": 1,
|
|
||||||
"app_id": 1,
|
|
||||||
"title": "Test 1",
|
|
||||||
"app_version": "0.1.0",
|
|
||||||
"app_mode": "Game",
|
|
||||||
"entrypoint": "frame",
|
|
||||||
"asset_table": [
|
|
||||||
{
|
|
||||||
"asset_id": 0,
|
|
||||||
"asset_name": "bgm_music",
|
|
||||||
"bank_type": "SOUNDS",
|
|
||||||
"offset": 0,
|
|
||||||
"size": 88200,
|
|
||||||
"decoded_size": 88200,
|
|
||||||
"codec": "RAW",
|
|
||||||
"metadata": {
|
|
||||||
"sample_rate": 44100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"asset_id": 1,
|
|
||||||
"asset_name": "mouse_cursor",
|
|
||||||
"bank_type": "TILES",
|
|
||||||
"offset": 88200,
|
|
||||||
"size": 2304,
|
|
||||||
"decoded_size": 2304,
|
|
||||||
"codec": "RAW",
|
|
||||||
"metadata": {
|
|
||||||
"tile_size": 16,
|
|
||||||
"width": 16,
|
|
||||||
"height": 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"preload": [
|
|
||||||
{ "asset_name": "bgm_music", "slot": 0 },
|
|
||||||
{ "asset_name": "mouse_cursor", "slot": 1 }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Binary file not shown.
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "test01",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"script_fe": "pbs",
|
|
||||||
"kind": "app",
|
|
||||||
"entry": "src/main/modules/main.pbs",
|
|
||||||
"out": "build/program.pbc",
|
|
||||||
"dependencies": {
|
|
||||||
"sdk": "../sdk"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
#!/bin/zsh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
sdk/prometeu build .
|
|
||||||
cp build/program.pbc cartridge/program.pbc
|
|
||||||
sdk/prometeu run cartridge
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../dist-staging/stable/prometeu-aarch64-apple-darwin
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"prometeuPbs.serverPath": "/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/target/debug/prometeu-lsp --studio"
|
|
||||||
}
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
import { Color, Gfx } from "@sdk:gfx";
|
|
||||||
import { Pad, Touch } from "@sdk:input";
|
|
||||||
import { Log } from "@sdk:log";
|
|
||||||
|
|
||||||
declare struct Vec2(x: int, y: int)
|
|
||||||
[
|
|
||||||
(x: int, y: int): (x, y) as default { }
|
|
||||||
(s: int): (s, s) as square { }
|
|
||||||
]
|
|
||||||
[[
|
|
||||||
ZERO: square(0)
|
|
||||||
]]
|
|
||||||
{
|
|
||||||
fn getX(self: this): int {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn getY(self: this): int {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(a: int, b: int): int {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_higher(a: int, b: int): bool {
|
|
||||||
let c: int = a + b;
|
|
||||||
return c >= 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add2(a: int, b: int): int {
|
|
||||||
let c = add(a, b);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frame(): void {
|
|
||||||
let zero = Vec2.ZERO;
|
|
||||||
let zz = add2(zero.getX(), zero.getY());
|
|
||||||
|
|
||||||
// 1. Locals & Arithmetic
|
|
||||||
let x = 10;
|
|
||||||
let y = 20;
|
|
||||||
let z = add(x, y);
|
|
||||||
|
|
||||||
// 2. Control Flow (if)
|
|
||||||
if z == 30 {
|
|
||||||
// 3. Syscall Clear
|
|
||||||
Gfx.clear(Color.GREEN);
|
|
||||||
} else {
|
|
||||||
Gfx.clear(Color.RED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Input Snapshot & Nested Member Access
|
|
||||||
let pad_a = Pad.a();
|
|
||||||
let touch_f = Touch.finger();
|
|
||||||
let is_pressed = pad_a.down || touch_f.down;
|
|
||||||
if is_pressed {
|
|
||||||
Gfx.clear(Color.BLUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if Pad.b().pressed {
|
|
||||||
Log.debug("B Pressed");
|
|
||||||
Gfx.clear(Color.RED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user