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>; type Out = Vec<u8>;
fn assemble(&self, session: &mut AsmSession) -> Result<Self::Out> { 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(); let len = self.len();
macro_rules! map_inst {
($op:expr, $dest:expr, $source:expr) => {{
let mut bytes = Vec::with_capacity(len); let mut bytes = Vec::with_capacity(len);
bytes.write_u16::<LE>($op).unwrap(); bytes.write_u16::<LE>(inst::POP).unwrap();
let dest = $dest; 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 = let dest_encoding =
dest.dest_encoding() dest.dest_encoding()
.ok_or_else(|| AsmError::IllegalDestValue { .ok_or_else(|| AsmError::IllegalDestValue {
value: dest.clone(), value: dest.clone(),
})?; })?;
let source = $source;
let source_encoding = source.source_encoding(); let source_encoding = source.source_encoding();
bytes bytes
.write_u8((dest_encoding << 4) | source_encoding) .write_u8((dest_encoding << 4) | source_encoding)
@@ -197,92 +240,50 @@ impl Asm for Inst {
bytes.extend(dest.assemble(session)?); bytes.extend(dest.assemble(session)?);
bytes.extend(source.assemble(session)?); bytes.extend(source.assemble(session)?);
assert_eq!( assert_eq!(
self.len(), len,
bytes.len(), bytes.len(),
"instruction size mismatch in {} instruction - {:?} produces these bytes {:?}",
stringify!($op),
self,
bytes
); );
Ok(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); let mut bytes = Vec::with_capacity(len);
bytes.write_u16::<LE>($op).unwrap(); bytes.write_u16::<LE>(op).unwrap();
let source = $source; 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; let source_encoding = source.source_encoding() << 4;
bytes.write_u8(source_encoding).unwrap(); bytes.write_u8(source_encoding).unwrap();
bytes.extend(source.assemble(session)?); bytes.extend(source.assemble(session)?);
assert_eq!( assert_eq!(len, bytes.len());
self.len(),
bytes.len(),
"instruction size mismatch in {} instruction - {:?} produces these bytes {:?}",
stringify!($op),
self,
bytes
);
Ok(bytes) 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); let mut bytes = Vec::with_capacity(len);
bytes.write_u16::<LE>($op).unwrap(); bytes.write_u16::<LE>(op).unwrap();
assert_eq!( assert_eq!(
self.len(), len,
bytes.len(), bytes.len(),
"instruction size mismatch in {} instruction - {:?} produces these bytes {:?}",
stringify!($op),
self,
bytes
); );
Ok(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, 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 }