use crate::error::{VmError, VmResult};
use crate::state::VmState;
pub fn handle_cmp(state: &mut VmState) -> VmResult<()> {
let b = state.pop()?;
let a = state.pop()?;
state.update_cmp_flags(a, b);
state.push(a)?;
state.push(b)
}
pub fn handle_jmp(state: &mut VmState) -> VmResult<()> {
let offset = state.read_i16()?;
jump_relative(state, offset)
}
pub fn handle_jz(state: &mut VmState) -> VmResult<()> {
let offset = state.read_i16()?;
if state.is_zero() {
jump_relative(state, offset)
} else {
Ok(())
}
}
pub fn handle_jnz(state: &mut VmState) -> VmResult<()> {
let offset = state.read_i16()?;
if !state.is_zero() {
jump_relative(state, offset)
} else {
Ok(())
}
}
pub fn handle_jgt(state: &mut VmState) -> VmResult<()> {
let offset = state.read_i16()?;
if !state.is_zero() && (state.is_negative() == state.is_overflow()) {
jump_relative(state, offset)
} else {
Ok(())
}
}
pub fn handle_jlt(state: &mut VmState) -> VmResult<()> {
let offset = state.read_i16()?;
if state.is_negative() != state.is_overflow() {
jump_relative(state, offset)
} else {
Ok(())
}
}
pub fn handle_jge(state: &mut VmState) -> VmResult<()> {
let offset = state.read_i16()?;
if state.is_negative() == state.is_overflow() {
jump_relative(state, offset)
} else {
Ok(())
}
}
pub fn handle_jle(state: &mut VmState) -> VmResult<()> {
let offset = state.read_i16()?;
if state.is_zero() || (state.is_negative() != state.is_overflow()) {
jump_relative(state, offset)
} else {
Ok(())
}
}
pub fn jump_relative(state: &mut VmState, offset: i16) -> VmResult<()> {
let new_ip = if offset >= 0 {
state.ip.checked_add(offset as usize)
} else {
state.ip.checked_sub((-offset) as usize)
};
match new_ip {
Some(ip) if ip <= state.code.len() => {
state.ip = ip;
Ok(())
}
_ => Err(VmError::InvalidJumpTarget),
}
}
pub fn handle_call(state: &mut VmState) -> VmResult<()> {
let offset = state.read_i16()?;
state.call_stack.push(state.ip);
jump_relative(state, offset)
}
pub fn handle_ret(state: &mut VmState) -> VmResult<()> {
match state.call_stack.pop() {
Some(return_addr) => {
state.ip = return_addr;
Ok(())
}
None => {
state.halted = true;
state.result = state.peek().unwrap_or(0);
Ok(())
}
}
}