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:
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -51,6 +51,8 @@ call "CALL"
|
||||
ret "RET"
|
||||
push "PUSH"
|
||||
pop "POP"
|
||||
int "INT"
|
||||
iret "IRET"
|
||||
mov "MOV"
|
||||
halt "HALT"
|
||||
nop "NOP"
|
||||
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user