401 lines
15 KiB
Rust
401 lines
15 KiB
Rust
use crate::hardware::HardwareBridge;
|
|
use crate::prometeu_os::NativeInterface;
|
|
use crate::vm::call_frame::CallFrame;
|
|
use crate::vm::opcode::OpCode;
|
|
use crate::vm::value::Value;
|
|
use crate::vm::Program;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum LogicalFrameEndingReason {
|
|
FrameSync,
|
|
BudgetExhausted,
|
|
Halted,
|
|
EndOfRom,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct BudgetReport {
|
|
pub cycles_used: u64,
|
|
pub reason: LogicalFrameEndingReason,
|
|
}
|
|
|
|
pub struct VirtualMachine {
|
|
pub pc: usize,
|
|
pub operand_stack: Vec<Value>,
|
|
pub call_stack: Vec<CallFrame>,
|
|
pub globals: Vec<Value>,
|
|
pub program: Program,
|
|
pub heap: Vec<Value>, // Simplificado para demo, futuramente RAM/Heap real
|
|
pub cycles: u64,
|
|
pub halted: bool,
|
|
}
|
|
|
|
impl VirtualMachine {
|
|
pub fn new(rom: Vec<u8>, constant_pool: Vec<Value>) -> Self {
|
|
Self {
|
|
pc: 0,
|
|
operand_stack: Vec::new(),
|
|
call_stack: Vec::new(),
|
|
globals: Vec::new(),
|
|
program: Program::new(rom, constant_pool),
|
|
heap: Vec::new(),
|
|
cycles: 0,
|
|
halted: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for VirtualMachine {
|
|
fn default() -> Self {
|
|
Self::new(vec![], vec![])
|
|
}
|
|
}
|
|
|
|
impl VirtualMachine {
|
|
pub fn run_budget(
|
|
&mut self,
|
|
budget: u64,
|
|
native: &mut dyn NativeInterface,
|
|
hw: &mut dyn HardwareBridge,
|
|
) -> Result<BudgetReport, String> {
|
|
let start_cycles = self.cycles;
|
|
let mut ending_reason: Option<LogicalFrameEndingReason> = None;
|
|
|
|
while (self.cycles - start_cycles) < budget
|
|
&& !self.halted
|
|
&& self.pc < self.program.rom.len()
|
|
{
|
|
let pc_before = self.pc;
|
|
let cycles_before = self.cycles;
|
|
|
|
// Fast-path: FRAME_SYNC encerra o frame lógico
|
|
let opcode_val = self.peek_u16()?;
|
|
let opcode = OpCode::try_from(opcode_val)?;
|
|
if opcode == OpCode::FrameSync {
|
|
self.pc += 2;
|
|
self.cycles += OpCode::FrameSync.cycles();
|
|
ending_reason = Some(LogicalFrameEndingReason::FrameSync);
|
|
break;
|
|
}
|
|
|
|
self.step(native, hw)?;
|
|
|
|
// garante progresso real
|
|
if self.pc == pc_before && self.cycles == cycles_before && !self.halted {
|
|
return Err(format!("VM stuck at PC 0x{:08X}", self.pc));
|
|
}
|
|
}
|
|
|
|
if ending_reason.is_none() {
|
|
if self.halted {
|
|
ending_reason = Some(LogicalFrameEndingReason::Halted);
|
|
} else if self.pc >= self.program.rom.len() {
|
|
ending_reason = Some(LogicalFrameEndingReason::EndOfRom);
|
|
} else {
|
|
ending_reason = Some(LogicalFrameEndingReason::BudgetExhausted);
|
|
}
|
|
}
|
|
|
|
Ok(BudgetReport {
|
|
cycles_used: self.cycles - start_cycles,
|
|
reason: ending_reason.unwrap(),
|
|
})
|
|
}
|
|
|
|
fn peek_u16(&self) -> Result<u16, String> {
|
|
if self.pc + 2 > self.program.rom.len() {
|
|
return Err("Unexpected end of ROM".into());
|
|
}
|
|
let bytes = [
|
|
self.program.rom[self.pc],
|
|
self.program.rom[self.pc + 1],
|
|
];
|
|
Ok(u16::from_le_bytes(bytes))
|
|
}
|
|
|
|
pub fn step(&mut self, native: &mut dyn NativeInterface, hw: &mut dyn HardwareBridge) -> Result<(), String> {
|
|
if self.halted || self.pc >= self.program.rom.len() {
|
|
return Ok(());
|
|
}
|
|
|
|
let opcode_val = self.read_u16()?;
|
|
let opcode = OpCode::try_from(opcode_val)?;
|
|
|
|
match opcode {
|
|
OpCode::Nop => {}
|
|
OpCode::Halt => {
|
|
self.halted = true;
|
|
}
|
|
OpCode::Jmp => {
|
|
let addr = self.read_u32()? as usize;
|
|
self.pc = addr;
|
|
}
|
|
OpCode::JmpIfFalse => {
|
|
let addr = self.read_u32()? as usize;
|
|
let val = self.pop()?;
|
|
if let Value::Boolean(false) = val {
|
|
self.pc = addr;
|
|
}
|
|
}
|
|
OpCode::PushConst => {
|
|
let idx = self.read_u32()? as usize;
|
|
let val = self.program.constant_pool.get(idx).cloned().ok_or("Invalid constant index")?;
|
|
self.push(val);
|
|
}
|
|
OpCode::Pop => {
|
|
self.pop()?;
|
|
}
|
|
OpCode::Dup => {
|
|
let val = self.peek()?.clone();
|
|
self.push(val);
|
|
}
|
|
OpCode::Swap => {
|
|
let a = self.pop()?;
|
|
let b = self.pop()?;
|
|
self.push(a);
|
|
self.push(b);
|
|
}
|
|
OpCode::Add => self.binary_op(|a, b| match (a, b) {
|
|
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_add(b))),
|
|
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
|
|
(Value::Integer(a), Value::Float(b)) => Ok(Value::Float(a as f64 + b)),
|
|
(Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a + b as f64)),
|
|
_ => Err("Invalid types for ADD".into()),
|
|
})?,
|
|
OpCode::Sub => self.binary_op(|a, b| match (a, b) {
|
|
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_sub(b))),
|
|
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)),
|
|
(Value::Integer(a), Value::Float(b)) => Ok(Value::Float(a as f64 - b)),
|
|
(Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a - b as f64)),
|
|
_ => Err("Invalid types for SUB".into()),
|
|
})?,
|
|
OpCode::Mul => self.binary_op(|a, b| match (a, b) {
|
|
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_mul(b))),
|
|
(Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)),
|
|
(Value::Integer(a), Value::Float(b)) => Ok(Value::Float(a as f64 * b)),
|
|
(Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a * b as f64)),
|
|
_ => Err("Invalid types for MUL".into()),
|
|
})?,
|
|
OpCode::Div => self.binary_op(|a, b| match (a, b) {
|
|
(Value::Integer(a), Value::Integer(b)) => {
|
|
if b == 0 { return Err("Division by zero".into()); }
|
|
Ok(Value::Integer(a / b))
|
|
}
|
|
(Value::Float(a), Value::Float(b)) => {
|
|
if b == 0.0 { return Err("Division by zero".into()); }
|
|
Ok(Value::Float(a / b))
|
|
}
|
|
(Value::Integer(a), Value::Float(b)) => {
|
|
if b == 0.0 { return Err("Division by zero".into()); }
|
|
Ok(Value::Float(a as f64 / b))
|
|
}
|
|
(Value::Float(a), Value::Integer(b)) => {
|
|
if b == 0 { return Err("Division by zero".into()); }
|
|
Ok(Value::Float(a / b as f64))
|
|
}
|
|
_ => Err("Invalid types for DIV".into()),
|
|
})?,
|
|
OpCode::Eq => self.binary_op(|a, b| Ok(Value::Boolean(a == b)))?,
|
|
OpCode::Neq => self.binary_op(|a, b| Ok(Value::Boolean(a != b)))?,
|
|
OpCode::Lt => self.binary_op(|a, b| {
|
|
match (a, b) {
|
|
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Boolean(a < b)),
|
|
(Value::Float(a), Value::Float(b)) => Ok(Value::Boolean(a < b)),
|
|
(Value::Integer(a), Value::Float(b)) => Ok(Value::Boolean((a as f64) < b)),
|
|
(Value::Float(a), Value::Integer(b)) => Ok(Value::Boolean(a < (b as f64))),
|
|
_ => Err("Invalid types for LT".into()),
|
|
}
|
|
})?,
|
|
OpCode::Gt => self.binary_op(|a, b| {
|
|
match (a, b) {
|
|
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Boolean(a > b)),
|
|
(Value::Float(a), Value::Float(b)) => Ok(Value::Boolean(a > b)),
|
|
(Value::Integer(a), Value::Float(b)) => Ok(Value::Boolean((a as f64) > b)),
|
|
(Value::Float(a), Value::Integer(b)) => Ok(Value::Boolean(a > (b as f64))),
|
|
_ => Err("Invalid types for GT".into()),
|
|
}
|
|
})?,
|
|
OpCode::And => self.binary_op(|a, b| match (a, b) {
|
|
(Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Boolean(a && b)),
|
|
_ => Err("Invalid types for AND".into()),
|
|
})?,
|
|
OpCode::Or => self.binary_op(|a, b| match (a, b) {
|
|
(Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Boolean(a || b)),
|
|
_ => Err("Invalid types for OR".into()),
|
|
})?,
|
|
OpCode::Not => {
|
|
let val = self.pop()?;
|
|
if let Value::Boolean(b) = val {
|
|
self.push(Value::Boolean(!b));
|
|
} else {
|
|
return Err("Invalid type for NOT".into());
|
|
}
|
|
}
|
|
OpCode::GetGlobal => {
|
|
let idx = self.read_u32()? as usize;
|
|
let val = self.globals.get(idx).cloned().ok_or("Invalid global index")?;
|
|
self.push(val);
|
|
}
|
|
OpCode::SetGlobal => {
|
|
let idx = self.read_u32()? as usize;
|
|
let val = self.pop()?;
|
|
if idx >= self.globals.len() {
|
|
self.globals.resize(idx + 1, Value::Null);
|
|
}
|
|
self.globals[idx] = val;
|
|
}
|
|
OpCode::GetLocal => {
|
|
let idx = self.read_u32()? as usize;
|
|
let frame = self.call_stack.last().ok_or("No active call frame")?;
|
|
let val = self.operand_stack.get(frame.stack_base + idx).cloned().ok_or("Invalid local index")?;
|
|
self.push(val);
|
|
}
|
|
OpCode::SetLocal => {
|
|
let idx = self.read_u32()? as usize;
|
|
let val = self.pop()?;
|
|
let frame = self.call_stack.last().ok_or("No active call frame")?;
|
|
let stack_idx = frame.stack_base + idx;
|
|
if stack_idx >= self.operand_stack.len() {
|
|
return Err("Local index out of bounds".into());
|
|
}
|
|
self.operand_stack[stack_idx] = val;
|
|
}
|
|
OpCode::Call => {
|
|
let addr = self.read_u32()? as usize;
|
|
let args_count = self.read_u32()? as usize;
|
|
let stack_base = self.operand_stack.len() - args_count;
|
|
self.call_stack.push(CallFrame {
|
|
return_address: self.pc,
|
|
stack_base,
|
|
locals_count: args_count,
|
|
});
|
|
self.pc = addr;
|
|
}
|
|
OpCode::Ret => {
|
|
let frame = self.call_stack.pop().ok_or("Call stack underflow")?;
|
|
let return_val = self.pop()?;
|
|
self.operand_stack.truncate(frame.stack_base);
|
|
self.push(return_val);
|
|
self.pc = frame.return_address;
|
|
}
|
|
OpCode::PushScope => {
|
|
let locals_count = self.read_u32()? as usize;
|
|
let stack_base = self.operand_stack.len();
|
|
for _ in 0..locals_count {
|
|
self.push(Value::Null);
|
|
}
|
|
self.call_stack.push(CallFrame {
|
|
return_address: 0,
|
|
stack_base,
|
|
locals_count,
|
|
});
|
|
}
|
|
OpCode::PopScope => {
|
|
let frame = self.call_stack.pop().ok_or("Call stack underflow")?;
|
|
self.operand_stack.truncate(frame.stack_base);
|
|
}
|
|
OpCode::Alloc => {
|
|
let size = self.read_u32()? as usize;
|
|
let ref_idx = self.heap.len();
|
|
for _ in 0..size {
|
|
self.heap.push(Value::Null);
|
|
}
|
|
self.push(Value::Ref(ref_idx));
|
|
}
|
|
OpCode::LoadRef => {
|
|
let offset = self.read_u32()? as usize;
|
|
let ref_val = self.pop()?;
|
|
if let Value::Ref(base) = ref_val {
|
|
let val = self.heap.get(base + offset).cloned().ok_or("Invalid heap access")?;
|
|
self.push(val);
|
|
} else {
|
|
return Err("Expected reference for LOAD_REF".into());
|
|
}
|
|
}
|
|
OpCode::StoreRef => {
|
|
let offset = self.read_u32()? as usize;
|
|
let val = self.pop()?;
|
|
let ref_val = self.pop()?;
|
|
if let Value::Ref(base) = ref_val {
|
|
if base + offset >= self.heap.len() {
|
|
return Err("Invalid heap access".into());
|
|
}
|
|
self.heap[base + offset] = val;
|
|
} else {
|
|
return Err("Expected reference for STORE_REF".into());
|
|
}
|
|
}
|
|
OpCode::Syscall => {
|
|
let id = self.read_u32()?;
|
|
let native_cycles = native.syscall(id, self, hw).map_err(|e| format!("syscall 0x{:08X} failed: {}", id, e))?;
|
|
self.cycles += native_cycles;
|
|
}
|
|
OpCode::FrameSync => {
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
self.cycles += opcode.cycles();
|
|
Ok(())
|
|
}
|
|
|
|
fn read_u32(&mut self) -> Result<u32, String> {
|
|
if self.pc + 4 > self.program.rom.len() {
|
|
return Err("Unexpected end of ROM".into());
|
|
}
|
|
let bytes = [
|
|
self.program.rom[self.pc],
|
|
self.program.rom[self.pc + 1],
|
|
self.program.rom[self.pc + 2],
|
|
self.program.rom[self.pc + 3],
|
|
];
|
|
self.pc += 4;
|
|
Ok(u32::from_le_bytes(bytes))
|
|
}
|
|
|
|
fn read_u16(&mut self) -> Result<u16, String> {
|
|
if self.pc + 2 > self.program.rom.len() {
|
|
return Err("Unexpected end of ROM".into());
|
|
}
|
|
let bytes = [
|
|
self.program.rom[self.pc],
|
|
self.program.rom[self.pc + 1],
|
|
];
|
|
self.pc += 2;
|
|
Ok(u16::from_le_bytes(bytes))
|
|
}
|
|
|
|
pub fn push(&mut self, val: Value) {
|
|
self.operand_stack.push(val);
|
|
}
|
|
|
|
pub fn pop(&mut self) -> Result<Value, String> {
|
|
self.operand_stack.pop().ok_or("Stack underflow".into())
|
|
}
|
|
|
|
pub fn pop_number(&mut self) -> Result<f64, String> {
|
|
let val = self.pop()?;
|
|
val.as_float().ok_or_else(|| "Expected number".into())
|
|
}
|
|
|
|
pub fn pop_integer(&mut self) -> Result<i64, String> {
|
|
let val = self.pop()?;
|
|
val.as_integer().ok_or_else(|| "Expected integer".into())
|
|
}
|
|
|
|
pub fn peek(&self) -> Result<&Value, String> {
|
|
self.operand_stack.last().ok_or("Stack underflow".into())
|
|
}
|
|
|
|
fn binary_op<F>(&mut self, f: F) -> Result<(), String>
|
|
where
|
|
F: FnOnce(Value, Value) -> Result<Value, String>,
|
|
{
|
|
let b = self.pop()?;
|
|
let a = self.pop()?;
|
|
let res = f(a, b)?;
|
|
self.push(res);
|
|
Ok(())
|
|
}
|
|
}
|