Some things that were previously hard VM-level errors are now handled by interrupts. While this is relatively easy to handle, I was wanting a little more structure for the error types - so, errors that should invoke an interrupt are passed along in their own structure in a VmError::Interrupt variant. If an error is raised during the tick() phase of execution that would cause an interrupt, that interrupt is intercepted and the VM continues. The State::interrupt() function also will catch double faults and triple faults, with a triple fault being its own variant in the VmError structure (so it cannot be intercepted as an interrupt by accident). Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
317 lines
7.9 KiB
Rust
317 lines
7.9 KiB
Rust
use crate::{inst, interrupt::InterruptVector, reg::Reg};
|
|
|
|
pub type Ast = Vec<Directive>;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum Directive {
|
|
Meta(MetaSection),
|
|
Data(DataSection),
|
|
Include(String),
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct MetaSection {
|
|
pub lines: Vec<MetaLine>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct MetaLine {
|
|
pub name: String,
|
|
pub value: Value,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DataSection {
|
|
pub name: String,
|
|
pub org: SectionOrg,
|
|
pub blocks: Vec<AlignedBlock>,
|
|
}
|
|
|
|
impl DataSection {
|
|
pub fn len(&self) -> usize {
|
|
let start = self.org.start();
|
|
let mut end = start;
|
|
for (pos, _) in self.lines(start) {
|
|
end = pos as u64;
|
|
}
|
|
(end - start) as usize
|
|
}
|
|
|
|
pub fn lines<'a>(&'a self, start: u64) -> DataLines<'a> {
|
|
DataLines::new(&self.blocks, start)
|
|
}
|
|
}
|
|
|
|
pub struct DataLines<'a> {
|
|
blocks: &'a Vec<AlignedBlock>,
|
|
block_idx: usize,
|
|
line_idx: usize,
|
|
pos: usize,
|
|
}
|
|
|
|
impl<'a> DataLines<'a> {
|
|
fn new(blocks: &'a Vec<AlignedBlock>, start: u64) -> Self {
|
|
DataLines {
|
|
blocks,
|
|
block_idx: 0,
|
|
line_idx: 0,
|
|
pos: start as usize,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for DataLines<'a> {
|
|
type Item = (usize, &'a DataLine);
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.block_idx >= self.blocks.len() {
|
|
return None;
|
|
}
|
|
let block = &self.blocks[self.block_idx];
|
|
if self.line_idx >= block.block.len() {
|
|
// next block - advance the position by the padding amount
|
|
self.block_idx += 1;
|
|
self.line_idx = 0;
|
|
// if there's a next block, update the padding
|
|
if self.block_idx < self.blocks.len() {
|
|
let block = &self.blocks[self.block_idx];
|
|
self.pos += block.padding_for(self.pos);
|
|
}
|
|
self.next()
|
|
} else {
|
|
let pos = self.pos;
|
|
let line = &block.block[self.line_idx];
|
|
self.line_idx += 1;
|
|
self.pos += line.len();
|
|
Some((pos, line))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum SectionOrg {
|
|
Start(u64),
|
|
StartEnd(u64, u64),
|
|
}
|
|
|
|
impl SectionOrg {
|
|
pub fn start(&self) -> u64 {
|
|
match self {
|
|
SectionOrg::Start(start) | SectionOrg::StartEnd(start, _) => *start,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct AlignedBlock {
|
|
pub alignment: IntSize,
|
|
pub block: Vec<DataLine>,
|
|
}
|
|
|
|
impl AlignedBlock {
|
|
pub fn padding_for(&self, len: usize) -> usize {
|
|
let align = self.alignment.len();
|
|
let padding = len % align;
|
|
if padding == 0 {
|
|
padding
|
|
} else {
|
|
align - padding
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum DataLine {
|
|
ValueDef(ValueDef),
|
|
Inst(Inst),
|
|
Export(String),
|
|
Label(String),
|
|
}
|
|
|
|
impl DataLine {
|
|
pub fn len(&self) -> usize {
|
|
match self {
|
|
DataLine::ValueDef(v) => v.len(),
|
|
DataLine::Inst(i) => i.len(),
|
|
DataLine::Export(_) | DataLine::Label(_) => 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum ValueDef {
|
|
Int(u64, IntSize),
|
|
String(String),
|
|
ZString(String),
|
|
Interrupt(bool, Value),
|
|
}
|
|
|
|
impl ValueDef {
|
|
pub fn len(&self) -> usize {
|
|
match self {
|
|
ValueDef::Int(_, s) => s.len(),
|
|
ValueDef::String(s) => 8 + s.as_bytes().len(),
|
|
ValueDef::ZString(s) => s.as_bytes().len() + 1,
|
|
ValueDef::Interrupt(_, _) => std::mem::size_of::<InterruptVector>(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum Value {
|
|
// TODO : immediate int sizes
|
|
// Int(u64, IntSize)
|
|
Int(u64),
|
|
Reg(Reg),
|
|
Name(String),
|
|
Here,
|
|
Addr(Box<Value>, IntSize),
|
|
}
|
|
|
|
impl Value {
|
|
pub fn len(&self) -> usize {
|
|
match self {
|
|
Value::Int(_) => 8,
|
|
Value::Reg(_) => 1,
|
|
Value::Name(_) => 8,
|
|
Value::Here => 8,
|
|
Value::Addr(v, _) => v.len(),
|
|
}
|
|
}
|
|
|
|
pub fn dest_encoding(&self) -> Option<u8> {
|
|
match self {
|
|
Value::Int(_) | Value::Name(_) | Value::Here => None,
|
|
Value::Reg(_) => Some(inst::DEST_REG),
|
|
// TODO : check reg vs int value, and use dest_reg_addr8/16/32/64 values
|
|
Value::Addr(v, size) => {
|
|
if let Value::Reg(_) = &**v {
|
|
match size {
|
|
IntSize::U64 => Some(inst::DEST_REG_ADDR64),
|
|
IntSize::U32 => Some(inst::DEST_REG_ADDR32),
|
|
IntSize::U16 => Some(inst::DEST_REG_ADDR16),
|
|
IntSize::U8 => Some(inst::DEST_REG_ADDR8),
|
|
}
|
|
} else {
|
|
match size {
|
|
IntSize::U64 => Some(inst::DEST_ADDR64),
|
|
IntSize::U32 => Some(inst::DEST_ADDR32),
|
|
IntSize::U16 => Some(inst::DEST_ADDR16),
|
|
IntSize::U8 => Some(inst::DEST_ADDR8),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn source_encoding(&self) -> u8 {
|
|
match self {
|
|
Value::Int(_) => inst::SOURCE_IMM64,
|
|
Value::Reg(_) => inst::SOURCE_REG,
|
|
// TODO : check reg vs int value, and use source_reg_addr8/16/32/64 values
|
|
Value::Name(_) | Value::Here => inst::SOURCE_IMM64,
|
|
Value::Addr(v, size) => {
|
|
if let Value::Reg(_) = &**v {
|
|
match size {
|
|
IntSize::U64 => inst::SOURCE_REG_ADDR64,
|
|
IntSize::U32 => inst::SOURCE_REG_ADDR32,
|
|
IntSize::U16 => inst::SOURCE_REG_ADDR16,
|
|
IntSize::U8 => inst::SOURCE_REG_ADDR8,
|
|
}
|
|
} else {
|
|
match size {
|
|
IntSize::U64 => inst::SOURCE_ADDR64,
|
|
IntSize::U32 => inst::SOURCE_ADDR32,
|
|
IntSize::U16 => inst::SOURCE_ADDR16,
|
|
IntSize::U8 => inst::SOURCE_ADDR8,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum IntSize {
|
|
U8,
|
|
U16,
|
|
U32,
|
|
U64,
|
|
}
|
|
|
|
impl IntSize {
|
|
pub fn len(&self) -> usize {
|
|
match self {
|
|
IntSize::U8 => 1,
|
|
IntSize::U16 => 2,
|
|
IntSize::U32 => 4,
|
|
IntSize::U64 => 8,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum Inst {
|
|
Add(Value, Value),
|
|
Sub(Value, Value),
|
|
Mul(Value, Value),
|
|
Div(Value, Value),
|
|
IDiv(Value, Value),
|
|
Mod(Value, Value),
|
|
And(Value, Value),
|
|
Or(Value, Value),
|
|
Xor(Value, Value),
|
|
Shl(Value, Value),
|
|
Shr(Value, Value),
|
|
INeg(Value, Value),
|
|
Inv(Value, Value),
|
|
Not(Value, Value),
|
|
CmpEq(Value, Value),
|
|
CmpLt(Value, Value),
|
|
Jmp(Value),
|
|
Jz(Value),
|
|
Jnz(Value),
|
|
Call(Value),
|
|
Ret,
|
|
Push(Value),
|
|
Pop(Value),
|
|
Int(Value, Value),
|
|
IRet,
|
|
Mov(Value, Value),
|
|
Halt,
|
|
Nop,
|
|
Dump,
|
|
}
|
|
|
|
impl Inst {
|
|
pub fn len(&self) -> usize {
|
|
match self {
|
|
Inst::Add(v1, v2)
|
|
| Inst::Sub(v1, v2)
|
|
| Inst::Mul(v1, v2)
|
|
| Inst::Div(v1, v2)
|
|
| Inst::IDiv(v1, v2)
|
|
| Inst::Mod(v1, v2)
|
|
| Inst::And(v1, v2)
|
|
| Inst::Or(v1, v2)
|
|
| Inst::Xor(v1, v2)
|
|
| Inst::Shl(v1, v2)
|
|
| Inst::Shr(v1, v2)
|
|
| Inst::INeg(v1, v2)
|
|
| Inst::Inv(v1, v2)
|
|
| 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)
|
|
| Inst::Jnz(v)
|
|
| Inst::Call(v)
|
|
| Inst::Push(v)
|
|
| Inst::Pop(v) => 3 + v.len(),
|
|
Inst::Ret | Inst::IRet | Inst::Halt | Inst::Nop | Inst::Dump => 2,
|
|
}
|
|
}
|
|
}
|