implement call, ret, far jumps, add, sub
This commit is contained in:
@@ -50,6 +50,12 @@ impl From<u64> for Addr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<usize> for Addr {
|
||||||
|
fn from(n: usize) -> Self {
|
||||||
|
Self(n as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Addr> for u64 {
|
impl From<Addr> for u64 {
|
||||||
fn from(addr: Addr) -> Self {
|
fn from(addr: Addr) -> Self {
|
||||||
addr.0
|
addr.0
|
||||||
@@ -92,7 +98,7 @@ impl From<String> for Label {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Routine name
|
/// Routine name
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct RoutineName(pub String);
|
pub struct RoutineName(pub String);
|
||||||
|
|
||||||
impl Display for RoutineName {
|
impl Display for RoutineName {
|
||||||
|
|||||||
+18
-2
@@ -53,10 +53,26 @@ pub enum Op {
|
|||||||
/// Compare two values and set conditional flags.
|
/// Compare two values and set conditional flags.
|
||||||
/// If the values are identical, evaluate if they are zero, positive, or negative (the "tst" op)
|
/// If the values are identical, evaluate if they are zero, positive, or negative (the "tst" op)
|
||||||
Cmp(Rd, Rd),
|
Cmp(Rd, Rd),
|
||||||
// Increment a value
|
|
||||||
|
// Arithmetic
|
||||||
Inc(Wr),
|
Inc(Wr),
|
||||||
// Decrement a value
|
|
||||||
Dec(Wr),
|
Dec(Wr),
|
||||||
|
Add(Wr, Rd),
|
||||||
|
Sub(Wr, Rd),
|
||||||
|
Mul(Wr, Rd),
|
||||||
|
Div(Wr, Rd),
|
||||||
|
Mod(Wr, /*rem*/Wr, Rd),
|
||||||
|
|
||||||
|
// /// Bitwise
|
||||||
|
And(Wr, Rd),
|
||||||
|
Or(Wr, Rd),
|
||||||
|
Xor(Wr, Rd),
|
||||||
|
Cpl(Wr),
|
||||||
|
Rol(Wr, Rd), // Rotate (with wrap-around)
|
||||||
|
Ror(Wr, Rd),
|
||||||
|
Lsl(Wr, Rd), // Shift
|
||||||
|
Lsr(Wr, Rd),
|
||||||
|
Asr(Wr, Rd),
|
||||||
|
|
||||||
// TODO arithmetics, bit manipulation, byte operations
|
// TODO arithmetics, bit manipulation, byte operations
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,104 @@ pub fn parse_op(keyword: &str, far : bool, mut arg_tokens: impl Iterator<Item=Se
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"add" => {
|
||||||
|
HLOp::L(Op::Add(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"sub" => {
|
||||||
|
HLOp::L(Op::Sub(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"mul" => {
|
||||||
|
HLOp::L(Op::Mul(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"div" => {
|
||||||
|
HLOp::L(Op::Div(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"mod" => {
|
||||||
|
HLOp::L(Op::Mod(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"and" => {
|
||||||
|
HLOp::L(Op::And(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"or" => {
|
||||||
|
HLOp::L(Op::Or(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"xor" => {
|
||||||
|
HLOp::L(Op::Xor(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"cpl" => {
|
||||||
|
HLOp::L(Op::Cpl(
|
||||||
|
parse_wr(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"rol" => {
|
||||||
|
HLOp::L(Op::Rol(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"ror" => {
|
||||||
|
HLOp::L(Op::Ror(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"lsl" | "asl" => {
|
||||||
|
HLOp::L(Op::Lsl(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"lsr" => {
|
||||||
|
HLOp::L(Op::Lsr(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
"asr" => {
|
||||||
|
HLOp::L(Op::Asr(
|
||||||
|
parse_wr(arg_tokens.next())?,
|
||||||
|
parse_rd(arg_tokens.next())?
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// TODO more instructions
|
// TODO more instructions
|
||||||
|
|
||||||
other => {
|
other => {
|
||||||
|
|||||||
+19
-1
@@ -10,7 +10,7 @@ fn main() {
|
|||||||
SimpleLogger::new().init().unwrap();
|
SimpleLogger::new().init().unwrap();
|
||||||
|
|
||||||
// ;(dec r0 (z? (ret)))
|
// ;(dec r0 (z? (ret)))
|
||||||
let program = "
|
/*let program = "
|
||||||
(
|
(
|
||||||
(main
|
(main
|
||||||
(ld r0 2)
|
(ld r0 2)
|
||||||
@@ -20,6 +20,24 @@ fn main() {
|
|||||||
(fault \"that's it\")
|
(fault \"that's it\")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
";*/
|
||||||
|
|
||||||
|
let program = "
|
||||||
|
(
|
||||||
|
(main
|
||||||
|
(ld r0 2)
|
||||||
|
(call add2x r0 15)
|
||||||
|
(ld r0 res0)
|
||||||
|
(fault \"that's it\")
|
||||||
|
)
|
||||||
|
(add2x
|
||||||
|
(ld r0 arg0)
|
||||||
|
(ld r1 arg1)
|
||||||
|
(add r0 r1)
|
||||||
|
(add r0 r1)
|
||||||
|
(ret r0)
|
||||||
|
)
|
||||||
|
)
|
||||||
";
|
";
|
||||||
|
|
||||||
let parsed = asm::assemble(program).unwrap();
|
let parsed = asm::assemble(program).unwrap();
|
||||||
|
|||||||
+118
-4
@@ -2,7 +2,7 @@ use crate::run_thread::{ThreadToken, RunThread};
|
|||||||
use asm::instr::{Op, Cond};
|
use asm::instr::{Op, Cond};
|
||||||
use crate::fault::Fault;
|
use crate::fault::Fault;
|
||||||
use crate::frame::StackFrame;
|
use crate::frame::StackFrame;
|
||||||
use asm::data::literal::{Value, is_positive, is_negative};
|
use asm::data::literal::{Value, is_positive, is_negative, Addr};
|
||||||
|
|
||||||
pub type CyclesSpent = usize;
|
pub type CyclesSpent = usize;
|
||||||
|
|
||||||
@@ -40,21 +40,73 @@ impl RunThread {
|
|||||||
msg: msg.clone().unwrap_or_else(|| "No msg".into())
|
msg: msg.clone().unwrap_or_else(|| "No msg".into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Op::FarJump(_) => unimplemented!(),
|
Op::FarJump(name) => {
|
||||||
Op::Call(_, _) => unimplemented!(),
|
debug!("Far jump to {}", name);
|
||||||
Op::Ret(_) => unimplemented!(),
|
match self.program.find_far_label(name) {
|
||||||
|
Ok(pos) => {
|
||||||
|
debug!("label is at {}", pos);
|
||||||
|
self.frame.pc = pos;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Op::Call(name, args) => {
|
||||||
|
debug!("Call routine {}", name);
|
||||||
|
match self.program.find_routine(name) {
|
||||||
|
Ok(pos) => {
|
||||||
|
debug!("routine is at {}", pos);
|
||||||
|
|
||||||
|
let mut values = Vec::with_capacity(args.len());
|
||||||
|
for arg in args {
|
||||||
|
values.push(self.frame.read(*arg)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut frame2 = StackFrame::new(pos, &values);
|
||||||
|
std::mem::swap(&mut self.frame, &mut frame2);
|
||||||
|
self.call_stack.push(frame2);
|
||||||
|
advance = 0;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Op::Ret(retvals) => {
|
||||||
|
match self.call_stack.pop() {
|
||||||
|
Some(previous) => {
|
||||||
|
let mut values = Vec::with_capacity(retvals.len());
|
||||||
|
for arg in retvals {
|
||||||
|
values.push(frame.read(*arg)?);
|
||||||
|
}
|
||||||
|
*frame = previous;
|
||||||
|
frame.set_retvals(&values);
|
||||||
|
advance = 1; // advance past the call
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(Fault::CallStackUnderflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Op::Skip(val) => {
|
Op::Skip(val) => {
|
||||||
let steps = frame.read(*val)?;
|
let steps = frame.read(*val)?;
|
||||||
advance = i64::from_ne_bytes(steps.to_ne_bytes());
|
advance = i64::from_ne_bytes(steps.to_ne_bytes());
|
||||||
|
self.program.validate_jump(frame.pc, Addr((frame.pc.0 as i64 + advance) as u64))?;
|
||||||
}
|
}
|
||||||
Op::SkipIf(cond, val) => {
|
Op::SkipIf(cond, val) => {
|
||||||
if frame.status.test(*cond) {
|
if frame.status.test(*cond) {
|
||||||
let steps = frame.read(*val)?;
|
let steps = frame.read(*val)?;
|
||||||
advance = i64::from_ne_bytes(steps.to_ne_bytes());
|
advance = i64::from_ne_bytes(steps.to_ne_bytes());
|
||||||
|
self.program.validate_jump(frame.pc, Addr((frame.pc.0 as i64 + advance) as u64))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Op::Mov(dst, src) => {
|
Op::Mov(dst, src) => {
|
||||||
|
frame.status.clear();
|
||||||
let val = frame.read(*src)?;
|
let val = frame.read(*src)?;
|
||||||
|
frame.status.zero = (val == 0);
|
||||||
|
frame.status.positive = is_positive(val);
|
||||||
|
frame.status.negative = is_negative(val);
|
||||||
frame.write(*dst, val)?;
|
frame.write(*dst, val)?;
|
||||||
}
|
}
|
||||||
Op::Cmp(a, b) => {
|
Op::Cmp(a, b) => {
|
||||||
@@ -88,6 +140,68 @@ impl RunThread {
|
|||||||
frame.status.negative = is_negative(val);
|
frame.status.negative = is_negative(val);
|
||||||
frame.write(*reg, val)?;
|
frame.write(*reg, val)?;
|
||||||
}
|
}
|
||||||
|
Op::Add(dst, src) => {
|
||||||
|
frame.status.clear();
|
||||||
|
let mut a = frame.read(dst.as_rd())?;
|
||||||
|
let mut b = frame.read(*src)?;
|
||||||
|
|
||||||
|
let val = a.wrapping_add(b);
|
||||||
|
frame.status.zero = (val == 0);
|
||||||
|
frame.status.positive = is_positive(val);
|
||||||
|
frame.status.negative = is_negative(val);
|
||||||
|
frame.status.overflow = b > Value::MAX - a; // TODO check
|
||||||
|
// TODO carry?
|
||||||
|
frame.write(*dst, val)?;
|
||||||
|
}
|
||||||
|
Op::Sub(dst, src) => {
|
||||||
|
frame.status.clear();
|
||||||
|
let mut a = frame.read(dst.as_rd())?;
|
||||||
|
let mut b = frame.read(*src)?;
|
||||||
|
|
||||||
|
let val = a.wrapping_sub(b);
|
||||||
|
frame.status.zero = (val == 0);
|
||||||
|
frame.status.positive = is_positive(val);
|
||||||
|
frame.status.negative = is_negative(val);
|
||||||
|
frame.status.overflow = b > a;
|
||||||
|
// TODO carry?
|
||||||
|
frame.write(*dst, val)?;
|
||||||
|
}
|
||||||
|
Op::Mul(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Div(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Mod(_, _, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::And(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Or(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Xor(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Cpl(_) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Rol(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Ror(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Lsl(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Lsr(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
Op::Asr(_, _) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EvalRes {
|
Ok(EvalRes {
|
||||||
|
|||||||
+19
-1
@@ -2,7 +2,7 @@ use thiserror::Error;
|
|||||||
use super::span::MemorySpan;
|
use super::span::MemorySpan;
|
||||||
use crate::run_thread::ThreadToken;
|
use crate::run_thread::ThreadToken;
|
||||||
use crate::mlock::ClaimId;
|
use crate::mlock::ClaimId;
|
||||||
use asm::data::literal::DebugMsg;
|
use asm::data::literal::{DebugMsg, RoutineName, Label};
|
||||||
use asm::data::Register;
|
use asm::data::Register;
|
||||||
|
|
||||||
#[derive(Error,Debug)]
|
#[derive(Error,Debug)]
|
||||||
@@ -44,6 +44,24 @@ pub enum Fault {
|
|||||||
RegisterNotWritable {
|
RegisterNotWritable {
|
||||||
reg: Register,
|
reg: Register,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("Called undefined routine: {routine}")]
|
||||||
|
NoSuchRoutine {
|
||||||
|
routine: RoutineName,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Jump to undefined far label: {label}")]
|
||||||
|
NoSuchFarLabel {
|
||||||
|
label: Label,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Crossed a barrier: {msg}")]
|
||||||
|
JumpThroughBarrier {
|
||||||
|
msg: DebugMsg,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Call stack underflow")]
|
||||||
|
CallStackUnderflow,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error,Debug)]
|
#[derive(Error,Debug)]
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ impl StackFrame {
|
|||||||
sf
|
sf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_retvals(&mut self, vals: &[Value]) {
|
||||||
|
for n in 0..(vals.len().min(REG_COUNT)) {
|
||||||
|
self.res[n] = vals[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, rd: Rd) -> Result<u64, Fault> {
|
pub fn read(&mut self, rd: Rd) -> Result<u64, Fault> {
|
||||||
match rd.d() {
|
match rd.d() {
|
||||||
SrcDisp::Immediate(v) => Ok(v),
|
SrcDisp::Immediate(v) => Ok(v),
|
||||||
|
|||||||
+65
-4
@@ -1,18 +1,47 @@
|
|||||||
use asm::instr::Op;
|
use asm::instr::{Op};
|
||||||
use asm::data::literal::Addr;
|
use asm::data::literal::{Addr, RoutineName, Label};
|
||||||
|
use crate::fault::Fault;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
ops: Vec<Op>,
|
ops: Vec<Op>,
|
||||||
|
routines: HashMap<RoutineName, Addr>,
|
||||||
|
far_labels: HashMap<Label, Addr>,
|
||||||
|
barriers: Vec<Addr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
pub fn new(ops : Vec<Op>) -> Self {
|
pub fn new(ops: Vec<Op>) -> Self {
|
||||||
Self {
|
let mut p = Self {
|
||||||
ops,
|
ops,
|
||||||
|
routines: Default::default(),
|
||||||
|
far_labels: Default::default(),
|
||||||
|
barriers: Default::default(),
|
||||||
|
};
|
||||||
|
p.scan();
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find all the named things
|
||||||
|
fn scan(&mut self) {
|
||||||
|
for (pos, op) in self.ops.iter().enumerate() {
|
||||||
|
match op {
|
||||||
|
Op::FarLabel(name) => {
|
||||||
|
self.far_labels.insert(name.clone(), pos.into());
|
||||||
|
}
|
||||||
|
Op::Routine(name) => {
|
||||||
|
self.routines.insert(name.clone(), pos.into());
|
||||||
|
}
|
||||||
|
Op::Barrier(_) => {
|
||||||
|
self.barriers.push(pos.into());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a program instruction at address
|
||||||
pub fn read(&self, addr: Addr) -> &Op {
|
pub fn read(&self, addr: Addr) -> &Op {
|
||||||
if addr.0 >= self.ops.len() as u64 {
|
if addr.0 >= self.ops.len() as u64 {
|
||||||
&Op::Nop
|
&Op::Nop
|
||||||
@@ -20,5 +49,37 @@ impl Program {
|
|||||||
&self.ops[addr.0 as usize]
|
&self.ops[addr.0 as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return error if any barrier is crossed during a jump / skip
|
||||||
|
pub fn validate_jump(&self, mut from: Addr, mut to: Addr) -> Result<(), Fault> {
|
||||||
|
if from > to {
|
||||||
|
std::mem::swap(&mut from, &mut to);
|
||||||
|
}
|
||||||
|
|
||||||
|
for b in &self.barriers {
|
||||||
|
if *b >= from && *b <= to {
|
||||||
|
if let Op::Barrier(msg) = self.read(*b) {
|
||||||
|
return Err(Fault::JumpThroughBarrier {
|
||||||
|
msg: msg.clone().unwrap_or("No msg".into())
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find routine by name
|
||||||
|
pub fn find_routine(&self, name: &RoutineName) -> Result<Addr, Fault> {
|
||||||
|
self.routines.get(name).copied()
|
||||||
|
.ok_or_else(|| Fault::NoSuchRoutine { routine: name.clone() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find far label by name
|
||||||
|
pub fn find_far_label(&self, name: &Label) -> Result<Addr, Fault> {
|
||||||
|
self.far_labels.get(name).copied()
|
||||||
|
.ok_or_else(|| Fault::NoSuchFarLabel { label: name.clone() })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user