Files
rasp/src/libvm/src/obj/syn/ast.rs
Alek Ratzloff bff9220fb1 Extend how interrupts are reported to the main execution loop
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>
2020-03-12 16:56:20 -04:00

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,
}
}
}