Big refactoring, invalid assembly is now unrepresentable assembly
This commit is contained in:
+38
-107
@@ -1,12 +1,10 @@
|
|||||||
use std::convert::TryFrom;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use pest::iterators::Pair;
|
use pest::iterators::Pair;
|
||||||
|
|
||||||
use vcore::opcodes::Opcode;
|
use vcore::opcodes::Opcode;
|
||||||
|
|
||||||
use crate::parse_error::ParseError;
|
use crate::vasm_parser::Rule;
|
||||||
use crate::vasm_parser::{Rule, VASMParser};
|
|
||||||
|
|
||||||
/// A non-opcode directive to the assembler
|
/// A non-opcode directive to the assembler
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
@@ -28,26 +26,6 @@ impl From<Pair<'_, Rule>> for Directive {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Either an opcode or a directive; something that lives in the
|
|
||||||
/// instruction column of a line
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
|
||||||
pub enum Instruction {
|
|
||||||
Directive(Directive),
|
|
||||||
Opcode(Opcode),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TryFrom<Pair<'a, Rule>> for Instruction {
|
|
||||||
type Error = ParseError<'a>;
|
|
||||||
|
|
||||||
fn try_from(pair: Pair<'a, Rule>) -> Result<Self, Self::Error> {
|
|
||||||
match pair.as_rule() {
|
|
||||||
Rule::opcode => Ok(Instruction::Opcode(Opcode::try_from(pair.as_str())?)),
|
|
||||||
Rule::directive => Ok(Instruction::Directive(Directive::from(pair))),
|
|
||||||
_ => Err(ParseError::InvalidInstruction(pair.as_str())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// One of the five arithmetical operators
|
/// One of the five arithmetical operators
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
pub enum Operator {
|
pub enum Operator {
|
||||||
@@ -96,12 +74,12 @@ impl<'a> Node<'a> {
|
|||||||
for inner in pair.into_inner() {
|
for inner in pair.into_inner() {
|
||||||
let string_inner = inner.as_str();
|
let string_inner = inner.as_str();
|
||||||
match string_inner {
|
match string_inner {
|
||||||
"\\t" => string.push_str("\t"),
|
"\\t" => string.push('\t'),
|
||||||
"\\r" => string.push_str("\r"),
|
"\\r" => string.push('\r'),
|
||||||
"\\n" => string.push_str("\n"),
|
"\\n" => string.push('\n'),
|
||||||
"\\0" => string.push_str("\0"),
|
"\\0" => string.push('\0'),
|
||||||
"\\\\" => string.push_str("\\"),
|
"\\\\" => string.push('\\'),
|
||||||
"\\\"" => string.push_str("\""),
|
"\\\"" => string.push('\"'),
|
||||||
_ => string.push_str(string_inner),
|
_ => string.push_str(string_inner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,102 +103,55 @@ impl<'a> Node<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<Pair<'a, Rule>> for Node<'a> {
|
impl<'a> From<Pair<'a, Rule>> for Node<'a> {
|
||||||
type Error = ParseError<'a>;
|
/// Parse a given pair into the `Node` it represents. The result of this is a
|
||||||
|
|
||||||
/// Attempt to parse a given pair into the `Node` it represents. The result of this is a
|
|
||||||
/// `Node` that might contain a borrow of part of the original code, but which in no way
|
/// `Node` that might contain a borrow of part of the original code, but which in no way
|
||||||
/// depends on the original pair's object model.
|
/// depends on the original pair's object model.
|
||||||
fn try_from(pair: Pair<'a, Rule>) -> Result<Self, Self::Error> {
|
fn from(pair: Pair<'a, Rule>) -> Self {
|
||||||
match pair.as_rule() {
|
match pair.as_rule() {
|
||||||
Rule::expr | Rule::term => {
|
Rule::expr | Rule::term => {
|
||||||
let mut iter = pair.into_inner();
|
let mut iter = pair.into_inner();
|
||||||
let first = Node::try_from(iter.next().unwrap())?;
|
let first = Node::from(iter.next().unwrap());
|
||||||
let mut rest = Vec::<(Operator, Node)>::new();
|
let mut rest = Vec::<(Operator, Node)>::new();
|
||||||
|
|
||||||
while let Some(operator) = iter.next() {
|
while let Some(operator) = iter.next() {
|
||||||
let rhs = iter.next().unwrap();
|
let rhs = iter.next().unwrap();
|
||||||
let op = Operator::from(operator);
|
let op = Operator::from(operator);
|
||||||
let node = Node::try_from(rhs)?;
|
let node = Node::from(rhs);
|
||||||
rest.push((op, node));
|
rest.push((op, node));
|
||||||
}
|
}
|
||||||
Ok(Node::Expr(Box::from(first), rest))
|
Node::Expr(Box::from(first), rest)
|
||||||
}
|
}
|
||||||
Rule::fact | Rule::number => Ok(Node::try_from(pair.into_inner().next().unwrap())?),
|
Rule::fact | Rule::number => Node::from(pair.into_inner().next().unwrap()),
|
||||||
Rule::dec_number | Rule::dec_zero | Rule::neg_number => {
|
Rule::dec_number | Rule::dec_zero | Rule::neg_number => {
|
||||||
Ok(Node::Number(i32::from_str(pair.as_str()).unwrap()))
|
Node::Number(i32::from_str(pair.as_str()).unwrap())
|
||||||
}
|
}
|
||||||
Rule::hex_number => Ok(Node::Number(
|
Rule::hex_number => {
|
||||||
i32::from_str_radix(pair.as_str().get(2..).unwrap(), 16).unwrap(),
|
Node::Number(i32::from_str_radix(pair.as_str().get(2..).unwrap(), 16).unwrap())
|
||||||
)),
|
}
|
||||||
Rule::bin_number => Ok(Node::Number(
|
Rule::bin_number => {
|
||||||
i32::from_str_radix(pair.as_str().get(2..).unwrap(), 2).unwrap(),
|
Node::Number(i32::from_str_radix(pair.as_str().get(2..).unwrap(), 2).unwrap())
|
||||||
)),
|
}
|
||||||
Rule::oct_number => Ok(Node::Number(
|
Rule::oct_number => {
|
||||||
i32::from_str_radix(pair.as_str().get(2..).unwrap(), 8).unwrap(),
|
Node::Number(i32::from_str_radix(pair.as_str().get(2..).unwrap(), 8).unwrap())
|
||||||
)),
|
}
|
||||||
Rule::string => Ok(Node::string(pair)),
|
Rule::string => Node::string(pair),
|
||||||
Rule::label => Ok(Node::Label(pair.as_str())),
|
Rule::label => Node::Label(pair.as_str()),
|
||||||
Rule::relative_label => Ok(Node::RelativeLabel(pair.as_str().get(1..).unwrap())),
|
Rule::relative_label => Node::RelativeLabel(pair.as_str().get(1..).unwrap()),
|
||||||
Rule::absolute_line_offset | Rule::relative_line_offset => Ok(Node::line_offset(pair)),
|
Rule::absolute_line_offset | Rule::relative_line_offset => Node::line_offset(pair),
|
||||||
_ => todo!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct VASMLine<'a> {
|
pub struct Label<'a>(pub &'a str);
|
||||||
label: Option<&'a str>,
|
|
||||||
instruction: Option<Instruction>,
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
argument: Option<Node<'a>>,
|
pub enum VASMLine<'a> {
|
||||||
}
|
Instruction(Option<Label<'a>>, Opcode, Option<Node<'a>>),
|
||||||
|
Db(Option<Label<'a>>, Node<'a>),
|
||||||
impl<'a> VASMLine<'a> {
|
Org(Option<Label<'a>>, Node<'a>),
|
||||||
pub fn blank() -> VASMLine<'a> {
|
Equ(Label<'a>, Node<'a>),
|
||||||
VASMLine {
|
LabelDef(Label<'a>),
|
||||||
label: None,
|
|
||||||
instruction: None,
|
|
||||||
argument: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_instruction(self, instruction: Instruction) -> VASMLine<'a> {
|
|
||||||
VASMLine {
|
|
||||||
label: self.label,
|
|
||||||
instruction: Some(instruction),
|
|
||||||
argument: self.argument,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_opcode(self, opcode: Opcode) -> VASMLine<'a> {
|
|
||||||
VASMLine {
|
|
||||||
label: self.label,
|
|
||||||
instruction: Some(Instruction::Opcode(opcode)),
|
|
||||||
argument: self.argument,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_directive(self, directive: Directive) -> VASMLine<'a> {
|
|
||||||
VASMLine {
|
|
||||||
label: self.label,
|
|
||||||
instruction: Some(Instruction::Directive(directive)),
|
|
||||||
argument: self.argument,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_label(self, label: &'a str) -> Self {
|
|
||||||
VASMLine {
|
|
||||||
label: Some(label),
|
|
||||||
instruction: self.instruction,
|
|
||||||
argument: self.argument,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_argument(self, argument: Node<'a>) -> Self {
|
|
||||||
VASMLine {
|
|
||||||
label: self.label,
|
|
||||||
instruction: self.instruction,
|
|
||||||
argument: Some(argument),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
extern crate pest;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate pest_derive;
|
||||||
|
|
||||||
|
pub mod ast;
|
||||||
|
pub mod parse_error;
|
||||||
|
pub mod vasm_parser;
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
extern crate pest;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate pest_derive;
|
|
||||||
|
|
||||||
mod vasm_parser;
|
|
||||||
mod parse_error;
|
|
||||||
mod ast;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
||||||
@@ -17,10 +17,9 @@ impl<'a> From<InvalidMnemonic<'a>> for ParseError<'a> {
|
|||||||
impl<'a> Display for ParseError<'a> {
|
impl<'a> Display for ParseError<'a> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
use ParseError::*;
|
use ParseError::*;
|
||||||
use crate::parse_error::ParseError::LineParseFailure;
|
|
||||||
match self {
|
match self {
|
||||||
LineParseFailure => write!(f, "Failed to parse line"),
|
LineParseFailure => write!(f, "Failed to parse line"),
|
||||||
InvalidInstruction(p) => write!(f, "Cannot parse {} as instruction", p)
|
InvalidInstruction(p) => write!(f, "Cannot parse {} as instruction", p),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-16
@@ -24,6 +24,7 @@ number = { dec_number | hex_number | bin_number | oct_number | dec_zero | neg_nu
|
|||||||
// a digit:
|
// a digit:
|
||||||
label_char = { ASCII_ALPHA_LOWER | ASCII_ALPHA_UPPER | "_" }
|
label_char = { ASCII_ALPHA_LOWER | ASCII_ALPHA_UPPER | "_" }
|
||||||
label = @{ label_char ~ (label_char | ASCII_DIGIT | "$")* }
|
label = @{ label_char ~ (label_char | ASCII_DIGIT | "$")* }
|
||||||
|
label_def = { label ~ ":" }
|
||||||
|
|
||||||
// To make relative jumps easier, we'll also allow an '@' at the start of a label, and interpret
|
// To make relative jumps easier, we'll also allow an '@' at the start of a label, and interpret
|
||||||
// that as meaning "relative to the first byte of this instruction:"
|
// that as meaning "relative to the first byte of this instruction:"
|
||||||
@@ -35,13 +36,7 @@ absolute_line_offset = { "$" ~ sign ~ dec_number }
|
|||||||
// And the relative form of that, @+nnn and @-nnn:
|
// And the relative form of that, @+nnn and @-nnn:
|
||||||
relative_line_offset = { "@" ~ sign ~ dec_number }
|
relative_line_offset = { "@" ~ sign ~ dec_number }
|
||||||
|
|
||||||
opcode = @{ ASCII_ALPHA_LOWER+ } // TODO: replace with actual list
|
opcode = @{ ASCII_ALPHA_LOWER+ }
|
||||||
|
|
||||||
// The assembler will support some directives:
|
|
||||||
// - .org to set the current address
|
|
||||||
// - .db to embed some data
|
|
||||||
// - .equ to define some constants
|
|
||||||
directive = { ".org" | ".db" | ".equ" }
|
|
||||||
|
|
||||||
// The .equ directive isn't much use without the ability to have expressions based on
|
// The .equ directive isn't much use without the ability to have expressions based on
|
||||||
// symbols, so, a quick arithmetic expression parser:
|
// symbols, so, a quick arithmetic expression parser:
|
||||||
@@ -61,14 +56,16 @@ string = ${ "\"" ~ string_inner* ~ "\"" }
|
|||||||
|
|
||||||
// Parsing a line
|
// Parsing a line
|
||||||
// Normally an assembly line will be a sequence of "label, opcode, argument, comment."
|
// Normally an assembly line will be a sequence of "label, opcode, argument, comment."
|
||||||
// However, most of these elements are optional. An opcode is only required if an
|
// However, only some combinations of these are valid. Comments are already handled by
|
||||||
// argument exists. Comments are already handled by the COMMENT pattern
|
// the COMMENT pattern.
|
||||||
|
// Also, a line might be an actual instruction, or the assembler will support some directives:
|
||||||
// Some sub-patterns for the portions of a line:
|
// - .org to set the current address
|
||||||
label_group = { label ~ ":" }
|
// - .db to embed some data
|
||||||
|
// - .equ to define some constants
|
||||||
// An opcode might be an actual opcode, or a directive
|
instruction = { label_def? ~ opcode ~ expr? }
|
||||||
instruction_group = { (opcode | directive) ~ (expr | string)? }
|
db_directive = { label_def? ~ ".db" ~ (expr | string) }
|
||||||
|
org_directive = { label_def? ~ ".org" ~ expr }
|
||||||
|
equ_directive = { label_def ~ ".equ" ~ expr }
|
||||||
|
|
||||||
// Finally the entire pattern for an assembly line:
|
// Finally the entire pattern for an assembly line:
|
||||||
line = { SOI ~ label_group? ~ instruction_group? ~ EOI }
|
line = { SOI ~ (db_directive | org_directive | equ_directive | instruction | label_def) ~ EOI }
|
||||||
+135
-77
@@ -1,18 +1,36 @@
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use pest::iterators::Pair;
|
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
|
|
||||||
use vcore::opcodes::Opcode;
|
use vcore::opcodes::Opcode;
|
||||||
|
|
||||||
use crate::ast::{Directive, Instruction, Node, Operator, VASMLine};
|
use crate::ast::{Label, Node, VASMLine};
|
||||||
use crate::parse_error::ParseError;
|
use crate::parse_error::ParseError;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[grammar = "vasm.pest"]
|
#[grammar = "vasm.pest"]
|
||||||
pub struct VASMParser;
|
pub struct VASMParser;
|
||||||
|
|
||||||
|
type Pair<'a> = pest::iterators::Pair<'a, Rule>;
|
||||||
|
|
||||||
|
trait Children {
|
||||||
|
fn first(self) -> Self;
|
||||||
|
fn only(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Children for Pair<'a> {
|
||||||
|
fn first(self) -> Self {
|
||||||
|
self.into_inner().next().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn only(self) -> Pair<'a> {
|
||||||
|
let mut iter = self.into_inner();
|
||||||
|
let child = iter.next().unwrap();
|
||||||
|
debug_assert_eq!(iter.next(), None);
|
||||||
|
child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform tree-shaking on a `Node` by recursively removing single-`Node` exprs.
|
/// Perform tree-shaking on a `Node` by recursively removing single-`Node` exprs.
|
||||||
fn shake(node: Node) -> Node {
|
fn shake(node: Node) -> Node {
|
||||||
match node {
|
match node {
|
||||||
@@ -29,79 +47,81 @@ fn shake(node: Node) -> Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn optional_label(pair: Option<Pair>) -> Option<Label> {
|
||||||
|
pair.map(|label| Label(label.only().as_str()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_vasm_line(line: &str) -> Result<VASMLine, ParseError<'_>> {
|
pub fn parse_vasm_line(line: &str) -> Result<VASMLine, ParseError<'_>> {
|
||||||
let mut pairs = VASMParser::parse(Rule::line, line)
|
let line = VASMParser::parse(Rule::line, line)
|
||||||
.map_err(|_| ParseError::LineParseFailure)?
|
.map_err(|_| ParseError::LineParseFailure)?
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_inner()
|
.first();
|
||||||
.peekable();
|
|
||||||
|
|
||||||
let mut vasm_line = VASMLine::blank();
|
match line.as_rule() {
|
||||||
|
Rule::instruction => {
|
||||||
if let Some(label_group) = pairs.next_if(|pair| pair.as_rule() == Rule::label_group) {
|
let mut iter = line.into_inner().peekable();
|
||||||
let label = label_group.into_inner().next().unwrap();
|
let label = optional_label(iter.next_if(|pair| pair.as_rule() == Rule::label_def));
|
||||||
vasm_line = vasm_line.with_label(label.as_str())
|
let opcode = Opcode::try_from(
|
||||||
|
iter.next_if(|pair| pair.as_rule() == Rule::opcode)
|
||||||
|
.unwrap()
|
||||||
|
.as_str(),
|
||||||
|
)?;
|
||||||
|
let argument = iter
|
||||||
|
.next_if(|pair| pair.as_rule() == Rule::expr)
|
||||||
|
.map(|expr| shake(Node::from(expr)));
|
||||||
|
return Ok(VASMLine::Instruction(label, opcode, argument));
|
||||||
}
|
}
|
||||||
|
Rule::db_directive => {
|
||||||
if let Some(instruction_group) = pairs.next_if(|pair| pair.as_rule() == Rule::instruction_group)
|
let mut iter = line.into_inner().peekable();
|
||||||
{
|
let label = optional_label(iter.next_if(|pair| pair.as_rule() == Rule::label_def));
|
||||||
let mut pairs = instruction_group.into_inner();
|
let argument = shake(Node::from(iter.next().unwrap()));
|
||||||
vasm_line = vasm_line.with_instruction(Instruction::try_from(pairs.next().unwrap())?);
|
return Ok(VASMLine::Db(label, argument));
|
||||||
|
|
||||||
if let Some(argument_group) = pairs.next() {
|
|
||||||
vasm_line = vasm_line.with_argument(shake(Node::try_from(argument_group)?))
|
|
||||||
}
|
}
|
||||||
|
Rule::org_directive => {
|
||||||
|
let mut iter = line.into_inner().peekable();
|
||||||
|
let label = optional_label(iter.next_if(|pair| pair.as_rule() == Rule::label_def));
|
||||||
|
let argument = shake(Node::from(iter.next().unwrap()));
|
||||||
|
return Ok(VASMLine::Org(label, argument));
|
||||||
|
}
|
||||||
|
Rule::equ_directive => {
|
||||||
|
let mut iter = line.into_inner();
|
||||||
|
let label = Label(iter.next().unwrap().only().as_str());
|
||||||
|
let argument = shake(Node::from(iter.next().unwrap()));
|
||||||
|
return Ok(VASMLine::Equ(label, argument));
|
||||||
|
}
|
||||||
|
Rule::label_def => {
|
||||||
|
return Ok(VASMLine::LabelDef(Label(line.only().as_str())));
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(vasm_line)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use vcore::opcodes::Opcode::*;
|
use vcore::opcodes::Opcode::*;
|
||||||
|
|
||||||
use super::Directive::*;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::ast::Operator;
|
||||||
|
|
||||||
impl<'a> VASMLine<'a> {
|
fn number(number: i32) -> Node<'static> {
|
||||||
pub fn op(opcode: Opcode) -> VASMLine<'a> {
|
Node::Number(number)
|
||||||
Self::blank().with_opcode(opcode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_number(opcode: Opcode, argument: i32) -> VASMLine<'a> {
|
fn string(s: &str) -> Node<'static> {
|
||||||
Self::blank()
|
Node::String(s.to_string())
|
||||||
.with_opcode(opcode)
|
|
||||||
.with_argument(Node::Number(argument))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn op_label(opcode: Opcode, argument: &'a str) -> VASMLine<'a> {
|
|
||||||
Self::blank()
|
|
||||||
.with_opcode(opcode)
|
|
||||||
.with_argument(Node::Label(argument))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn op_rel_label(opcode: Opcode, argument: &'a str) -> VASMLine<'a> {
|
|
||||||
Self::blank()
|
|
||||||
.with_opcode(opcode)
|
|
||||||
.with_argument(Node::RelativeLabel(argument))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn op_off(opcode: Opcode, argument: Node<'a>) -> VASMLine<'a> {
|
|
||||||
Self::blank().with_opcode(opcode).with_argument(argument)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dir_str(directive: Directive, string: &str) -> VASMLine<'a> {
|
|
||||||
Self::blank()
|
|
||||||
.with_directive(directive)
|
|
||||||
.with_argument(Node::String(string.to_string()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse() {
|
fn test_parse() {
|
||||||
assert_eq!(parse_vasm_line("add"), Ok(VASMLine::op(Add)));
|
assert_eq!(
|
||||||
assert_eq!(parse_vasm_line("sub"), Ok(VASMLine::op(Sub)));
|
parse_vasm_line("add"),
|
||||||
|
Ok(VASMLine::Instruction(None, Add, None))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_vasm_line("sub"),
|
||||||
|
Ok(VASMLine::Instruction(None, Sub, None))
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("blah"),
|
parse_vasm_line("blah"),
|
||||||
Err(ParseError::InvalidInstruction("blah"))
|
Err(ParseError::InvalidInstruction("blah"))
|
||||||
@@ -111,43 +131,64 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numbers() {
|
fn test_numbers() {
|
||||||
assert_eq!(parse_vasm_line("add 45"), Ok(VASMLine::op_number(Add, 45)));
|
assert_eq!(
|
||||||
assert_eq!(parse_vasm_line("add 0"), Ok(VASMLine::op_number(Add, 0)));
|
parse_vasm_line("add 45"),
|
||||||
|
Ok(VASMLine::Instruction(None, Add, Some(number(45))))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_vasm_line("blah: add 45"),
|
||||||
|
Ok(VASMLine::Instruction(
|
||||||
|
Some(Label("blah")),
|
||||||
|
Add,
|
||||||
|
Some(number(45))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_vasm_line("add 0"),
|
||||||
|
Ok(VASMLine::Instruction(None, Add, Some(number(0))))
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("add 0x10"),
|
parse_vasm_line("add 0x10"),
|
||||||
Ok(VASMLine::op_number(Add, 16))
|
Ok(VASMLine::Instruction(None, Add, Some(number(16))))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("add 0b1111"),
|
parse_vasm_line("add 0b1111"),
|
||||||
Ok(VASMLine::op_number(Add, 15))
|
Ok(VASMLine::Instruction(None, Add, Some(number(15))))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("add 0o377"),
|
parse_vasm_line("add 0o377"),
|
||||||
Ok(VASMLine::op_number(Add, 255))
|
Ok(VASMLine::Instruction(None, Add, Some(number(255))))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("add -17"),
|
parse_vasm_line("add -17"),
|
||||||
Ok(VASMLine::op_number(Add, -17))
|
Ok(VASMLine::Instruction(None, Add, Some(number(-17))))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_strings() {
|
fn test_dbs() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line(".db \"blah\""),
|
parse_vasm_line(".db \"blah\""),
|
||||||
Ok(VASMLine::dir_str(Db, "blah"))
|
Ok(VASMLine::Db(None, string("blah")))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line(".db \"blah\\twith escapes\\0\""),
|
parse_vasm_line(".db \"blah\\twith escapes\\0\""),
|
||||||
Ok(VASMLine::dir_str(Db, "blah\twith escapes\0"))
|
Ok(VASMLine::Db(None, string("blah\twith escapes\0")))
|
||||||
)
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_vasm_line("foo: .db 47"),
|
||||||
|
Ok(VASMLine::Db(Some(Label("foo")), number(47)))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_exprs() {
|
fn test_exprs() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("add 2 + 3 * (4 - 5) + 6"),
|
parse_vasm_line("add 2 + 3 * (4 - 5) + 6"),
|
||||||
Ok(VASMLine::op(Add).with_argument(Node::Expr(
|
Ok(VASMLine::Instruction(
|
||||||
|
None,
|
||||||
|
Add,
|
||||||
|
Some(Node::Expr(
|
||||||
Box::from(Node::Number(2)),
|
Box::from(Node::Number(2)),
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
@@ -165,7 +206,8 @@ mod test {
|
|||||||
),
|
),
|
||||||
(Operator::Add, Node::Number(6))
|
(Operator::Add, Node::Number(6))
|
||||||
],
|
],
|
||||||
)))
|
))
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,11 +215,15 @@ mod test {
|
|||||||
fn test_expr_labels() {
|
fn test_expr_labels() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("loadw foo"),
|
parse_vasm_line("loadw foo"),
|
||||||
Ok(VASMLine::op_label(Loadw, "foo"))
|
Ok(VASMLine::Instruction(None, Loadw, Some(Node::Label("foo"))))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("brz @blah"),
|
parse_vasm_line("brz @blah"),
|
||||||
Ok(VASMLine::op_rel_label(Brz, "blah"))
|
Ok(VASMLine::Instruction(
|
||||||
|
None,
|
||||||
|
Brz,
|
||||||
|
Some(Node::RelativeLabel("blah"))
|
||||||
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,11 +231,19 @@ mod test {
|
|||||||
fn test_expr_offsets() {
|
fn test_expr_offsets() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("jmp $-2"),
|
parse_vasm_line("jmp $-2"),
|
||||||
Ok(VASMLine::op_off(Jmp, Node::AbsoluteOffset(-2)))
|
Ok(VASMLine::Instruction(
|
||||||
|
None,
|
||||||
|
Jmp,
|
||||||
|
Some(Node::AbsoluteOffset(-2))
|
||||||
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("brz @+3"),
|
parse_vasm_line("brz @+3"),
|
||||||
Ok(VASMLine::op_off(Brz, Node::RelativeOffset(3)))
|
Ok(VASMLine::Instruction(
|
||||||
|
None,
|
||||||
|
Brz,
|
||||||
|
Some(Node::RelativeOffset(3))
|
||||||
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,27 +251,31 @@ mod test {
|
|||||||
fn test_parse_labels() {
|
fn test_parse_labels() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("foo: add"),
|
parse_vasm_line("foo: add"),
|
||||||
Ok(VASMLine::op(Add).with_label("foo"))
|
Ok(VASMLine::Instruction(Some(Label("foo")), Add, None))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("bar:"),
|
parse_vasm_line("bar:"),
|
||||||
Ok(VASMLine::blank().with_label("bar"))
|
Ok(VASMLine::LabelDef(Label("bar")))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("foo: add 43"),
|
parse_vasm_line("foo: add 43"),
|
||||||
Ok(VASMLine::op_number(Add, 43).with_label("foo"))
|
Ok(VASMLine::Instruction(
|
||||||
|
Some(Label("foo")),
|
||||||
|
Add,
|
||||||
|
Some(number(43))
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_directives() {
|
fn test_parse_directives() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("foo: .equ"),
|
parse_vasm_line("foo: .equ 47"),
|
||||||
Ok(VASMLine::blank().with_label("foo").with_directive(Equ))
|
Ok(VASMLine::Equ(Label("foo"), number(47)))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line(".db"),
|
parse_vasm_line(".org 0x400"),
|
||||||
Ok(VASMLine::blank().with_directive(Db))
|
Ok(VASMLine::Org(None, number(1024)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-3
@@ -112,6 +112,10 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cmp_owned)]
|
||||||
|
// This is needed because clippy can't figure out, on alt / agt, that even though yes you can
|
||||||
|
// directly compare two Words, that means something different than comparing the i32s those
|
||||||
|
// Words represent. That difference is the entire point of the agt / alt instructions.
|
||||||
fn execute(&mut self, instruction: Instruction) -> Word {
|
fn execute(&mut self, instruction: Instruction) -> Word {
|
||||||
if let Some(arg) = instruction.arg {
|
if let Some(arg) = instruction.arg {
|
||||||
self.push_data(arg)
|
self.push_data(arg)
|
||||||
@@ -242,7 +246,9 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self) {
|
pub fn tick(&mut self) {
|
||||||
if self.halted { return }
|
if self.halted {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
match self.fetch() {
|
match self.fetch() {
|
||||||
Ok(instr) => {
|
Ok(instr) => {
|
||||||
@@ -329,7 +335,7 @@ mod tests {
|
|||||||
let mut cpu = CPU::new(Memory::default());
|
let mut cpu = CPU::new(Memory::default());
|
||||||
given(&mut cpu);
|
given(&mut cpu);
|
||||||
let new_pc = cpu.execute(Instruction {
|
let new_pc = cpu.execute(Instruction {
|
||||||
opcode: opcode,
|
opcode,
|
||||||
arg: None,
|
arg: None,
|
||||||
length: 1,
|
length: 1,
|
||||||
});
|
});
|
||||||
@@ -501,8 +507,10 @@ mod tests {
|
|||||||
simple_opcode_test(vec![5, 3], Lt, vec![0]);
|
simple_opcode_test(vec![5, 3], Lt, vec![0]);
|
||||||
simple_opcode_test(vec![5, 7], Lt, vec![1]);
|
simple_opcode_test(vec![5, 7], Lt, vec![1]);
|
||||||
simple_opcode_test(vec![5, to_word(-3)], Agt, vec![1]);
|
simple_opcode_test(vec![5, to_word(-3)], Agt, vec![1]);
|
||||||
|
simple_opcode_test(vec![to_word(-3), 5], Agt, vec![0]);
|
||||||
simple_opcode_test(vec![5, 10], Agt, vec![0]);
|
simple_opcode_test(vec![5, 10], Agt, vec![0]);
|
||||||
simple_opcode_test(vec![5, to_word(-3)], Alt, vec![0]);
|
simple_opcode_test(vec![5, to_word(-3)], Alt, vec![0]);
|
||||||
|
simple_opcode_test(vec![to_word(-3), 5], Alt, vec![1]);
|
||||||
simple_opcode_test(vec![5, 10], Alt, vec![1]);
|
simple_opcode_test(vec![5, 10], Alt, vec![1]);
|
||||||
simple_opcode_test(vec![0b1100, 2], Rshift, vec![3]);
|
simple_opcode_test(vec![0b1100, 2], Rshift, vec![3]);
|
||||||
simple_opcode_test(vec![0b1100, 2], Lshift, vec![0b110000]);
|
simple_opcode_test(vec![0b1100, 2], Lshift, vec![0b110000]);
|
||||||
@@ -541,7 +549,7 @@ mod tests {
|
|||||||
fn test_cpu_new() {
|
fn test_cpu_new() {
|
||||||
let cpu = CPU::new(Memory::default());
|
let cpu = CPU::new(Memory::default());
|
||||||
assert_eq!(cpu.pc, 1024);
|
assert_eq!(cpu.pc, 1024);
|
||||||
assert_eq!(cpu.halted, true);
|
assert!(cpu.halted);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+4
-4
@@ -12,7 +12,7 @@ impl Word {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use vcore::word::Word;
|
/// use vcore::word::Word;
|
||||||
///
|
///
|
||||||
/// assert!(Word::from_bytes([0x01, 0x02, 0x03]) == 0x030201);
|
/// assert_eq!(Word::from_bytes([0x01, 0x02, 0x03]), 0x030201);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_bytes(bytes: [u8; 3]) -> Self {
|
pub fn from_bytes(bytes: [u8; 3]) -> Self {
|
||||||
let [a, b, c] = bytes;
|
let [a, b, c] = bytes;
|
||||||
@@ -130,9 +130,9 @@ impl From<Word> for bool {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_from_bool() {
|
fn to_from_bool() {
|
||||||
assert_eq!(bool::from(Word::from(0)), false);
|
assert!(!bool::from(Word::from(0)));
|
||||||
assert_eq!(bool::from(Word::from(1)), true);
|
assert!(bool::from(Word::from(1)));
|
||||||
assert_eq!(bool::from(Word::from(0x123456)), true);
|
assert!(bool::from(Word::from(0x123456)));
|
||||||
|
|
||||||
assert_eq!(Word::from(false), Word::from(0));
|
assert_eq!(Word::from(false), Word::from(0));
|
||||||
assert_eq!(Word::from(true), Word::from(1));
|
assert_eq!(Word::from(true), Word::from(1));
|
||||||
|
|||||||
Reference in New Issue
Block a user