472 lines
14 KiB
Rust
472 lines
14 KiB
Rust
use crate::binread::BinRead;
|
|
use crate::frame::Frame;
|
|
use crate::machine::Machine;
|
|
use std::io::Read;
|
|
use std::num::Wrapping;
|
|
use crate::Result;
|
|
use crate::value::Value;
|
|
use std::convert::TryInto;
|
|
use crate::netstack::NetStack;
|
|
|
|
pub type OpFunc = fn(&mut Machine) -> Result<()>;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum Args {
|
|
Byte,
|
|
Short,
|
|
Var,
|
|
Label,
|
|
Constant,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum Operation {
|
|
Op(&'static str, OpFunc, Vec<Args>),
|
|
Invalid,
|
|
}
|
|
|
|
const JUMP_OFFSET: i32 = 3;
|
|
|
|
lazy_static! {
|
|
static ref OPS_MAP: Vec<Operation> = {
|
|
let mut m = vec![Operation::Invalid; 0xFF + 1];
|
|
|
|
m[0x00] = Operation::Op("NOP", nop, vec![]);
|
|
|
|
m[0x10] = Operation::Op("BIPUSH", bipush, vec![Args::Byte]);
|
|
m[0x13] = Operation::Op("LDC_W", ldc_w, vec![Args::Constant]);
|
|
m[0x15] = Operation::Op("ILOAD", iload, vec![Args::Var]);
|
|
|
|
m[0x36] = Operation::Op("ISTORE", istore, vec![Args::Var]);
|
|
|
|
m[0x57] = Operation::Op("POP", pop, vec![]);
|
|
m[0x59] = Operation::Op("DUP", dup, vec![]);
|
|
m[0x5F] = Operation::Op("SWAP", swap, vec![]);
|
|
|
|
m[0x60] = Operation::Op("IADD", iadd, vec![]);
|
|
m[0x64] = Operation::Op("ISUB", isub, vec![]);
|
|
|
|
#[cfg(feature = "extra.arithmetic")]
|
|
{
|
|
m[0x70] = Operation::Op("SHL", shl, vec![]);
|
|
m[0x71] = Operation::Op("SHR", shr, vec![]);
|
|
m[0x72] = Operation::Op("IMUL", imul, vec![]);
|
|
m[0x73] = Operation::Op("IDIV", idiv, vec![]);
|
|
}
|
|
m[0x7E] = Operation::Op("IAND", iand, vec![]);
|
|
|
|
m[0x84] = Operation::Op("IINC", iinc, vec![Args::Var, Args::Byte]);
|
|
|
|
m[0x99] = Operation::Op("IFEQ", ifeq, vec![Args::Label]);
|
|
m[0x9b] = Operation::Op("IFLT", iflt, vec![Args::Label]);
|
|
m[0x9F] = Operation::Op("IF_ICMPEQ", if_icmpeq, vec![Args::Label]);
|
|
|
|
m[0xA7] = Operation::Op("GOTO", goto, vec![Args::Label]);
|
|
m[0xAC] = Operation::Op("IRETURN", ireturn, vec![]);
|
|
|
|
m[0xB0] = Operation::Op("IOR", ior, vec![]);
|
|
m[0xB6] = Operation::Op("INVOKEVIRTUAL", invokevirtual, vec![Args::Constant]);
|
|
|
|
m[0xC4] = Operation::Op("WIDE", wide, vec![]);
|
|
|
|
#[cfg(feature = "bonus.heap")]
|
|
{
|
|
m[0xD1] = Operation::Op("NEWARRAY", newarray, vec![]);
|
|
m[0xD2] = Operation::Op("IALOAD", iaload, vec![]);
|
|
m[0xD3] = Operation::Op("IASTORE", iastore, vec![]);
|
|
m[0xD4] = Operation::Op("GC", gc, vec![]);
|
|
}
|
|
|
|
#[cfg(feature = "bonus.network")]
|
|
{
|
|
m[0xE1] = Operation::Op("NETBIND", netbind, vec![]);
|
|
m[0xE2] = Operation::Op("NETCONNECT", netconnect, vec![]);
|
|
m[0xE3] = Operation::Op("NETIN", netin, vec![]);
|
|
m[0xE4] = Operation::Op("NETOUT", netout, vec![]);
|
|
m[0xE5] = Operation::Op("NETCLOSE", netclose, vec![]);
|
|
}
|
|
|
|
#[cfg(feature = "extra.sleep")]
|
|
{
|
|
m[0xF0] = Operation::Op("SLP", slp, vec![Args::Byte]);
|
|
}
|
|
m[0xFC] = Operation::Op("IN", _in, vec![]);
|
|
m[0xFD] = Operation::Op("OUT", out, vec![]);
|
|
m[0xFE] = Operation::Op("ERR", err, vec![]);
|
|
m[0xFF] = Operation::Op("HALT", halt, vec![]);
|
|
|
|
m
|
|
};
|
|
}
|
|
|
|
pub fn num_to_op(op: u8) -> &'static Operation {
|
|
&OPS_MAP[op as usize]
|
|
}
|
|
|
|
fn nop(_: &mut Machine) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
fn bipush(machine: &mut Machine) -> Result<()> {
|
|
let val = match machine.block.read_i8() {
|
|
Ok(a) => i32::from(a),
|
|
Err(_) => return Err("Expected argument"),
|
|
};
|
|
machine.cur_stack().push(Value::Int(val));
|
|
Ok(())
|
|
}
|
|
|
|
fn dup(machine: &mut Machine) -> Result<()> {
|
|
let val = machine.cur_stack().top()?.clone();
|
|
machine.cur_stack().push(val);
|
|
Ok(())
|
|
}
|
|
|
|
fn out(machine: &mut Machine) -> Result<()> {
|
|
let val: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
|
|
let buffer = [val as u8];
|
|
let mut out = machine.stream_out.lock().unwrap();
|
|
out.write_all(&buffer).unwrap();
|
|
out.flush().unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
fn pop(machine: &mut Machine) -> Result<()> {
|
|
machine.cur_stack().pop()?;
|
|
Ok(())
|
|
}
|
|
|
|
fn iadd(machine: &mut Machine) -> Result<()> {
|
|
let a: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let b: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let res = Wrapping(a) + Wrapping(b);
|
|
machine.cur_stack().push(Value::Int(res.0));
|
|
Ok(())
|
|
}
|
|
|
|
fn isub(machine: &mut Machine) -> Result<()> {
|
|
let a: Wrapping<i32> = Wrapping(machine.cur_stack().pop()?.try_into()?);
|
|
let b: Wrapping<i32> = Wrapping(machine.cur_stack().pop()?.try_into()?);
|
|
machine.cur_stack().push(Value::Int((b - a).0));
|
|
Ok(())
|
|
}
|
|
|
|
fn _in(machine: &mut Machine) -> Result<()> {
|
|
let mut buffer = [0; 1];
|
|
let val = match machine.stream_in.read_exact(&mut buffer) {
|
|
Err(_) => 0i32,
|
|
Ok(_) => i32::from(buffer[0]),
|
|
};
|
|
machine.cur_stack().push(Value::Int(val));
|
|
Ok(())
|
|
}
|
|
|
|
fn goto(machine: &mut Machine) -> Result<()> {
|
|
let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET;
|
|
machine.block.jump(offset)
|
|
}
|
|
|
|
fn err(machine: &mut Machine) -> Result<()> {
|
|
machine.halted = true;
|
|
eprintln!("MACHINE CALLED ERR");
|
|
Ok(())
|
|
}
|
|
|
|
fn halt(machine: &mut Machine) -> Result<()> {
|
|
machine.halted = true;
|
|
Ok(())
|
|
}
|
|
|
|
fn ifeq(machine: &mut Machine) -> Result<()> {
|
|
let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET;
|
|
let tos = machine.cur_stack().pop()?;
|
|
// IFEQ is special because it gets special support for NetRefs
|
|
let val = match tos {
|
|
Value::NetRef(v) => v as i32,
|
|
Value::Int(v) => v,
|
|
Value::HeapRef(_) => Err("cannot use heapref in IFEQ")?,
|
|
};
|
|
|
|
if val == 0 {
|
|
return machine.block.jump(offset);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn iflt(machine: &mut Machine) -> Result<()> {
|
|
let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET;
|
|
let compare: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
if compare < 0 {
|
|
return machine.block.jump(offset);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn if_icmpeq(machine: &mut Machine) -> Result<()> {
|
|
let offset = i32::from(machine.block.read_i16()?) - JUMP_OFFSET;
|
|
if machine.cur_stack().pop()? == machine.cur_stack().pop()? {
|
|
return machine.block.jump(offset);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn iand(machine: &mut Machine) -> Result<()> {
|
|
let a: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let b: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
machine.cur_stack().push(Value::Int(a & b));
|
|
Ok(())
|
|
}
|
|
|
|
fn ior(machine: &mut Machine) -> Result<()> {
|
|
let a: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let b: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
machine.cur_stack().push(Value::Int(a | b));
|
|
Ok(())
|
|
}
|
|
|
|
fn ldc_w(machine: &mut Machine) -> Result<()> {
|
|
let offset = machine.block.read_i16()? as usize;
|
|
let val = machine.pool.get(offset)?;
|
|
machine.cur_stack().push(Value::Int(val));
|
|
Ok(())
|
|
}
|
|
|
|
fn swap(machine: &mut Machine) -> Result<()> {
|
|
let a = machine.cur_stack().pop()?;
|
|
let b = machine.cur_stack().pop()?;
|
|
machine.cur_stack().push(a);
|
|
machine.cur_stack().push(b);
|
|
Ok(())
|
|
}
|
|
|
|
fn wide(machine: &mut Machine) -> Result<()> {
|
|
machine.wide = true;
|
|
machine.step()
|
|
}
|
|
|
|
fn iload(machine: &mut Machine) -> Result<()> {
|
|
let a = machine.read_index()?;
|
|
let val = machine.cur_frame().get(a)?.clone();
|
|
machine.cur_stack().push(val);
|
|
Ok(())
|
|
}
|
|
|
|
fn istore(machine: &mut Machine) -> Result<()> {
|
|
let a = machine.read_index()?;
|
|
let val = machine.cur_stack().pop()?;
|
|
machine.cur_frame().set(a, val)
|
|
}
|
|
|
|
fn iinc(machine: &mut Machine) -> Result<()> {
|
|
let a = machine.read_index()?;
|
|
let b = i32::from(machine.block.read_i8()?);
|
|
let val: i32 = machine.cur_frame().get(a)?.try_into()?;
|
|
machine.cur_frame().set(a, Value::Int(val + b))
|
|
}
|
|
|
|
fn invokevirtual(machine: &mut Machine) -> Result<()> {
|
|
let method_index = machine.block.read_u16()?;
|
|
let invoke_addr = machine.pool.get(method_index as usize)? as usize;
|
|
let return_addr = machine.get_program_counter();
|
|
|
|
machine.block.seek(invoke_addr)?;
|
|
let arg_count = machine.block.read_u16()?;
|
|
let var_count = machine.block.read_u16()?;
|
|
let mut newframe = Frame::new((arg_count + var_count) as usize, invoke_addr);
|
|
|
|
// Lifetime for cur_stack
|
|
{
|
|
let cur_stack = machine.cur_stack();
|
|
for i in 1..arg_count {
|
|
newframe.set((arg_count - i) as usize, cur_stack.pop()?)?;
|
|
}
|
|
|
|
cur_stack.pop()?; // Nuke the OBJREF
|
|
}
|
|
|
|
newframe.set(0, Value::Int(return_addr as i32))?;
|
|
|
|
machine.frame.push(newframe);
|
|
Ok(())
|
|
}
|
|
|
|
fn ireturn(machine: &mut Machine) -> Result<()> {
|
|
let mut prev_frame: Frame = match machine.frame.pop() {
|
|
Some(a) => a,
|
|
None => return Err("Got no frame... somehow..."),
|
|
};
|
|
let result = prev_frame.stack.pop()?;
|
|
let return_addr: i32 = prev_frame.get(0)?.try_into()?;
|
|
machine.cur_stack().push(result);
|
|
machine.block.seek(return_addr as usize)
|
|
}
|
|
|
|
#[cfg(feature = "extra.sleep")]
|
|
fn slp(machine: &mut Machine) -> Result<()> {
|
|
use std::thread;
|
|
use std::time;
|
|
let val = match machine.block.read_u8() {
|
|
Ok(a) => u64::from(a),
|
|
Err(_) => return Err("Expected argument"),
|
|
};
|
|
let slpdur = time::Duration::from_millis(val * 100);
|
|
thread::sleep(slpdur);
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "bonus.network")]
|
|
fn netbind(machine: &mut Machine) -> Result<()> {
|
|
let port: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
|
|
let mut stack = NetStack::new();
|
|
if stack.bind(port as u16).is_err() {
|
|
machine.cur_stack().push(Value::Int(0));
|
|
return Ok(());
|
|
}
|
|
|
|
machine.net.push(stack);
|
|
let idx = machine.net.len();
|
|
machine.cur_stack().push(Value::NetRef(idx));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "bonus.network")]
|
|
fn netconnect(machine: &mut Machine) -> Result<()> {
|
|
let port: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let host: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
|
|
let mut stack = NetStack::new();
|
|
if stack.connect(host as u32, port as u16).is_err() {
|
|
machine.cur_stack().push(Value::Int(0));
|
|
return Ok(());
|
|
}
|
|
machine.net.push(stack);
|
|
let idx = machine.net.len();
|
|
machine.cur_stack().push(Value::NetRef(idx));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "bonus.network")]
|
|
fn netin(machine: &mut Machine) -> Result<()> {
|
|
let idx = match machine.cur_stack().pop()? {
|
|
Value::NetRef(x) => x,
|
|
_ => Err("Invalid netref given to network instruction")?,
|
|
};
|
|
|
|
let conn = machine.net.get_mut(idx - 1).ok_or("Invalid network connection")?;
|
|
let byte = i32::from(conn.read_byte()?);
|
|
machine.cur_stack().push(Value::Int(byte));
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "bonus.network")]
|
|
fn netout(machine: &mut Machine) -> Result<()> {
|
|
let idx = match machine.cur_stack().pop()? {
|
|
Value::NetRef(x) => x,
|
|
_ => Err("Invalid netref given to network instruction")?,
|
|
};
|
|
|
|
let val: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let conn = machine.net.get_mut(idx - 1).ok_or("Invalid network connection")?;
|
|
conn.write_byte(val as u8)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "bonus.network")]
|
|
fn netclose(machine: &mut Machine) -> Result<()> {
|
|
let idx = match machine.cur_stack().pop()? {
|
|
Value::NetRef(x) => x,
|
|
_ => Err("Invalid netref given to network instruction")?,
|
|
};
|
|
|
|
machine.net.get_mut(idx - 1).ok_or("Invalid network connection")?.close()?;
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "bonus.heap")]
|
|
fn newarray(machine: &mut Machine) -> Result<()> {
|
|
let size: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let heap = machine.heap.alloc(size as usize);
|
|
machine.cur_stack().push(Value::HeapRef(heap));
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "bonus.heap")]
|
|
fn iastore(machine: &mut Machine) -> Result<()> {
|
|
use std::cell::RefCell;
|
|
|
|
let heap = match machine.cur_stack().pop()? {
|
|
Value::HeapRef(a) => a,
|
|
_ => return Err("Cannot use int as heapref"),
|
|
};
|
|
let index: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let value = machine.cur_stack().pop()?;
|
|
|
|
RefCell::borrow_mut(&heap)[index as usize] = value;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "bonus.heap")]
|
|
fn iaload(machine: &mut Machine) -> Result<()> {
|
|
let heap = match machine.cur_stack().pop()? {
|
|
Value::HeapRef(a) => a,
|
|
_ => return Err("Cannot use int as heapref"),
|
|
};
|
|
let index: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
|
|
let value: Value;
|
|
{
|
|
value = heap.borrow()[index as usize].clone();
|
|
}
|
|
machine.cur_stack().push(value);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "bonus.heap")]
|
|
fn gc(machine: &mut Machine) -> Result<()> {
|
|
machine.heap.gc();
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "extra.arithmetic")]
|
|
fn shl(machine: &mut Machine) -> Result<()> {
|
|
let shift: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let value: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
|
|
machine.cur_stack().push(Value::Int(value << shift));
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "extra.arithmetic")]
|
|
fn shr(machine: &mut Machine) -> Result<()> {
|
|
let shift: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let value: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
|
|
let result: u32 = (value as u32) >> (shift as u32);
|
|
machine.cur_stack().push(Value::Int(result as i32));
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "extra.arithmetic")]
|
|
fn imul(machine: &mut Machine) -> Result<()> {
|
|
let a: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let b: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
|
|
machine.cur_stack().push(Value::Int(a * b));
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "extra.arithmetic")]
|
|
fn idiv(machine: &mut Machine) -> Result<()> {
|
|
let divisor: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
let value: i32 = machine.cur_stack().pop()?.try_into()?;
|
|
|
|
machine.cur_stack().push(Value::Int(value / divisor));
|
|
Ok(())
|
|
}
|