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 {
|
||||
match self {
|
||||
Value::Int32(i) => i.to_string(),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
#![allow(clippy::collapsible_if)]
|
||||
use crate::memory_banks::{SoundBankPoolInstaller, TileBankPoolInstaller};
|
||||
use prometeu_hal::AssetBridge;
|
||||
use prometeu_hal::asset::{
|
||||
@ -486,12 +487,12 @@ impl AssetManager {
|
||||
let palette_data = &buffer[pixel_data_size..pixel_data_size + 2048];
|
||||
|
||||
let mut palettes = [[Color::BLACK; 16]; 64];
|
||||
for p in 0..64 {
|
||||
for c in 0..16 {
|
||||
for (p, pal) in palettes.iter_mut().enumerate() {
|
||||
for (c, slot) in pal.iter_mut().enumerate() {
|
||||
let offset = (p * 16 + c) * 2;
|
||||
let color_raw =
|
||||
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 {
|
||||
BankType::TILES => {
|
||||
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 = self
|
||||
@ -708,7 +709,7 @@ impl AssetManager {
|
||||
}
|
||||
BankType::SOUNDS => {
|
||||
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 = self
|
||||
|
||||
@ -8,6 +8,7 @@ pub enum LoopMode {
|
||||
}
|
||||
|
||||
pub trait AudioBridge {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn play(
|
||||
&mut self,
|
||||
bank_id: u8,
|
||||
@ -19,6 +20,7 @@ pub trait AudioBridge {
|
||||
priority: u8,
|
||||
loop_mode: LoopMode,
|
||||
);
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn play_sample(
|
||||
&mut self,
|
||||
sample: Arc<Sample>,
|
||||
|
||||
@ -27,7 +27,7 @@ pub struct Button {
|
||||
impl Button {
|
||||
pub fn begin_frame(&mut self, is_down_now: bool) {
|
||||
let was_down = self.down;
|
||||
self.down = is_down_now.clone();
|
||||
self.down = is_down_now;
|
||||
|
||||
self.pressed = !was_down && self.down;
|
||||
self.released = was_down && !self.down;
|
||||
|
||||
@ -50,55 +50,55 @@ impl Certifier {
|
||||
|
||||
let mut violations = 0;
|
||||
|
||||
if let Some(budget) = self.config.cycles_budget_per_frame {
|
||||
if telemetry.cycles_used > budget {
|
||||
log_service.log(
|
||||
ts_ms,
|
||||
telemetry.frame_index,
|
||||
LogLevel::Warn,
|
||||
LogSource::Pos,
|
||||
0xCA01,
|
||||
format!(
|
||||
"Cert: cycles_used exceeded budget ({} > {})",
|
||||
telemetry.cycles_used, budget
|
||||
),
|
||||
);
|
||||
violations += 1;
|
||||
}
|
||||
if let Some(budget) = self.config.cycles_budget_per_frame
|
||||
&& telemetry.cycles_used > budget
|
||||
{
|
||||
log_service.log(
|
||||
ts_ms,
|
||||
telemetry.frame_index,
|
||||
LogLevel::Warn,
|
||||
LogSource::Pos,
|
||||
0xCA01,
|
||||
format!(
|
||||
"Cert: cycles_used exceeded budget ({} > {})",
|
||||
telemetry.cycles_used, budget
|
||||
),
|
||||
);
|
||||
violations += 1;
|
||||
}
|
||||
|
||||
if let Some(limit) = self.config.max_syscalls_per_frame {
|
||||
if telemetry.syscalls > limit {
|
||||
log_service.log(
|
||||
ts_ms,
|
||||
telemetry.frame_index,
|
||||
LogLevel::Warn,
|
||||
LogSource::Pos,
|
||||
0xCA02,
|
||||
format!(
|
||||
"Cert: syscalls per frame exceeded limit ({} > {})",
|
||||
telemetry.syscalls, limit
|
||||
),
|
||||
);
|
||||
violations += 1;
|
||||
}
|
||||
if let Some(limit) = self.config.max_syscalls_per_frame
|
||||
&& telemetry.syscalls > limit
|
||||
{
|
||||
log_service.log(
|
||||
ts_ms,
|
||||
telemetry.frame_index,
|
||||
LogLevel::Warn,
|
||||
LogSource::Pos,
|
||||
0xCA02,
|
||||
format!(
|
||||
"Cert: syscalls per frame exceeded limit ({} > {})",
|
||||
telemetry.syscalls, limit
|
||||
),
|
||||
);
|
||||
violations += 1;
|
||||
}
|
||||
|
||||
if let Some(limit) = self.config.max_host_cpu_us_per_frame {
|
||||
if telemetry.host_cpu_time_us > limit {
|
||||
log_service.log(
|
||||
ts_ms,
|
||||
telemetry.frame_index,
|
||||
LogLevel::Warn,
|
||||
LogSource::Pos,
|
||||
0xCA03,
|
||||
format!(
|
||||
"Cert: host_cpu_time_us exceeded limit ({} > {})",
|
||||
telemetry.host_cpu_time_us, limit
|
||||
),
|
||||
);
|
||||
violations += 1;
|
||||
}
|
||||
if let Some(limit) = self.config.max_host_cpu_us_per_frame
|
||||
&& telemetry.host_cpu_time_us > limit
|
||||
{
|
||||
log_service.log(
|
||||
ts_ms,
|
||||
telemetry.frame_index,
|
||||
LogLevel::Warn,
|
||||
LogSource::Pos,
|
||||
0xCA03,
|
||||
format!(
|
||||
"Cert: host_cpu_time_us exceeded limit ({} > {})",
|
||||
telemetry.host_cpu_time_us, limit
|
||||
),
|
||||
);
|
||||
violations += 1;
|
||||
}
|
||||
|
||||
violations
|
||||
|
||||
@ -54,6 +54,7 @@ impl Heap {
|
||||
|
||||
/// Allocate a new object with the given kind and raw payload bytes.
|
||||
/// Returns an opaque `HeapRef` handle.
|
||||
#[cfg(test)]
|
||||
pub fn allocate_object(&mut self, kind: ObjectKind, payload: &[u8]) -> HeapRef {
|
||||
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 };
|
||||
@ -65,6 +66,7 @@ impl Heap {
|
||||
|
||||
/// Allocate a new `Array` object with the given `Value` elements.
|
||||
/// `payload_len` stores the element count; raw `payload` bytes are empty.
|
||||
#[cfg(test)]
|
||||
pub fn allocate_array(&mut self, elements: Vec<Value>) -> HeapRef {
|
||||
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 };
|
||||
@ -128,6 +130,7 @@ impl Heap {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
let idx = r.0 as usize;
|
||||
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)
|
||||
}
|
||||
|
||||
/// Internal: list inner `HeapRef` children of an object without allocating.
|
||||
/// Note: GC mark no longer uses this helper; kept for potential diagnostics.
|
||||
// Internal: list inner `HeapRef` children of an object without allocating.
|
||||
// Note: GC mark no longer uses this helper; kept for potential diagnostics.
|
||||
// fn children_of(&self, r: HeapRef) -> Box<dyn Iterator<Item = HeapRef> + '_> {
|
||||
// let idx = r.0 as usize;
|
||||
// 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.
|
||||
#[cfg(test)]
|
||||
pub fn closure_env_slice(&self, r: HeapRef) -> Option<&[Value]> {
|
||||
let idx = r.0 as usize;
|
||||
let slot = self.objects.get(idx)?.as_ref()?;
|
||||
@ -261,14 +265,14 @@ impl Heap {
|
||||
ObjectKind::Array => {
|
||||
if let Some(elems) = obj.array_elems.as_ref() {
|
||||
for val in elems.iter() {
|
||||
if let Value::HeapRef(child) = val {
|
||||
if self.is_valid(*child) {
|
||||
let marked = self
|
||||
.header(*child)
|
||||
.map(|h: &ObjectHeader| h.is_marked())
|
||||
.unwrap_or(false);
|
||||
if !marked { stack.push(*child); }
|
||||
}
|
||||
if let Value::HeapRef(child) = val
|
||||
&& self.is_valid(*child)
|
||||
{
|
||||
let marked = self
|
||||
.header(*child)
|
||||
.map(|h: &ObjectHeader| h.is_marked())
|
||||
.unwrap_or(false);
|
||||
if !marked { stack.push(*child); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -281,14 +285,14 @@ impl Heap {
|
||||
if let Some(env) = obj.closure_env.as_ref() {
|
||||
debug_assert_eq!(env.len(), env_len, "closure env len must match encoded env_len");
|
||||
for val in env[..env_len].iter() {
|
||||
if let Value::HeapRef(child) = val {
|
||||
if self.is_valid(*child) {
|
||||
let marked = self
|
||||
.header(*child)
|
||||
.map(|h: &ObjectHeader| h.is_marked())
|
||||
.unwrap_or(false);
|
||||
if !marked { stack.push(*child); }
|
||||
}
|
||||
if let Value::HeapRef(child) = val
|
||||
&& self.is_valid(*child)
|
||||
{
|
||||
let marked = self
|
||||
.header(*child)
|
||||
.map(|h: &ObjectHeader| h.is_marked())
|
||||
.unwrap_or(false);
|
||||
if !marked { stack.push(*child); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -296,14 +300,14 @@ impl Heap {
|
||||
ObjectKind::Coroutine => {
|
||||
if let Some(co) = obj.coroutine.as_ref() {
|
||||
for val in co.stack.iter() {
|
||||
if let Value::HeapRef(child) = val {
|
||||
if self.is_valid(*child) {
|
||||
let marked = self
|
||||
.header(*child)
|
||||
.map(|h: &ObjectHeader| h.is_marked())
|
||||
.unwrap_or(false);
|
||||
if !marked { stack.push(*child); }
|
||||
}
|
||||
if let Value::HeapRef(child) = val
|
||||
&& self.is_valid(*child)
|
||||
{
|
||||
let marked = self
|
||||
.header(*child)
|
||||
.map(|h: &ObjectHeader| h.is_marked())
|
||||
.unwrap_or(false);
|
||||
if !marked { stack.push(*child); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -333,7 +337,6 @@ impl Heap {
|
||||
|
||||
/// Current number of allocated (live) objects.
|
||||
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):
|
||||
/// 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> {
|
||||
let mut out = Vec::new();
|
||||
for (idx, slot) in self.objects.iter().enumerate() {
|
||||
if let Some(obj) = slot {
|
||||
if obj.header.kind == ObjectKind::Coroutine {
|
||||
if let Some(co) = &obj.coroutine {
|
||||
if matches!(co.state, CoroutineState::Ready | CoroutineState::Sleeping) {
|
||||
out.push(HeapRef(idx as u32));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(obj) = slot
|
||||
&& obj.header.kind == ObjectKind::Coroutine
|
||||
&& let Some(co) = &obj.coroutine
|
||||
&& matches!(co.state, CoroutineState::Ready | CoroutineState::Sleeping)
|
||||
{
|
||||
out.push(HeapRef(idx as u32));
|
||||
}
|
||||
}
|
||||
out
|
||||
|
||||
@ -51,12 +51,15 @@
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum ObjectKind {
|
||||
/// 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,
|
||||
|
||||
/// 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,
|
||||
|
||||
/// 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,
|
||||
|
||||
/// Compiled closure/function value.
|
||||
@ -69,9 +72,11 @@ pub enum ObjectKind {
|
||||
Closure = 3,
|
||||
|
||||
/// Byte buffer / blob. `payload_len` is the number of bytes.
|
||||
#[allow(dead_code)] // Public/stable tag retained for future/host APIs
|
||||
Bytes = 4,
|
||||
|
||||
/// User-defined/native host object. Payload shape is host-defined.
|
||||
#[allow(dead_code)] // Reserved for host/native integrations
|
||||
UserData = 5,
|
||||
|
||||
/// Coroutine object: suspended execution context with its own stack/frames.
|
||||
|
||||
@ -42,11 +42,14 @@ impl Scheduler {
|
||||
self.ready_queue.pop_front()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn is_ready_empty(&self) -> bool { self.ready_queue.is_empty() }
|
||||
#[cfg(test)]
|
||||
pub fn ready_len(&self) -> usize { self.ready_queue.len() }
|
||||
|
||||
// ---------- Current tracking (no switching here) ----------
|
||||
pub fn set_current(&mut self, coro: Option<HeapRef>) { self.current = coro; }
|
||||
#[cfg(test)]
|
||||
pub fn current(&self) -> Option<HeapRef> { self.current }
|
||||
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)
|
||||
if let OpCode::Yield = instr.opcode {
|
||||
if in_height != 0 {
|
||||
return Err(VerifierError::InvalidYieldContext { pc: func_start + pc, height: in_height });
|
||||
}
|
||||
if let OpCode::Yield = instr.opcode && in_height != 0 {
|
||||
return Err(VerifierError::InvalidYieldContext { pc: func_start + pc, height: in_height });
|
||||
}
|
||||
|
||||
// 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::CallClosure => {
|
||||
// If we reached here, we handled it above and set dynamic_pushes
|
||||
@ -340,14 +338,12 @@ impl Verifier {
|
||||
});
|
||||
}
|
||||
|
||||
if instr.opcode == OpCode::Ret {
|
||||
if in_height != func.return_slots {
|
||||
return Err(VerifierError::BadRetStackHeight {
|
||||
pc: func_start + pc,
|
||||
height: in_height,
|
||||
expected: func.return_slots,
|
||||
});
|
||||
}
|
||||
if instr.opcode == OpCode::Ret && in_height != func.return_slots {
|
||||
return Err(VerifierError::BadRetStackHeight {
|
||||
pc: func_start + pc,
|
||||
height: in_height,
|
||||
expected: func.return_slots,
|
||||
});
|
||||
}
|
||||
|
||||
// Propagate to successors
|
||||
|
||||
@ -228,7 +228,7 @@ impl VirtualMachine {
|
||||
|
||||
// Resolve the entrypoint: empty (defaults to func 0), numeric func_idx, or symbol name.
|
||||
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>() {
|
||||
program
|
||||
.functions
|
||||
@ -525,7 +525,7 @@ impl VirtualMachine {
|
||||
.imm_u32()
|
||||
.map_err(|e| LogicalFrameEndingReason::Panic(format!("{:?}", e)))?
|
||||
as usize;
|
||||
let val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
||||
let val = self.pop().map_err(LogicalFrameEndingReason::Panic)?;
|
||||
match val {
|
||||
Value::Boolean(false) => {
|
||||
let func_start = self
|
||||
@ -551,7 +551,7 @@ impl VirtualMachine {
|
||||
.imm_u32()
|
||||
.map_err(|e| LogicalFrameEndingReason::Panic(format!("{:?}", e)))?
|
||||
as usize;
|
||||
let val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
||||
let val = self.pop().map_err(LogicalFrameEndingReason::Panic)?;
|
||||
match val {
|
||||
Value::Boolean(true) => {
|
||||
let func_start = self
|
||||
@ -622,7 +622,7 @@ impl VirtualMachine {
|
||||
// Pop args top-first, then reverse to logical order arg1..argN
|
||||
let mut args: Vec<Value> = Vec::with_capacity(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();
|
||||
|
||||
@ -1040,7 +1040,7 @@ impl VirtualMachine {
|
||||
_ => Err(OpError::Panic("Invalid types for MOD".into())),
|
||||
})?,
|
||||
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 {
|
||||
self.push(Value::Int64(b as i64));
|
||||
} else {
|
||||
@ -1050,13 +1050,13 @@ impl VirtualMachine {
|
||||
}
|
||||
}
|
||||
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(|| {
|
||||
LogicalFrameEndingReason::Panic(
|
||||
"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(
|
||||
TRAP_OOB,
|
||||
OpCode::IntToBoundChecked as u16,
|
||||
@ -1101,7 +1101,7 @@ impl VirtualMachine {
|
||||
_ => Err(OpError::Panic("Invalid types for OR".into())),
|
||||
})?,
|
||||
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 {
|
||||
self.push(Value::Boolean(!b));
|
||||
} else {
|
||||
@ -1148,7 +1148,7 @@ impl VirtualMachine {
|
||||
_ => Err(OpError::Panic("Invalid types for Shr".into())),
|
||||
})?,
|
||||
OpCode::Neg => {
|
||||
let val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
||||
let val = self.pop().map_err(LogicalFrameEndingReason::Panic)?;
|
||||
match val {
|
||||
Value::Int32(a) => self.push(Value::Int32(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.
|
||||
pub fn deterministic_rng() -> StdRng {
|
||||
rng_from_seed(0xC0FFEE_5EED)
|
||||
rng_from_seed(0x00C0_FFEE_5EED)
|
||||
}
|
||||
|
||||
/// 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