From 89bd82ab82489ba0954d0db98962ce3be7f2bca5 Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Tue, 20 Jan 2026 09:31:45 +0000 Subject: [PATCH] improve stack contract --- .../src/prometeu_os/prometeu_os.rs | 38 ++++++++++++++----- docs/specs/topics/chapter-2.md | 27 +++++++++++-- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs index f216b10b..04748f08 100644 --- a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs +++ b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs @@ -290,7 +290,7 @@ impl PrometeuOS { // Helper para syscalls - fn syscall_log_write(&mut self, level_val: i64, tag: u16, msg: String) -> Result { + fn syscall_log_write(&mut self, vm: &mut VirtualMachine, level_val: i64, tag: u16, msg: String) -> Result { let level = match level_val { 0 => LogLevel::Trace, 1 => LogLevel::Debug, @@ -308,6 +308,7 @@ impl PrometeuOS { self.logs_written_this_frame.insert(app_id, count + 1); self.log(LogLevel::Warn, LogSource::App { app_id }, 0, "App exceeded log limit per frame".to_string()); } + vm.push(Value::Null); return Ok(50); } @@ -320,6 +321,7 @@ impl PrometeuOS { self.log(level, LogSource::App { app_id }, tag, final_msg); + vm.push(Value::Null); Ok(100) } @@ -529,6 +531,12 @@ mod tests { let recent = os.log_service.get_recent(1); assert_eq!(recent[0].msg, "Tagged Log"); assert_eq!(recent[0].tag, 42); + assert_eq!(vm.pop().unwrap(), Value::Null); + + // 6. GFX Syscall return test + vm.push(Value::Int64(1)); // color_idx + os.syscall(0x1001, &mut vm, &mut hw).unwrap(); // gfx.clear + assert_eq!(vm.pop().unwrap(), Value::Null); } } @@ -552,24 +560,27 @@ impl NativeInterface for PrometeuOS { // system.has_cart() -> bool 0x0001 => { // Returns true if a cartridge is available. + vm.push(Value::Boolean(true)); // For now, assume true or check state Ok(10) } - // system.run_cart() + // system.run_cart() -> null 0x0002 => { // Triggers loading and execution of the current cartridge. + vm.push(Value::Null); Ok(100) } // --- GFX Syscalls --- - // gfx.clear(color_index) + // gfx.clear(color_index) -> null 0x1001 => { let color_idx = vm.pop_integer()? as usize; let color = self.get_color(color_idx, hw); hw.gfx_mut().clear(color); + vm.push(Value::Null); Ok(100) } - // gfx.draw_rect(x, y, w, h, color_index) + // gfx.draw_rect(x, y, w, h, color_index) -> null 0x1002 => { let color_idx = vm.pop_integer()? as usize; let h = vm.pop_integer()? as i32; @@ -578,9 +589,10 @@ impl NativeInterface for PrometeuOS { let x = vm.pop_integer()? as i32; let color = self.get_color(color_idx, hw); hw.gfx_mut().fill_rect(x, y, w, h, color); + vm.push(Value::Null); Ok(200) } - // gfx.draw_line(x1, y1, x2, y2, color_index) + // gfx.draw_line(x1, y1, x2, y2, color_index) -> null 0x1003 => { let color_idx = vm.pop_integer()? as usize; let y2 = vm.pop_integer()? as i32; @@ -589,9 +601,10 @@ impl NativeInterface for PrometeuOS { let x1 = vm.pop_integer()? as i32; let color = self.get_color(color_idx, hw); hw.gfx_mut().draw_line(x1, y1, x2, y2, color); + vm.push(Value::Null); Ok(200) } - // gfx.draw_circle(x, y, r, color_index) + // gfx.draw_circle(x, y, r, color_index) -> null 0x1004 => { let color_idx = vm.pop_integer()? as usize; let r = vm.pop_integer()? as i32; @@ -599,9 +612,10 @@ impl NativeInterface for PrometeuOS { let x = vm.pop_integer()? as i32; let color = self.get_color(color_idx, hw); hw.gfx_mut().draw_circle(x, y, r, color); + vm.push(Value::Null); Ok(200) } - // gfx.draw_disc(x, y, r, border_color_idx, fill_color_idx) + // gfx.draw_disc(x, y, r, border_color_idx, fill_color_idx) -> null 0x1005 => { let fill_color_idx = vm.pop_integer()? as usize; let border_color_idx = vm.pop_integer()? as usize; @@ -611,9 +625,10 @@ impl NativeInterface for PrometeuOS { let fill_color = self.get_color(fill_color_idx, hw); let border_color = self.get_color(border_color_idx, hw); hw.gfx_mut().draw_disc(x, y, r, border_color, fill_color); + vm.push(Value::Null); Ok(300) } - // gfx.draw_square(x, y, w, h, border_color_idx, fill_color_idx) + // gfx.draw_square(x, y, w, h, border_color_idx, fill_color_idx) -> null 0x1006 => { let fill_color_idx = vm.pop_integer()? as usize; let border_color_idx = vm.pop_integer()? as usize; @@ -624,6 +639,7 @@ impl NativeInterface for PrometeuOS { let fill_color = self.get_color(fill_color_idx, hw); let border_color = self.get_color(border_color_idx, hw); hw.gfx_mut().draw_square(x, y, w, h, border_color, fill_color); + vm.push(Value::Null); Ok(200) } @@ -657,6 +673,7 @@ impl NativeInterface for PrometeuOS { if let Some(s) = sample { hw.audio_mut().play(s, voice_id, volume, pan, pitch, 0, crate::hardware::LoopMode::Off); } + vm.push(Value::Null); Ok(300) } @@ -718,6 +735,7 @@ impl NativeInterface for PrometeuOS { 0x4004 => { let handle = vm.pop_integer()? as u32; self.open_files.remove(&handle); + vm.push(Value::Null); Ok(100) } // FS_LISTDIR(path) @@ -770,7 +788,7 @@ impl NativeInterface for PrometeuOS { _ => return Err("Expected string message".into()), }; let level = vm.pop_integer()?; - self.syscall_log_write(level, 0, msg) + self.syscall_log_write(vm, level, 0, msg) } // LOG_WRITE_TAG(level, tag, msg) 0x5002 => { @@ -780,7 +798,7 @@ impl NativeInterface for PrometeuOS { }; let tag = vm.pop_integer()? as u16; let level = vm.pop_integer()?; - self.syscall_log_write(level, tag, msg) + self.syscall_log_write(vm, level, tag, msg) } _ => Err(format!("Unknown syscall: 0x{:08X}", id)), diff --git a/docs/specs/topics/chapter-2.md b/docs/specs/topics/chapter-2.md index 614fba58..daa4d3f6 100644 --- a/docs/specs/topics/chapter-2.md +++ b/docs/specs/topics/chapter-2.md @@ -84,11 +84,30 @@ Do not exist: --- -## 4. Stack Conventions +## 4. Stack Conventions & Calling ABI -* Operations use the top of the stack -* Results always return to the stack -* Last pushed = first consumed +* Operations use the top of the stack. +* Results always return to the stack. +* **LIFO Order:** Last pushed = first consumed. +* **Mandatory Return:** Every function (`Call`) and `Syscall` MUST leave exactly **one** value on the stack upon completion. If there is no meaningful value to return, `Null` must be pushed. + +### 4.1 Calling Convention (Call / Ret) + +1. **Arguments:** The caller pushes arguments in order (arg0, arg1, ..., argN-1). +2. **Execution:** The `Call` instruction specifies `args_count`. These `N` values become the **locals** of the new frame (local 0 = arg0, local 1 = arg1, etc.). +3. **Return Value:** Before executing `Ret`, the callee MUST push its return value. +4. **Cleanup:** The `Ret` instruction is responsible for: + - Popping the return value. + - Removing all locals (the arguments) from the operand stack. + - Re-pushing the return value. + - Restoring the previous frame and PC. + +### 4.2 Syscall Convention + +1. **Arguments:** The caller pushes arguments in order. +2. **Execution:** The native implementation pops arguments as needed. Since it's a stack, it will pop them in reverse order (argN-1 first, then argN-2, ..., arg0). +3. **Return Value:** The native implementation MUST push exactly one value onto the stack before returning to the VM. +4. **Cleanup:** The native implementation is responsible for popping all arguments it expects. Example: