Reorganize instruction assembler logic (fixing a known bug), add interrupts to frontend

* Instruction assembler used to use macros for handling source,
  dest-source, source-source, etc instructions. It did not have a
  source-source implementation, however, so the two source-source
  instructions (cmpeq and cmplt) were treated as dest-source. This has
  been refactored into some local methods that handle this now.
* Syntax for interrupts and interrupt returns are implemented and appear
  to be working

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-03-06 12:53:07 -05:00
parent fbe2c529af
commit 1f93732a7c
4 changed files with 102 additions and 93 deletions

View File

@@ -177,19 +177,62 @@ impl Asm for Inst {
type Out = Vec<u8>;
fn assemble(&self, session: &mut AsmSession) -> Result<Self::Out> {
match self {
Inst::Add(v1, v2) => self.map_dest_source(session, inst::ADD, v1, v2),
Inst::Sub(v1, v2) => self.map_dest_source(session, inst::SUB, v1, v2),
Inst::Mul(v1, v2) => self.map_dest_source(session, inst::MUL, v1, v2),
Inst::Div(v1, v2) => self.map_dest_source(session, inst::DIV, v1, v2),
Inst::IDiv(v1, v2) => self.map_dest_source(session, inst::IDIV, v1, v2),
Inst::Mod(v1, v2) => self.map_dest_source(session, inst::MOD, v1, v2),
Inst::And(v1, v2) => self.map_dest_source(session, inst::AND, v1, v2),
Inst::Or(v1, v2) => self.map_dest_source(session, inst::OR, v1, v2),
Inst::Xor(v1, v2) => self.map_dest_source(session, inst::XOR, v1, v2),
Inst::Shl(v1, v2) => self.map_dest_source(session, inst::SHL, v1, v2),
Inst::Shr(v1, v2) => self.map_dest_source(session, inst::SHR, v1, v2),
Inst::INeg(v1, v2) => self.map_dest_source(session, inst::INEG, v1, v2),
Inst::Inv(v1, v2) => self.map_dest_source(session, inst::INV, v1, v2),
Inst::Not(v1, v2) => self.map_dest_source(session, inst::NOT, v1, v2),
Inst::CmpEq(v1, v2) => self.map_source_source(session, inst::CMPEQ, v1, v2),
Inst::CmpLt(v1, v2) => self.map_source_source(session, inst::CMPLT, v1, v2),
Inst::Jmp(v) => self.map_source(session, inst::JMP, v),
Inst::Jz(v) => self.map_source(session, inst::JZ, v),
Inst::Jnz(v) => self.map_source(session, inst::JNZ, v),
Inst::Call(v) => self.map_source(session, inst::CALL, v),
Inst::Ret => self.map_inst(inst::RET),
Inst::Push(v) => self.map_source(session, inst::PUSH, v),
Inst::Pop(dest) => {
let len = self.len();
macro_rules! map_inst {
($op:expr, $dest:expr, $source:expr) => {{
let mut bytes = Vec::with_capacity(len);
bytes.write_u16::<LE>($op).unwrap();
let dest = $dest;
bytes.write_u16::<LE>(inst::POP).unwrap();
let dest_encoding = dest.source_encoding() << 4;
bytes.write_u8(dest_encoding).unwrap();
bytes.extend(dest.assemble(session)?);
assert_eq!(
len,
bytes.len(),
);
Ok(bytes)
}
Inst::Int(v1, v2) => self.map_source_source(session, inst::INT, v1, v2),
Inst::IRet => self.map_inst(inst::IRET),
Inst::Mov(v1, v2) => self.map_dest_source(session, inst::MOV, v1, v2),
Inst::Halt => self.map_inst(inst::HALT),
Inst::Nop => self.map_inst(inst::NOP),
Inst::Dump => self.map_inst(inst::DUMP),
}
}
}
impl Inst {
fn map_dest_source(&self, session: &mut AsmSession, op: inst::InstOp, dest: &Value, source: &Value) -> Result<Vec<u8>> {
let len = self.len();
let mut bytes = Vec::with_capacity(len);
bytes.write_u16::<LE>(op).unwrap();
let dest_encoding =
dest.dest_encoding()
.ok_or_else(|| AsmError::IllegalDestValue {
value: dest.clone(),
})?;
let source = $source;
let source_encoding = source.source_encoding();
bytes
.write_u8((dest_encoding << 4) | source_encoding)
@@ -197,92 +240,50 @@ impl Asm for Inst {
bytes.extend(dest.assemble(session)?);
bytes.extend(source.assemble(session)?);
assert_eq!(
self.len(),
len,
bytes.len(),
"instruction size mismatch in {} instruction - {:?} produces these bytes {:?}",
stringify!($op),
self,
bytes
);
Ok(bytes)
}};
}
($op:expr, $source:expr) => {{
fn map_source_source(&self, session: &mut AsmSession, op: inst::InstOp, s1: &Value, s2: &Value) -> Result<Vec<u8>> {
let len = self.len();
let mut bytes = Vec::with_capacity(len);
bytes.write_u16::<LE>($op).unwrap();
let source = $source;
bytes.write_u16::<LE>(op).unwrap();
let s1_encoding = s1.source_encoding();
let s2_encoding = s2.source_encoding();
bytes
.write_u8((s1_encoding << 4) | s2_encoding)
.unwrap();
bytes.extend(s1.assemble(session)?);
bytes.extend(s2.assemble(session)?);
assert_eq!(
len,
bytes.len(),
);
Ok(bytes)
}
fn map_source(&self, session: &mut AsmSession, op: inst::InstOp, source: &Value) -> Result<Vec<u8>> {
let len = self.len();
let mut bytes = Vec::with_capacity(len);
bytes.write_u16::<LE>(op).unwrap();
let source_encoding = source.source_encoding() << 4;
bytes.write_u8(source_encoding).unwrap();
bytes.extend(source.assemble(session)?);
assert_eq!(
self.len(),
bytes.len(),
"instruction size mismatch in {} instruction - {:?} produces these bytes {:?}",
stringify!($op),
self,
bytes
);
assert_eq!(len, bytes.len());
Ok(bytes)
}};
}
($op:expr) => {{
fn map_inst(&self, op: inst::InstOp) -> Result<Vec<u8>> {
let len = self.len();
let mut bytes = Vec::with_capacity(len);
bytes.write_u16::<LE>($op).unwrap();
bytes.write_u16::<LE>(op).unwrap();
assert_eq!(
self.len(),
len,
bytes.len(),
"instruction size mismatch in {} instruction - {:?} produces these bytes {:?}",
stringify!($op),
self,
bytes
);
Ok(bytes)
}};
}
match self {
Inst::Add(v1, v2) => map_inst!(inst::ADD, v1, v2),
Inst::Sub(v1, v2) => map_inst!(inst::SUB, v1, v2),
Inst::Mul(v1, v2) => map_inst!(inst::MUL, v1, v2),
Inst::Div(v1, v2) => map_inst!(inst::DIV, v1, v2),
Inst::IDiv(v1, v2) => map_inst!(inst::IDIV, v1, v2),
Inst::Mod(v1, v2) => map_inst!(inst::MOD, v1, v2),
Inst::And(v1, v2) => map_inst!(inst::AND, v1, v2),
Inst::Or(v1, v2) => map_inst!(inst::OR, v1, v2),
Inst::Xor(v1, v2) => map_inst!(inst::XOR, v1, v2),
Inst::Shl(v1, v2) => map_inst!(inst::SHL, v1, v2),
Inst::Shr(v1, v2) => map_inst!(inst::SHR, v1, v2),
Inst::INeg(v1, v2) => map_inst!(inst::INEG, v1, v2),
Inst::Inv(v1, v2) => map_inst!(inst::INV, v1, v2),
Inst::Not(v1, v2) => map_inst!(inst::NOT, v1, v2),
// TODO/BUG: CmpEq and CmpLt both take two sources instead of a source and destination
Inst::CmpEq(v1, v2) => map_inst!(inst::CMPEQ, v1, v2),
Inst::CmpLt(v1, v2) => map_inst!(inst::CMPLT, v1, v2),
Inst::Jmp(v) => map_inst!(inst::JMP, v),
Inst::Jz(v) => map_inst!(inst::JZ, v),
Inst::Jnz(v) => map_inst!(inst::JNZ, v),
Inst::Call(v) => map_inst!(inst::CALL, v),
Inst::Ret => map_inst!(inst::RET),
Inst::Push(v) => map_inst!(inst::PUSH, v),
Inst::Pop(dest) => {
let mut bytes = Vec::with_capacity(len);
bytes.write_u16::<LE>(inst::POP).unwrap();
let dest_encoding = dest.source_encoding() << 4;
bytes.write_u8(dest_encoding).unwrap();
bytes.extend(dest.assemble(session)?);
assert_eq!(
self.len(),
bytes.len(),
"instruction size mismatch in inst::PUSH instruction - {:?} produces these bytes {:?}",
self,
bytes
);
Ok(bytes)
}
Inst::Mov(v1, v2) => map_inst!(inst::MOV, v1, v2),
Inst::Halt => map_inst!(inst::HALT),
Inst::Nop => map_inst!(inst::NOP),
Inst::Dump => map_inst!(inst::DUMP),
}
}
}

View File

@@ -217,6 +217,8 @@ pub enum Inst {
Ret,
Push(Value),
Pop(Value),
Int(Value, Value),
IRet,
Mov(Value, Value),
Halt,
Nop,
@@ -242,6 +244,7 @@ impl Inst {
| Inst::Not(v1, v2)
| Inst::CmpEq(v1, v2)
| Inst::CmpLt(v1, v2)
| Inst::Int(v1, v2)
| Inst::Mov(v1, v2) => { 3 + v1.len() + v2.len() }
Inst::Jmp(v)
| Inst::Jz(v)
@@ -250,6 +253,7 @@ impl Inst {
| Inst::Push(v)
| Inst::Pop(v) => { 3 + v.len() }
Inst::Ret
| Inst::IRet
| Inst::Halt
| Inst::Nop
| Inst::Dump => { 2 }

View File

@@ -51,6 +51,8 @@ call "CALL"
ret "RET"
push "PUSH"
pop "POP"
int "INT"
iret "IRET"
mov "MOV"
halt "HALT"
nop "NOP"

View File

@@ -105,6 +105,8 @@ Inst -> Inst:
| 'RET' { Inst::Ret }
| 'PUSH' Value { Inst::Push($2) }
| 'POP' Value { Inst::Pop($2) }
| 'INT' Value 'COMMA' Value { Inst::Int($2, $4) }
| 'IRET' { Inst::IRet }
| 'MOV' Value 'COMMA' Value { Inst::Mov($2, $4) }
| 'HALT' { Inst::Halt }
| 'NOP' { Inst::Nop }