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>;
|
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
Reference in New Issue
Block a user