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

View File

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

View File

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

View File

@@ -105,6 +105,8 @@ Inst -> Inst:
| 'RET' { Inst::Ret } | 'RET' { Inst::Ret }
| 'PUSH' Value { Inst::Push($2) } | 'PUSH' Value { Inst::Push($2) }
| 'POP' Value { Inst::Pop($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) } | 'MOV' Value 'COMMA' Value { Inst::Mov($2, $4) }
| 'HALT' { Inst::Halt } | 'HALT' { Inst::Halt }
| 'NOP' { Inst::Nop } | 'NOP' { Inst::Nop }