msp430-asm: init repo with proof-of-concept code
This commit is contained in:
commit
a9ee7d3bc9
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
14
.rustfmt.toml
Normal file
14
.rustfmt.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
unstable_features = true
|
||||||
|
max_width = 120
|
||||||
|
wrap_comments = true
|
||||||
|
comment_width = 100
|
||||||
|
|
||||||
|
# Allow structs to fill an entire line
|
||||||
|
use_small_heuristics = "Max"
|
||||||
|
# Allow small functions on single line
|
||||||
|
fn_single_line = true
|
||||||
|
|
||||||
|
# Alignment
|
||||||
|
enum_discrim_align_threshold = 12
|
||||||
|
#struct_field_align_threshold = 12
|
||||||
|
where_single_line = true
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "msp430-asm"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["John Breaux"]
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
regex = "1.9.3"
|
107
src/error.rs
Normal file
107
src/error.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
// TODO: Be incredibly specific about the source of the errors
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
tokenizer::token::{OwnedToken, Types},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Store error context in error. for example:
|
||||||
|
// Error {ExpectationFailed{...}, WhileParsing(Register)}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Produced by [Parser](crate::parser::Parser::parse<T>())
|
||||||
|
ParseError(parser::root::Root, Box<dyn std::error::Error + 'static>),
|
||||||
|
Contextual(Context, Box<Self>),
|
||||||
|
/// Produced by [`TokenStream::expect`] when the next [Token] isn't the expected [Type]
|
||||||
|
UnexpectedToken {
|
||||||
|
expected: Type,
|
||||||
|
got: OwnedToken,
|
||||||
|
},
|
||||||
|
/// Produced by [`TokenStream::expect_any_of`] when the next [Token] isn't any of the expected
|
||||||
|
/// [Types](Type)
|
||||||
|
AllExpectationsFailed {
|
||||||
|
expected: Types,
|
||||||
|
got: OwnedToken,
|
||||||
|
},
|
||||||
|
/// Produced by
|
||||||
|
/// [Number](parser::instruction::encoding::number::Number)[::parse()](parser::parsable::Parsable::parse())
|
||||||
|
/// when the parsed number contains digits too high for the specified radix
|
||||||
|
UnexpectedDigits(String, u32),
|
||||||
|
/// Produced by
|
||||||
|
/// [Opcode](parser::instruction::opcode::Opcode)[::parse()](parser::parsable::Parsable::parse())
|
||||||
|
/// when the opcode passed lexing but did not match recognized opcodes.
|
||||||
|
///
|
||||||
|
/// This should be interpreted as a failure in lexing.
|
||||||
|
UnrecognizedOpcode(String),
|
||||||
|
NotARegister(String),
|
||||||
|
RegisterTooHigh(u16),
|
||||||
|
FatSecondaryImmediate(isize),
|
||||||
|
NumberTooWide(isize),
|
||||||
|
JumpedTooFar(isize),
|
||||||
|
JumpedOdd(isize),
|
||||||
|
EndOfFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn context(self, c: Context) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Contextual(..) => self,
|
||||||
|
_ => Self::Contextual(c, Box::new(self)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracts the root of the error tree
|
||||||
|
pub fn bare(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Contextual(_, bare) => bare.bare(),
|
||||||
|
_ => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap(mut self, other: Self) -> Self {
|
||||||
|
if let Self::Contextual(_, err) = &mut self {
|
||||||
|
_ = std::mem::replace(err.as_mut(), other)
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expected<E: AsRef<[Type]>, T: Into<OwnedToken>>(expected: E, got: T) -> Self {
|
||||||
|
match expected.as_ref().len() {
|
||||||
|
1 => Self::UnexpectedToken { expected: expected.as_ref()[0], got: got.into() },
|
||||||
|
_ => Self::AllExpectationsFailed { expected: expected.as_ref().into(), got: got.into() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::Contextual(ctx, error) => write!(f, "{ctx}: {error}"),
|
||||||
|
Error::ParseError(_, error) => write!(f, "Error encountered while parsing:\n{error}"),
|
||||||
|
Error::UnexpectedToken { expected, got } => write!(f, "Expected {expected}, got {got}."),
|
||||||
|
Error::AllExpectationsFailed { expected, got } => write!(f, "Expected one of {expected}, got {got}."),
|
||||||
|
Error::UnexpectedDigits(number, radix) => write!(f, "Number `{number}` is not base {radix}."),
|
||||||
|
Error::UnrecognizedOpcode(op) => write!(f, "{op} is not an opcode"),
|
||||||
|
Error::NotARegister(reg) => write!(f, "{reg} is not a register"),
|
||||||
|
Error::RegisterTooHigh(reg) => write!(f, "r{reg} is not a register"),
|
||||||
|
Error::FatSecondaryImmediate(num) => write!(f, "Secondary immediate must be #0 or #1, not #{num}"),
|
||||||
|
Error::NumberTooWide(num) => write!(f, "{num} does not fit in 16 bits"),
|
||||||
|
Error::JumpedTooFar(num) => write!(f, "{num} is too far away (jump targets must be in range (-3fc..=3fe"),
|
||||||
|
Error::JumpedOdd(num) => write!(f, "Jump target {num} should not be odd."),
|
||||||
|
Error::EndOfFile => write!(f, "Unexpected end of file"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
Error::ParseError(_, e) => Some(e.as_ref()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/hash.rs
Normal file
17
src/hash.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! Convenience trait for dealing with hashable data
|
||||||
|
pub type Hash = u64;
|
||||||
|
pub trait FromHash: From<Hash> {
|
||||||
|
/// Hashes anything that implements [type@Hash] using the [DefaultHasher](std::collections::hash_map::DefaultHasher)
|
||||||
|
fn hash<T: std::hash::Hash>(hashable: T) -> Hash {
|
||||||
|
use std::hash::Hasher;
|
||||||
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
||||||
|
hashable.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
fn from_hash<T: std::hash::Hash>(hashable: T) -> Self
|
||||||
|
where Self: Sized {
|
||||||
|
Self::from(Self::hash(hashable))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: From<Hash>> FromHash for T {}
|
21
src/lib.rs
Normal file
21
src/lib.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! An assembler for the TI MSP430
|
||||||
|
pub mod preamble {
|
||||||
|
use super::*;
|
||||||
|
pub use error::Error;
|
||||||
|
pub use hash::{FromHash, Hash};
|
||||||
|
pub use linker::{Linker, Visitor};
|
||||||
|
pub use parser::Parser;
|
||||||
|
pub use tokenizer::{
|
||||||
|
context::Context,
|
||||||
|
token::{Token, Type},
|
||||||
|
TokenStream, Tokenizer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
use preamble::*;
|
||||||
|
pub mod error;
|
||||||
|
pub mod hash;
|
||||||
|
pub mod linker;
|
||||||
|
pub mod parser;
|
||||||
|
pub mod tokenizer;
|
20
src/linker.rs
Normal file
20
src/linker.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
/// TODO: tree traversal and label resolution
|
||||||
|
use crate::parser::preamble::*;
|
||||||
|
pub trait Visitor<T> {
|
||||||
|
// visit_node for all nodes
|
||||||
|
fn visit_register(&mut self, r: &Register) -> T;
|
||||||
|
fn visit_number(&mut self, n: &Number) -> T;
|
||||||
|
fn visit_width(&mut self, w: &Width) -> T;
|
||||||
|
fn visit_primary_operand(&mut self, p: &PrimaryOperand) -> T;
|
||||||
|
fn visit_secondary_operand(&mut self, d: &SecondaryOperand) -> T;
|
||||||
|
fn visit_jump_target(&mut self, t: &JumpTarget) -> T;
|
||||||
|
fn visit_encoding(&mut self, e: &Encoding) -> T;
|
||||||
|
fn visit_opcode(&mut self, o: &Opcode) -> T;
|
||||||
|
fn visit_instruction(&mut self, i: &Instruction) -> T;
|
||||||
|
fn visit_directive(&mut self, d: &Directive) -> T;
|
||||||
|
// the most important one: resolve identifiers
|
||||||
|
fn visit_identifier(&mut self, i: &Identifier) -> T;
|
||||||
|
}
|
||||||
|
/// TODO: [Linker]
|
||||||
|
pub struct Linker;
|
104
src/main.rs
Normal file
104
src/main.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
//! Simple frontend for the assembler
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use msp430_asm::preamble::*;
|
||||||
|
|
||||||
|
// const ASM: &str = r"
|
||||||
|
// //.org 8000
|
||||||
|
// //.define INT #2400
|
||||||
|
// //entry:
|
||||||
|
// mov.b 8000(sp), r15 ; pop into sp
|
||||||
|
// rrc @pc+
|
||||||
|
// add #64, r8
|
||||||
|
// call #10 // call INT
|
||||||
|
// ";
|
||||||
|
|
||||||
|
fn main() -> Result<(), Error> {
|
||||||
|
// Get args
|
||||||
|
let mut repl = true;
|
||||||
|
for arg in std::env::args() {
|
||||||
|
match arg.as_str() {
|
||||||
|
"-" | "-f" | "--file" => repl = false,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide if repl mode is enabled
|
||||||
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
if repl {
|
||||||
|
// print!("> ");
|
||||||
|
// let _ = std::io::stdout().flush();
|
||||||
|
while let Ok(len) = std::io::stdin().read_line(&mut buf) {
|
||||||
|
match len {
|
||||||
|
0 => break,
|
||||||
|
1 => continue,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
if len < 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// print!("\nLexer: ");
|
||||||
|
// tokenizer_dump(&mut Tokenizer::new(&buf));
|
||||||
|
//print!("Parser: ");
|
||||||
|
match Parser::default().parse(&buf) {
|
||||||
|
Ok(line) => println!("{line:x}"),
|
||||||
|
//Ok(tree) => println!("=> {tree}\n => {tree:x}"),
|
||||||
|
Err(error) => println!("{error}"),
|
||||||
|
}
|
||||||
|
buf.clear();
|
||||||
|
// print!("> ");
|
||||||
|
// let _ = std::io::stdout().flush();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::io::stdin().lock().read_to_string(&mut buf).map_err(|_| Error::EndOfFile)?;
|
||||||
|
let mut tk = Tokenizer::new(&buf);
|
||||||
|
|
||||||
|
// println!("Lexer: ");
|
||||||
|
// tokenizer_dump(&mut Tokenizer::new(&buf));
|
||||||
|
let tree = Parser::default().parse_with(&mut tk);
|
||||||
|
match &tree {
|
||||||
|
Ok(tree) => println!("{tree:x}"),
|
||||||
|
Err(error) => eprintln!("{error}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn tokenizer_dump<'text, T: TokenStream<'text>>(t: &mut T) {
|
||||||
|
for token in t {
|
||||||
|
match token.variant() {
|
||||||
|
//Token::Space => (),
|
||||||
|
Type::Endl => {
|
||||||
|
println!();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Type::Comment => (),
|
||||||
|
Type::Label => (),
|
||||||
|
Type::Insn => (),
|
||||||
|
Type::ByteWidth => (),
|
||||||
|
Type::WordWidth => (),
|
||||||
|
Type::Register => (),
|
||||||
|
Type::RadixMarkerHex => (),
|
||||||
|
Type::RadixMarkerOct => (),
|
||||||
|
Type::RadixMarkerBin => (),
|
||||||
|
Type::Number => (),
|
||||||
|
Type::Minus => (),
|
||||||
|
Type::LParen => (),
|
||||||
|
Type::RParen => (),
|
||||||
|
Type::Indirect => (),
|
||||||
|
Type::Plus => (),
|
||||||
|
Type::Absolute => (),
|
||||||
|
Type::Immediate => (),
|
||||||
|
Type::Identifier => (),
|
||||||
|
Type::Directive => (),
|
||||||
|
Type::Separator => (),
|
||||||
|
Type::EndOfFile => (),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
print!("{token:?} ");
|
||||||
|
}
|
||||||
|
}
|
212
src/parser.rs
Normal file
212
src/parser.rs
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! Parses [Tokens](crate::Token) into an [abstract syntax tree](Root)
|
||||||
|
|
||||||
|
use crate::{Error, Hash, TokenStream, Type};
|
||||||
|
use std::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
|
pub mod preamble {
|
||||||
|
//! All the different AST node types
|
||||||
|
use super::*;
|
||||||
|
// Traits
|
||||||
|
pub use parsable::Parsable;
|
||||||
|
|
||||||
|
pub use comment::Comment;
|
||||||
|
pub use directive::Directive;
|
||||||
|
pub use identifier::Identifier;
|
||||||
|
pub use instruction::{
|
||||||
|
encoding::{
|
||||||
|
encoding_parser::EncodingParser, jump_target::JumpTarget, number::Number, primary_operand::PrimaryOperand,
|
||||||
|
register::Register, secondary_operand::SecondaryOperand, width::Width, Encoding,
|
||||||
|
},
|
||||||
|
opcode::Opcode,
|
||||||
|
Instruction,
|
||||||
|
};
|
||||||
|
pub use label::Label;
|
||||||
|
pub use line::Line;
|
||||||
|
pub use root::Root;
|
||||||
|
}
|
||||||
|
use preamble::*;
|
||||||
|
|
||||||
|
pub(crate) mod parsable;
|
||||||
|
|
||||||
|
pub(crate) mod comment;
|
||||||
|
pub(crate) mod directive;
|
||||||
|
pub(crate) mod identifier;
|
||||||
|
pub(crate) mod instruction;
|
||||||
|
pub(crate) mod label;
|
||||||
|
|
||||||
|
pub(crate) mod line {
|
||||||
|
// © 2023 John Breaux
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A line is one of:
|
||||||
|
/// - [`Label`] (definition)
|
||||||
|
/// - [`Instruction`]
|
||||||
|
/// - [`Directive`]
|
||||||
|
/// - [`Comment`]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Line {
|
||||||
|
Empty,
|
||||||
|
Label(Label), // TODO: Label resolution
|
||||||
|
Insn(Instruction),
|
||||||
|
Directive(Directive),
|
||||||
|
Comment(Comment),
|
||||||
|
EndOfFile, // Expected end of file
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for Line {
|
||||||
|
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: TokenStream<'text> {
|
||||||
|
if let Ok(token) = stream.peek_expect_any_of([Type::Comment, Type::Directive, Type::Insn, Type::Identifier])
|
||||||
|
{
|
||||||
|
return Ok(match token.variant() {
|
||||||
|
Type::Comment => Self::Comment(Comment::parse(p, stream)?),
|
||||||
|
Type::Directive => Self::Directive(Directive::parse(p, stream)?),
|
||||||
|
Type::Identifier => Self::Label(Label::parse(p, stream)?),
|
||||||
|
Type::Insn => Self::Insn(Instruction::parse(p, stream)?),
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// TODO: preserve comments
|
||||||
|
let token = stream.expect_any_of([Type::EndOfFile])?;
|
||||||
|
Ok(match token.variant() {
|
||||||
|
Type::EndOfFile => Self::EndOfFile,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for Line {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Empty => writeln!(f, "\n"),
|
||||||
|
Self::Label(arg0) => Display::fmt(arg0, f),
|
||||||
|
Self::Insn(arg0) => Display::fmt(arg0, f),
|
||||||
|
Self::Directive(arg0) => Display::fmt(arg0, f),
|
||||||
|
Self::Comment(arg0) => Display::fmt(arg0, f),
|
||||||
|
Self::EndOfFile => write!(f, "; End of file."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl LowerHex for Line {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Line::Insn(arg0) => LowerHex::fmt(arg0, f),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod root {
|
||||||
|
// © 2023 John Breaux
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Contains the entire AST
|
||||||
|
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Root(pub Vec<Line>);
|
||||||
|
|
||||||
|
// TODO: Get data out of ParseTree
|
||||||
|
// TODO: Maybe implement some sort of follower
|
||||||
|
impl Parsable for Root {
|
||||||
|
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: TokenStream<'text> {
|
||||||
|
let mut lines = vec![];
|
||||||
|
loop {
|
||||||
|
match Line::parse(p, stream) {
|
||||||
|
Ok(Line::EndOfFile) => break,
|
||||||
|
Ok(line) => lines.push(line),
|
||||||
|
Err(e) => {
|
||||||
|
let ret = Self(lines);
|
||||||
|
eprintln!("{ret}");
|
||||||
|
eprintln!("Error:{e}\n");
|
||||||
|
eprint!("Remaining:");
|
||||||
|
stream.for_each(|t| eprint!("{t}"));
|
||||||
|
eprintln!();
|
||||||
|
return Err(Error::ParseError(ret, Box::new(e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Root(lines))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Root {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for line in self.0.iter() {
|
||||||
|
f.pad(&format!("{line} "))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl LowerHex for Root {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for line in self.0.iter() {
|
||||||
|
LowerHex::fmt(line, f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Debug for Root {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for line in self.0.iter() {
|
||||||
|
Display::fmt(line, f)?;
|
||||||
|
Debug::fmt(line, f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type for [Parser] callbacks
|
||||||
|
pub type EmitComment = Box<dyn FnMut(&str)>;
|
||||||
|
pub type DefineLabel = Box<dyn FnMut(&Identifier) -> Result<(), Error>>;
|
||||||
|
|
||||||
|
pub struct Parser {
|
||||||
|
radix: u32,
|
||||||
|
// TODO: callbacks for emitted token sequences?!
|
||||||
|
on_label: Option<DefineLabel>,
|
||||||
|
on_comment: Option<EmitComment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
pub fn parse_with<'t, T>(self, stream: &'t mut T) -> Result<Root, Error>
|
||||||
|
where T: TokenStream<'t> {
|
||||||
|
Root::parse(&self, &mut stream.ignore_spaces())
|
||||||
|
}
|
||||||
|
pub fn parse<T>(self, input: &T) -> Result<Root, Error>
|
||||||
|
where T: AsRef<str> + ?Sized {
|
||||||
|
Root::parse(&self, &mut super::Tokenizer::new(input).ignore_spaces())
|
||||||
|
}
|
||||||
|
pub fn parse_one<T>(self, input: &T) -> Result<Line, Error>
|
||||||
|
where T: AsRef<str> + ?Sized {
|
||||||
|
Line::parse(&self, &mut super::Tokenizer::new(input).ignore_spaces())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the default radix for [Token](crate::tokenizer::token::Token) -> [Number]
|
||||||
|
/// conversion
|
||||||
|
pub fn radix(mut self, radix: u32) { self.radix = radix; }
|
||||||
|
|
||||||
|
/// Inform the caller of a new identifier definition
|
||||||
|
pub fn define_label(&mut self, l: &Identifier) -> Result<(), Error> {
|
||||||
|
match self.on_label.as_mut() {
|
||||||
|
Some(f) => f(l),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Inform the caller of an identifier being used
|
||||||
|
pub fn emit_comment(&mut self, d: &str) {
|
||||||
|
if let Some(f) = self.on_comment.as_mut() {
|
||||||
|
f(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Parser {
|
||||||
|
fn default() -> Self { Self { radix: 16, on_label: None, on_comment: None } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Parser {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Parser").field("radix", &self.radix).finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
15
src/parser/comment.rs
Normal file
15
src/parser/comment.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
use super::*;
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Comment(pub String);
|
||||||
|
|
||||||
|
impl Parsable for Comment {
|
||||||
|
fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: TokenStream<'text> {
|
||||||
|
let token = stream.expect(Type::Comment)?;
|
||||||
|
Ok(Self(token.lexeme().to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for Comment {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(&self.0, f) }
|
||||||
|
}
|
32
src/parser/directive.rs
Normal file
32
src/parser/directive.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! A [Directive] issues commands directly to the [Tokenizer](crate::Tokenizer) and
|
||||||
|
//! [Linker](crate::Linker)
|
||||||
|
use super::*;
|
||||||
|
use crate::hash::FromHash;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Directive(pub Hash, pub String);
|
||||||
|
|
||||||
|
impl Directive {
|
||||||
|
fn str<S: ToString>(mut self, s: S) -> Self {
|
||||||
|
self.1 = s.to_string();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Hash> for Directive {
|
||||||
|
fn from(value: Hash) -> Self { Self(value, String::new()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for Directive {
|
||||||
|
fn parse<'text, T>(_p: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: TokenStream<'text> {
|
||||||
|
// expect a directive
|
||||||
|
let d = stream.expect(Type::Directive)?;
|
||||||
|
// send the directive to the listener
|
||||||
|
Ok(Self::from_hash(d.lexeme()).str(d.lexeme()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for Directive {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.1) }
|
||||||
|
}
|
34
src/parser/identifier.rs
Normal file
34
src/parser/identifier.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
use super::*;
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Identifier {
|
||||||
|
Hash(Hash),
|
||||||
|
Str(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Identifier {
|
||||||
|
fn str<T: AsRef<str>>(s: T) -> Self { Self::Str(s.as_ref().into()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Hash> for Identifier {
|
||||||
|
fn from(value: Hash) -> Self { Self::Hash(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for Identifier {
|
||||||
|
fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: TokenStream<'text> {
|
||||||
|
let token = stream.expect(Type::Identifier)?;
|
||||||
|
match token.variant() {
|
||||||
|
Type::Identifier => Ok(Self::str(token.lexeme())),
|
||||||
|
_ => unreachable!("Expected Identifier, got {token:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for Identifier {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Identifier::Hash(_) => Display::fmt("Unresolved", f),
|
||||||
|
Identifier::Str(s) => Display::fmt(s, f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
src/parser/instruction.rs
Normal file
67
src/parser/instruction.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! An [Instruction] contains the [Opcode] and [Encoding] information for a single msp430
|
||||||
|
//! instruction
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! Note: [Opcode] and [Encoding] are very tightly coupled, because they represent interdependent parts
|
||||||
|
//! of the same instruction. This is why [Opcode]::resolve() returns an [EncodingParser] -- otherwise,
|
||||||
|
//! there's an explosion of states that I can't really cope with on my own. Really, there's about 9
|
||||||
|
//! valid classes of instruction, some of which are only used for one or two of the MSP430's
|
||||||
|
//! instructions.
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub mod encoding;
|
||||||
|
pub mod opcode;
|
||||||
|
|
||||||
|
/// Represents an entire MSP430 instruction
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Instruction(Opcode, Encoding);
|
||||||
|
|
||||||
|
impl Instruction {
|
||||||
|
pub fn opcode(&self) -> &Opcode { &self.0 }
|
||||||
|
pub fn encoding(&self) -> &Encoding { &self.1 }
|
||||||
|
/// Gets the Instruction as a [u16]
|
||||||
|
pub fn word(&self) -> u16 { self.0 as u16 | self.1.word() }
|
||||||
|
/// Gets the [extension words]
|
||||||
|
pub fn ext_words(&self) -> (Option<u16>, Option<u16>) { self.1.extwords() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for Instruction {
|
||||||
|
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
T: crate::TokenStream<'text>,
|
||||||
|
{
|
||||||
|
// parse an opcode
|
||||||
|
let insn = stream.expect(Type::Insn)?;
|
||||||
|
let opcode: Opcode = insn.parse()?;
|
||||||
|
// resolve the opcode to a final opcode and an encoding
|
||||||
|
let (opcode, encoding) = opcode.resolve();
|
||||||
|
// parse the encoding
|
||||||
|
let encoding = encoding.parse(p, stream)?;
|
||||||
|
Ok(Self(opcode, encoding))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Instruction> for u16 {
|
||||||
|
fn from(value: Instruction) -> Self { value.word() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Instruction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}{}", self.0, self.1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LowerHex for Instruction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let (word, (ext_src, ext_dst)) = (self.word(), self.ext_words());
|
||||||
|
write!(f, "{:04x} ", word.swap_bytes())?;
|
||||||
|
if let Some(e) = ext_src {
|
||||||
|
write!(f, "{:04x} ", e.swap_bytes())?
|
||||||
|
}
|
||||||
|
if let Some(e) = ext_dst {
|
||||||
|
write!(f, "{:04x} ", e.swap_bytes())?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
81
src/parser/instruction/encoding.rs
Normal file
81
src/parser/instruction/encoding.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! An [Encoding] represents the set of arguments for the [msp430's instructions](Opcode)
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub mod number;
|
||||||
|
pub mod register;
|
||||||
|
pub mod width;
|
||||||
|
|
||||||
|
pub mod jump_target;
|
||||||
|
pub mod primary_operand;
|
||||||
|
pub mod secondary_operand;
|
||||||
|
|
||||||
|
mod builder;
|
||||||
|
pub mod encoding_parser;
|
||||||
|
|
||||||
|
use builder::{DoubleBuilder, JumpBuilder, ReflexiveBuilder, SingleBuilder};
|
||||||
|
use encoding_parser::EncodingParser;
|
||||||
|
|
||||||
|
/// Represents an [instruction encoding](https://mspgcc.sourceforge.net/manual/x223.html)
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// use msp430_asm::{*, parser::{Encoding, EncodingParser}};
|
||||||
|
/// // Create a token sequence
|
||||||
|
/// let asm_file = r".b 8000(r15)";
|
||||||
|
/// // Create a single-operand encoding parser
|
||||||
|
/// let single: EncodingParser = Encoding::single().end();
|
||||||
|
/// // Parse an Encoding from it
|
||||||
|
/// let encoding: Encoding = single
|
||||||
|
/// .parse(&Parser::default(), &mut Tokenizer::new(asm_file).ignore_spaces())
|
||||||
|
/// .unwrap();
|
||||||
|
/// // Print the Encoding
|
||||||
|
/// println!("{encoding}");
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Encoding {
|
||||||
|
Single { width: Width, dst: PrimaryOperand },
|
||||||
|
Jump { target: JumpTarget },
|
||||||
|
Double { width: Width, src: PrimaryOperand, dst: SecondaryOperand },
|
||||||
|
}
|
||||||
|
impl Encoding {
|
||||||
|
/// Returns a builder for [Encoding::Single]
|
||||||
|
pub fn single() -> SingleBuilder { Default::default() }
|
||||||
|
/// Returns a builder for [Encoding::Jump]
|
||||||
|
pub fn jump() -> JumpBuilder { Default::default() }
|
||||||
|
/// Returns a builder for [Encoding::Double]
|
||||||
|
pub fn double() -> DoubleBuilder { Default::default() }
|
||||||
|
/// Returns a builder for [Encoding::Double]
|
||||||
|
///
|
||||||
|
/// The reflexive pseudo-[Encoding] is a [Double](Encoding::Double) where the src and
|
||||||
|
/// dst are the same
|
||||||
|
pub fn reflexive() -> ReflexiveBuilder { Default::default() }
|
||||||
|
///
|
||||||
|
pub fn word(&self) -> u16 {
|
||||||
|
match *self {
|
||||||
|
Encoding::Single { width, dst } => u16::from(width) | dst.mode() | dst.register() as u16,
|
||||||
|
Encoding::Jump { target } => target.word(),
|
||||||
|
Encoding::Double { width, src, dst } => {
|
||||||
|
u16::from(width) | src.mode() | dst.mode() | dst.register() as u16 | ((src.register() as u16) << 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Returns extwords for instruction
|
||||||
|
pub fn extwords(&self) -> (Option<u16>, Option<u16>) {
|
||||||
|
match self {
|
||||||
|
Encoding::Double { src, dst, .. } => (src.ext_word(), dst.ext_word()),
|
||||||
|
Encoding::Single { dst, .. } => (dst.ext_word(), None),
|
||||||
|
Encoding::Jump { .. } => (None, None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Encoding {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Encoding::Single { width, dst } => write!(f, "{width} {dst}"),
|
||||||
|
Encoding::Jump { target } => write!(f, " {target}"),
|
||||||
|
Encoding::Double { width, src, dst } => write!(f, "{width} {src}, {dst}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
src/parser/instruction/encoding/builder.rs
Normal file
76
src/parser/instruction/encoding/builder.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! Builder API for [EncodingParser]
|
||||||
|
use super::*;
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct SingleBuilder {
|
||||||
|
width: Option<Width>,
|
||||||
|
dst: Option<PrimaryOperand>,
|
||||||
|
}
|
||||||
|
impl SingleBuilder {
|
||||||
|
pub fn width(mut self, width: bool) -> Self {
|
||||||
|
self.width = Some(width.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets the [PrimaryOperand] field
|
||||||
|
pub fn operand(mut self, dst: PrimaryOperand) -> Self {
|
||||||
|
self.dst = Some(dst);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Build
|
||||||
|
pub fn end(&self) -> EncodingParser { EncodingParser::Single { width: self.width, dst: self.dst } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct JumpBuilder {
|
||||||
|
target: Option<JumpTarget>,
|
||||||
|
}
|
||||||
|
impl JumpBuilder {
|
||||||
|
pub fn target(mut self, target: JumpTarget) -> Self {
|
||||||
|
self.target = Some(target);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn end(&self) -> EncodingParser { EncodingParser::Jump { target: self.target } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct DoubleBuilder {
|
||||||
|
width: Option<Width>,
|
||||||
|
src: Option<PrimaryOperand>,
|
||||||
|
dst: Option<SecondaryOperand>,
|
||||||
|
}
|
||||||
|
impl DoubleBuilder {
|
||||||
|
/// Sets the [Width] field
|
||||||
|
pub fn width(mut self, width: bool) -> Self {
|
||||||
|
self.width = Some(width.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets the [PrimaryOperand] field
|
||||||
|
pub fn src(mut self, src: PrimaryOperand) -> Self {
|
||||||
|
self.src = Some(src);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets the [PrimaryOperand] field
|
||||||
|
pub fn dst(mut self, dst: SecondaryOperand) -> Self {
|
||||||
|
self.dst = Some(dst);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn end(&self) -> EncodingParser { EncodingParser::Double { width: self.width, src: self.src, dst: self.dst } }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ReflexiveBuilder {
|
||||||
|
width: Option<Width>,
|
||||||
|
reg: Option<SecondaryOperand>,
|
||||||
|
}
|
||||||
|
impl ReflexiveBuilder {
|
||||||
|
/// Sets the [Width] field
|
||||||
|
pub fn width(mut self, width: bool) -> Self {
|
||||||
|
self.width = Some(width.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn reg(mut self, reg: SecondaryOperand) -> Self {
|
||||||
|
self.reg = Some(reg);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn end(&self) -> EncodingParser { EncodingParser::Reflexive { width: self.width, reg: self.reg } }
|
||||||
|
}
|
39
src/parser/instruction/encoding/encoding_parser.rs
Normal file
39
src/parser/instruction/encoding/encoding_parser.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! An [EncodingParser] builds an [Encoding] from a [TokenStream]
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// Builds an [Encoding] using [Tokens](crate::Token) from an input [TokenStream]
|
||||||
|
pub enum EncodingParser {
|
||||||
|
Single { width: Option<Width>, dst: Option<PrimaryOperand> },
|
||||||
|
Jump { target: Option<JumpTarget> },
|
||||||
|
Double { width: Option<Width>, src: Option<PrimaryOperand>, dst: Option<SecondaryOperand> },
|
||||||
|
Reflexive { width: Option<Width>, reg: Option<SecondaryOperand> },
|
||||||
|
}
|
||||||
|
impl EncodingParser {
|
||||||
|
/// Constructs an [Encoding] from this [EncodingParser], filling holes
|
||||||
|
/// with the tokenstream
|
||||||
|
pub fn parse<'text, T>(&self, p: &Parser, stream: &mut T) -> Result<Encoding, Error>
|
||||||
|
where T: crate::TokenStream<'text> {
|
||||||
|
Ok(match self {
|
||||||
|
Self::Single { width, dst } => {
|
||||||
|
let width = width.unwrap_or_else(|| Width::parse_or_default(p, stream));
|
||||||
|
let dst = if let Some(dst) = dst { *dst } else { PrimaryOperand::parse(p, stream)? };
|
||||||
|
Encoding::Single { width, dst }
|
||||||
|
}
|
||||||
|
Self::Jump { target } => Encoding::Jump { target: target.unwrap_or(JumpTarget::parse(p, stream)?) },
|
||||||
|
Self::Double { width, src, dst } => {
|
||||||
|
let width = width.unwrap_or_else(|| Width::parse_or_default(p, stream));
|
||||||
|
let src = if let Some(src) = src { *src } else { PrimaryOperand::parse(p, stream)? };
|
||||||
|
let dst = if let Some(dst) = dst { *dst } else { SecondaryOperand::parse(p, stream)? };
|
||||||
|
|
||||||
|
Encoding::Double { width, src, dst }
|
||||||
|
}
|
||||||
|
Self::Reflexive { width, reg } => {
|
||||||
|
let width = width.unwrap_or_else(|| Width::parse(p, stream).unwrap_or_default());
|
||||||
|
let reg = if let Some(reg) = reg { *reg } else { SecondaryOperand::parse(p, stream)? };
|
||||||
|
Encoding::Double { width, src: reg.into(), dst: reg }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
39
src/parser/instruction/encoding/jump_target.rs
Normal file
39
src/parser/instruction/encoding/jump_target.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! A [JumpTarget] contains the [pc-relative offset](Number) or [Identifier]
|
||||||
|
//! for a [Jump instruction encoding](Encoding::Jump)
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// The target of a [Jump](Encoding::Jump)
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct JumpTarget(Number);
|
||||||
|
|
||||||
|
impl JumpTarget {
|
||||||
|
pub fn word(&self) -> u16 { u16::from(self.0) & 0x3ff }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for JumpTarget {
|
||||||
|
/// - Identifier
|
||||||
|
/// - Number
|
||||||
|
/// - Negative
|
||||||
|
/// - Number
|
||||||
|
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: crate::TokenStream<'text> {
|
||||||
|
// Try to parse a number
|
||||||
|
let target = Number::parse(p, stream)?;
|
||||||
|
match target.into() {
|
||||||
|
i if i % 2 != 0 => Err(Error::JumpedOdd(i).context(stream.context()))?,
|
||||||
|
i if (-1024..=1022).contains(&(i - 2)) => Ok(Self((target - 2) >> 1)),
|
||||||
|
i => Err(Error::JumpedTooFar(i).context(stream.context()))?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JumpTarget> for u16 {
|
||||||
|
fn from(value: JumpTarget) -> Self { value.0.into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for JumpTarget {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", (1 + isize::from(self.0)) << 1)
|
||||||
|
}
|
||||||
|
}
|
75
src/parser/instruction/encoding/number.rs
Normal file
75
src/parser/instruction/encoding/number.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! A [Number] represents a 16-bit signed or unsigned word
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// TODO: Allow identifiers/expressions in place of numbers
|
||||||
|
// - Dependency inversion in TokenStream to allow swapping the parser mid-parse?
|
||||||
|
// - Oh my god, not relying on std::iter::Iterator allows for so many more parsing options
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Number(isize, u32); // (value, radix)
|
||||||
|
|
||||||
|
impl Parsable for Number {
|
||||||
|
// A number is:
|
||||||
|
// RadixMarker[Hex|Oct|Bin]?
|
||||||
|
// - Number
|
||||||
|
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: TokenStream<'text> {
|
||||||
|
use Type::*;
|
||||||
|
let negative = stream.expect(Minus).is_ok();
|
||||||
|
let radix = match stream
|
||||||
|
.expect_any_of([RadixMarkerHex, RadixMarkerDec, RadixMarkerOct, RadixMarkerBin])
|
||||||
|
.ok()
|
||||||
|
.map(|t| t.variant())
|
||||||
|
{
|
||||||
|
Some(RadixMarkerHex) => 16,
|
||||||
|
Some(RadixMarkerDec) => 10,
|
||||||
|
Some(RadixMarkerOct) => 8,
|
||||||
|
Some(RadixMarkerBin) => 2,
|
||||||
|
_ => p.radix,
|
||||||
|
};
|
||||||
|
let number = stream.expect(Number)?;
|
||||||
|
let number = isize::from_str_radix(number.lexeme(), radix)
|
||||||
|
.map_err(|_| Error::UnexpectedDigits(number.lexeme().into(), radix).context(stream.context()))?
|
||||||
|
* if negative { -1 } else { 1 };
|
||||||
|
// Ensure number fits within a *signed or unsigned* 16-bit int (it will be truncated to fit)
|
||||||
|
Ok(Self(
|
||||||
|
if (-0x8000..0x10000).contains(&number) {
|
||||||
|
number
|
||||||
|
} else {
|
||||||
|
Err(Error::NumberTooWide(number).context(stream.context()))?
|
||||||
|
},
|
||||||
|
radix,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Number> for isize {
|
||||||
|
fn from(value: Number) -> Self { value.0 as Self }
|
||||||
|
}
|
||||||
|
impl From<Number> for i32 {
|
||||||
|
fn from(value: Number) -> Self { value.0 as Self }
|
||||||
|
}
|
||||||
|
impl From<Number> for u16 {
|
||||||
|
/// Converts this type from the input type.
|
||||||
|
fn from(value: Number) -> Self { value.0 as Self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Sub<isize> for Number {
|
||||||
|
type Output = Self;
|
||||||
|
fn sub(mut self, rhs: isize) -> Self::Output {
|
||||||
|
self.0 -= rhs;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Shr<usize> for Number {
|
||||||
|
type Output = Self;
|
||||||
|
fn shr(mut self, rhs: usize) -> Self::Output {
|
||||||
|
self.0 >>= rhs;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Number {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:x}", self.0) }
|
||||||
|
}
|
141
src/parser/instruction/encoding/primary_operand.rs
Normal file
141
src/parser/instruction/encoding/primary_operand.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! A [PrimaryOperand] contains the first [Register], addressing mode, and Extension
|
||||||
|
//! Word for an [instruction](Instruction)
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// The Source of a [Double](Encoding::Double) or Destination of a
|
||||||
|
/// [Single](Encoding::Single)
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum PrimaryOperand {
|
||||||
|
Direct(Register),
|
||||||
|
Indirect(Register),
|
||||||
|
PostInc(Register),
|
||||||
|
Indexed(Register, Number),
|
||||||
|
Absolute(Number),
|
||||||
|
Immediate(Number),
|
||||||
|
Four,
|
||||||
|
Eight,
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
MinusOne,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrimaryOperand {
|
||||||
|
/// Returns the mode bits
|
||||||
|
pub fn mode(&self) -> u16 {
|
||||||
|
use PrimaryOperand::*;
|
||||||
|
match self {
|
||||||
|
Direct(_) | Zero => 0,
|
||||||
|
Indexed(_, _) | Absolute(_) | One => 1 << 4,
|
||||||
|
Indirect(_) | Two | Four => 2 << 4,
|
||||||
|
PostInc(_) | Immediate(_) | MinusOne | Eight => 3 << 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Gets the register
|
||||||
|
pub fn register(&self) -> Register {
|
||||||
|
use PrimaryOperand::*;
|
||||||
|
match self {
|
||||||
|
Direct(r) | Indexed(r, _) | Indirect(r) | PostInc(r) => *r,
|
||||||
|
Immediate(_) => Register::pc,
|
||||||
|
Absolute(_) | Four | Eight => Register::sr,
|
||||||
|
Zero | One | Two | MinusOne => Register::cg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Gets the extension word, if present
|
||||||
|
pub fn ext_word(&self) -> Option<u16> {
|
||||||
|
use PrimaryOperand::*;
|
||||||
|
match self {
|
||||||
|
Indexed(_, w) | Absolute(w) | Immediate(w) => Some((*w).into()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for PrimaryOperand {
|
||||||
|
// - Register
|
||||||
|
// - Indirect
|
||||||
|
// - Register
|
||||||
|
// - PostInc?
|
||||||
|
// - Number
|
||||||
|
// - OpenIdx
|
||||||
|
// - Register
|
||||||
|
// - CloseIdx
|
||||||
|
// - Absolute
|
||||||
|
// - Number
|
||||||
|
// - Immediate
|
||||||
|
// - Number
|
||||||
|
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: crate::TokenStream<'text> {
|
||||||
|
use PrimaryOperand::*;
|
||||||
|
// Try parsing as Register Direct
|
||||||
|
if let Some(r) = Register::try_parse(p, stream)? {
|
||||||
|
return Ok(Self::Direct(r));
|
||||||
|
}
|
||||||
|
// Try parsing as Number Indexed
|
||||||
|
if let Some(idx) = Number::try_parse(p, stream)? {
|
||||||
|
stream.expect(Type::LParen)?;
|
||||||
|
let reg = Register::parse(p, stream)?;
|
||||||
|
stream.expect(Type::RParen)?;
|
||||||
|
return Ok(Self::Indexed(reg, idx));
|
||||||
|
}
|
||||||
|
// Or directly match any of the valid prefix markers
|
||||||
|
let token = stream.expect_any_of([Type::Indirect, Type::Absolute, Type::Immediate])?;
|
||||||
|
Ok(match token.variant() {
|
||||||
|
Type::Indirect => {
|
||||||
|
let reg = stream.expect(Type::Register)?.parse()?;
|
||||||
|
match stream.expect(Type::Plus) {
|
||||||
|
Ok(_) => PostInc(reg),
|
||||||
|
Err(_) => Indirect(reg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Absolute => Absolute(Number::parse(p, stream)?),
|
||||||
|
Type::Immediate => {
|
||||||
|
let number = Number::parse(p, stream)?;
|
||||||
|
match number.into() {
|
||||||
|
// There are two representations for the all-ones constant, since Number preserves absolute
|
||||||
|
// signedness.
|
||||||
|
-1 | 0xffff => MinusOne,
|
||||||
|
0 => Zero,
|
||||||
|
1 => One,
|
||||||
|
2 => Two,
|
||||||
|
4 => Four,
|
||||||
|
8 => Eight,
|
||||||
|
_ => Immediate(number),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("Token {token:?} passed expectation but failed match!"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SecondaryOperand> for PrimaryOperand {
|
||||||
|
fn from(value: SecondaryOperand) -> Self {
|
||||||
|
match value {
|
||||||
|
SecondaryOperand::Direct(r) => Self::Direct(r),
|
||||||
|
SecondaryOperand::Indexed(r, n) => Self::Indexed(r, n),
|
||||||
|
SecondaryOperand::Absolute(n) => Self::Absolute(n),
|
||||||
|
SecondaryOperand::Zero => Self::Zero,
|
||||||
|
SecondaryOperand::One => Self::One,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PrimaryOperand {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Direct(r) => write!(f, "{r}"),
|
||||||
|
Self::Indirect(r) => write!(f, "@{r}"),
|
||||||
|
Self::PostInc(r) => write!(f, "@{r}+"),
|
||||||
|
Self::Indexed(r, idx) => write!(f, "{idx}({r})"),
|
||||||
|
Self::Absolute(n) => write!(f, "&{n}"),
|
||||||
|
Self::Immediate(n) => write!(f, "#{n}"),
|
||||||
|
Self::Four => write!(f, "#4"),
|
||||||
|
Self::Eight => write!(f, "#8"),
|
||||||
|
Self::Zero => write!(f, "#0"),
|
||||||
|
Self::One => write!(f, "#1"),
|
||||||
|
Self::Two => write!(f, "#2"),
|
||||||
|
Self::MinusOne => write!(f, "#-1"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
src/parser/instruction/encoding/register.rs
Normal file
111
src/parser/instruction/encoding/register.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! A [Register] epresents [one of the MSP430 processor's registers](https://mspgcc.sourceforge.net/manual/x82.html)
|
||||||
|
use super::*;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// A [Register] epresents [one of the MSP430 processor's registers](https://mspgcc.sourceforge.net/manual/x82.html)
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Register {
|
||||||
|
/// Program Counter
|
||||||
|
pc,
|
||||||
|
/// Stack Pointer
|
||||||
|
sp,
|
||||||
|
/// Status Register
|
||||||
|
sr,
|
||||||
|
/// Constant Generator
|
||||||
|
cg,
|
||||||
|
r4,
|
||||||
|
r5,
|
||||||
|
r6,
|
||||||
|
r7,
|
||||||
|
r8,
|
||||||
|
r9,
|
||||||
|
r10,
|
||||||
|
r11,
|
||||||
|
r12,
|
||||||
|
r13,
|
||||||
|
r14,
|
||||||
|
r15,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for Register {
|
||||||
|
fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: crate::TokenStream<'text> {
|
||||||
|
stream.expect(Type::Register).map_err(|e| e.context(stream.context()))?.lexeme().parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Register> for u16 {
|
||||||
|
fn from(value: Register) -> Self { value as u16 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u16> for Register {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||||
|
use Register::*;
|
||||||
|
Ok(match value {
|
||||||
|
0 => pc,
|
||||||
|
1 => sp,
|
||||||
|
2 => sr,
|
||||||
|
3 => cg,
|
||||||
|
4 => r4,
|
||||||
|
5 => r5,
|
||||||
|
6 => r6,
|
||||||
|
7 => r7,
|
||||||
|
8 => r8,
|
||||||
|
9 => r9,
|
||||||
|
10 => r10,
|
||||||
|
11 => r11,
|
||||||
|
12 => r12,
|
||||||
|
13 => r13,
|
||||||
|
14 => r14,
|
||||||
|
15 => r15,
|
||||||
|
_ => return Err(Error::RegisterTooHigh(value)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Register {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
use Register::*;
|
||||||
|
match s {
|
||||||
|
"pc" => Ok(pc),
|
||||||
|
"sp" => Ok(sp),
|
||||||
|
"sr" => Ok(sr),
|
||||||
|
"cg" => Ok(cg),
|
||||||
|
_ => str::parse::<u16>(&s[1..]).map_err(|_| -> Self::Err { Error::NotARegister(s.into()) })?.try_into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Register> for &str {
|
||||||
|
fn from(value: Register) -> Self {
|
||||||
|
use Register::*;
|
||||||
|
match value {
|
||||||
|
pc => "pc",
|
||||||
|
sp => "sp",
|
||||||
|
sr => "sr",
|
||||||
|
cg => "cg",
|
||||||
|
r4 => "r4",
|
||||||
|
r5 => "r5",
|
||||||
|
r6 => "r6",
|
||||||
|
r7 => "r7",
|
||||||
|
r8 => "r8",
|
||||||
|
r9 => "r9",
|
||||||
|
r10 => "r10",
|
||||||
|
r11 => "r11",
|
||||||
|
r12 => "r12",
|
||||||
|
r13 => "r13",
|
||||||
|
r14 => "r14",
|
||||||
|
r15 => "r15",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Register {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", <&str>::from(*self)) }
|
||||||
|
}
|
95
src/parser/instruction/encoding/secondary_operand.rs
Normal file
95
src/parser/instruction/encoding/secondary_operand.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! A [SecondaryOperand] contains the second [Register], addressing mode, and Extension
|
||||||
|
//! Word for a [two-operand](Encoding::Double) [instruction](Instruction)
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// The destination of a [Double](Encoding::Double)
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum SecondaryOperand {
|
||||||
|
Direct(Register),
|
||||||
|
Indexed(Register, Number),
|
||||||
|
Absolute(Number),
|
||||||
|
// Joke encodings?
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecondaryOperand {
|
||||||
|
pub fn mode(&self) -> u16 {
|
||||||
|
use SecondaryOperand::*;
|
||||||
|
match self {
|
||||||
|
Direct(_) | Zero => 0,
|
||||||
|
Indexed(_, _) | Absolute(_) | One => 1 << 7,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn register(&self) -> Register {
|
||||||
|
use SecondaryOperand::*;
|
||||||
|
match self {
|
||||||
|
Direct(r) | Indexed(r, _) => *r,
|
||||||
|
Absolute(_) => Register::sr,
|
||||||
|
Zero | One => Register::cg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// This is the only way to have an extension word
|
||||||
|
pub fn ext_word(&self) -> Option<u16> {
|
||||||
|
use SecondaryOperand::*;
|
||||||
|
match self {
|
||||||
|
Indexed(_, w) | Absolute(w) => Some((*w).into()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsable for SecondaryOperand {
|
||||||
|
/// Separator
|
||||||
|
/// - Register => Direct
|
||||||
|
/// - Number => Indexed
|
||||||
|
/// - OpenIdx
|
||||||
|
/// - Register
|
||||||
|
/// - CloseIdx
|
||||||
|
/// - Absolute
|
||||||
|
/// - Number
|
||||||
|
/// - Immediate
|
||||||
|
/// - Number == 0, 1
|
||||||
|
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, crate::Error>
|
||||||
|
where T: crate::TokenStream<'text> {
|
||||||
|
use SecondaryOperand::*;
|
||||||
|
stream.allow(Type::Separator);
|
||||||
|
// Try parsing as Register Direct
|
||||||
|
if let Some(r) = Register::try_parse(p, stream)? {
|
||||||
|
return Ok(Self::Direct(r));
|
||||||
|
}
|
||||||
|
// Try parsing as Number Indexed
|
||||||
|
if let Some(idx) = Number::try_parse(p, stream)? {
|
||||||
|
stream.expect(Type::LParen)?;
|
||||||
|
let reg = Register::parse(p, stream)?;
|
||||||
|
stream.expect(Type::RParen)?;
|
||||||
|
return Ok(Self::Indexed(reg, idx));
|
||||||
|
}
|
||||||
|
let token = stream.expect_any_of([Type::Absolute, Type::Immediate])?;
|
||||||
|
Ok(match token.variant() {
|
||||||
|
Type::Absolute => Absolute(Number::parse(p, stream)?),
|
||||||
|
Type::Immediate => {
|
||||||
|
let number = Number::parse(p, stream)?;
|
||||||
|
match number.into() {
|
||||||
|
0 => Zero,
|
||||||
|
1 => One,
|
||||||
|
n => Err(Error::FatSecondaryImmediate(n as isize).context(stream.context()))?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("Token {token:?} passed expectation but failed match!"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SecondaryOperand {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Direct(r) => write!(f, "{r}"),
|
||||||
|
Self::Indexed(r, idx) => write!(f, "{idx}({r})"),
|
||||||
|
Self::Absolute(n) => write!(f, "&{n}"),
|
||||||
|
Self::Zero => write!(f, "#0"),
|
||||||
|
Self::One => write!(f, "#1"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/parser/instruction/encoding/width.rs
Normal file
31
src/parser/instruction/encoding/width.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Represents an instruction's operand width.
|
||||||
|
///
|
||||||
|
/// Evaluates to false when instruction takes word-sized operands, or true when
|
||||||
|
/// instruction takes byte-sized operands
|
||||||
|
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Width(bool);
|
||||||
|
|
||||||
|
impl Parsable for Width {
|
||||||
|
fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: TokenStream<'text> {
|
||||||
|
let Ok(token) = stream.expect_any_of([Type::ByteWidth, Type::WordWidth]) else {
|
||||||
|
return Ok(Self(false));
|
||||||
|
};
|
||||||
|
Ok(Self(token.is_variant(Type::ByteWidth)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Width> for u16 {
|
||||||
|
fn from(value: Width) -> Self { (value.0 as Self) << 6 }
|
||||||
|
}
|
||||||
|
impl From<Width> for bool {
|
||||||
|
fn from(value: Width) -> Self { value.0 }
|
||||||
|
}
|
||||||
|
impl From<bool> for Width {
|
||||||
|
fn from(value: bool) -> Self { Width(value) }
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for Width {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(if self.0 { ".b" } else { "" }) }
|
||||||
|
}
|
258
src/parser/instruction/opcode.rs
Normal file
258
src/parser/instruction/opcode.rs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! An [Opcode] encodes an msp430 operation
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// Opcode from the [MSPGCC Manual][1]
|
||||||
|
///
|
||||||
|
/// Calling [`resolve()`](Opcode::resolve()) will emit an [EncodingParser] which will
|
||||||
|
/// extract from a [TokenStream] only the required arguments for that call.
|
||||||
|
///
|
||||||
|
/// [1]: https://mspgcc.sourceforge.net/manual/x223.html
|
||||||
|
#[allow(clippy::identity_op)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Opcode {
|
||||||
|
// "Emulated" opcodes
|
||||||
|
Nop,
|
||||||
|
Pop,
|
||||||
|
Br,
|
||||||
|
Ret,
|
||||||
|
Clrc,
|
||||||
|
Setc,
|
||||||
|
Clrz,
|
||||||
|
Setz,
|
||||||
|
Clrn,
|
||||||
|
Setn,
|
||||||
|
Dint,
|
||||||
|
Eint,
|
||||||
|
Rla,
|
||||||
|
Rlc,
|
||||||
|
Inv,
|
||||||
|
Clr,
|
||||||
|
Tst,
|
||||||
|
Dec,
|
||||||
|
Decd,
|
||||||
|
Inc,
|
||||||
|
Incd,
|
||||||
|
Adc,
|
||||||
|
Dadc,
|
||||||
|
Sbc,
|
||||||
|
// Single
|
||||||
|
Rrc = 0x1000 | 0 << 7,
|
||||||
|
Swpb = 0x1000 | 1 << 7,
|
||||||
|
Rra = 0x1000 | 2 << 7,
|
||||||
|
Sxt = 0x1000 | 3 << 7,
|
||||||
|
Push = 0x1000 | 4 << 7,
|
||||||
|
Call = 0x1000 | 5 << 7,
|
||||||
|
Reti = 0x1000 | 6 << 7,
|
||||||
|
// Jump
|
||||||
|
Jnz = 0x2000 | 0 << 10,
|
||||||
|
Jz = 0x2000 | 1 << 10,
|
||||||
|
Jnc = 0x2000 | 2 << 10,
|
||||||
|
Jc = 0x2000 | 3 << 10,
|
||||||
|
Jn = 0x2000 | 4 << 10,
|
||||||
|
Jge = 0x2000 | 5 << 10,
|
||||||
|
Jl = 0x2000 | 6 << 10,
|
||||||
|
Jmp = 0x2000 | 7 << 10,
|
||||||
|
// Double
|
||||||
|
Mov = 0x4000,
|
||||||
|
Add = 0x5000,
|
||||||
|
Addc = 0x6000,
|
||||||
|
Subc = 0x7000,
|
||||||
|
Sub = 0x8000,
|
||||||
|
Cmp = 0x9000,
|
||||||
|
Dadd = 0xa000,
|
||||||
|
Bit = 0xb000,
|
||||||
|
Bic = 0xc000,
|
||||||
|
Bis = 0xd000,
|
||||||
|
Xor = 0xe000,
|
||||||
|
And = 0xf000,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Opcode {
|
||||||
|
pub fn takes_width(&self) -> bool {
|
||||||
|
use Opcode::*;
|
||||||
|
match self {
|
||||||
|
Rrc => true,
|
||||||
|
Swpb => false,
|
||||||
|
Rra => true,
|
||||||
|
Sxt => false,
|
||||||
|
Push => true,
|
||||||
|
Call | Reti => false,
|
||||||
|
Jnz | Jz | Jnc | Jc | Jn | Jge | Jl | Jmp => false,
|
||||||
|
Mov | Add | Addc | Subc | Sub | Cmp | Dadd | Bit | Bic | Bis | Xor | And => true,
|
||||||
|
Nop | Pop | Br | Ret | Clrc | Setc | Clrz | Setz | Clrn | Setn | Dint | Eint | Rla | Rlc | Inv | Clr
|
||||||
|
| Tst | Dec | Decd | Inc | Incd | Adc | Dadc | Sbc => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Resolve an Opcode into an [Opcode] and an [EncodingParser]
|
||||||
|
pub fn resolve(self) -> (Opcode, EncodingParser) {
|
||||||
|
use super::Encoding as Enc;
|
||||||
|
use Opcode::*;
|
||||||
|
use Register::*;
|
||||||
|
use {PrimaryOperand as Src, SecondaryOperand as Dst};
|
||||||
|
match self {
|
||||||
|
Rrc | Swpb | Rra | Sxt | Push | Call | Reti => (self, Enc::single().end()),
|
||||||
|
Jnz | Jz | Jnc | Jc | Jn | Jge | Jl | Jmp => (self, Enc::jump().end()),
|
||||||
|
Mov | Add | Addc | Subc | Sub | Cmp | Dadd | Bit | Bic | Bis | Xor | And => (self, Enc::double().end()),
|
||||||
|
Nop => (Mov, Enc::double().src(Src::Zero).dst(Dst::Zero).end()),
|
||||||
|
Pop => (Mov, Enc::double().src(Src::PostInc(sp)).end()),
|
||||||
|
Br => (Mov, Enc::double().dst(Dst::Direct(pc)).end()),
|
||||||
|
Ret => (Mov, Enc::double().src(Src::PostInc(sp)).dst(Dst::Direct(pc)).end()),
|
||||||
|
Clrc => (Bic, Enc::double().src(Src::One).dst(Dst::Direct(sr)).end()),
|
||||||
|
Setc => (Bis, Enc::double().src(Src::One).dst(Dst::Direct(sr)).end()),
|
||||||
|
Clrz => (Bic, Enc::double().src(Src::Two).dst(Dst::Direct(sr)).end()),
|
||||||
|
Setz => (Bis, Enc::double().src(Src::Two).dst(Dst::Direct(sr)).end()),
|
||||||
|
Clrn => (Bic, Enc::double().src(Src::Four).dst(Dst::Direct(sr)).end()),
|
||||||
|
Setn => (Bis, Enc::double().src(Src::Four).dst(Dst::Direct(sr)).end()),
|
||||||
|
Dint => (Bic, Enc::double().src(Src::Eight).dst(Dst::Direct(sr)).end()),
|
||||||
|
Eint => (Bis, Enc::double().src(Src::Eight).dst(Dst::Direct(sr)).end()),
|
||||||
|
Rla => (Add, Enc::reflexive().end()),
|
||||||
|
Rlc => (Addc, Enc::reflexive().end()),
|
||||||
|
Inv => (Xor, Enc::double().src(Src::MinusOne).end()),
|
||||||
|
Clr => (Mov, Enc::double().src(Src::Zero).end()),
|
||||||
|
Tst => (Cmp, Enc::double().src(Src::Zero).end()),
|
||||||
|
Dec => (Sub, Enc::double().src(Src::One).end()),
|
||||||
|
Decd => (Sub, Enc::double().src(Src::Two).end()),
|
||||||
|
Inc => (Add, Enc::double().src(Src::One).end()),
|
||||||
|
Incd => (Add, Enc::double().src(Src::Two).end()),
|
||||||
|
Adc => (Addc, Enc::double().src(Src::Zero).end()),
|
||||||
|
Dadc => (Dadd, Enc::double().src(Src::Zero).end()),
|
||||||
|
Sbc => (Subc, Enc::double().src(Src::Zero).end()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Opcode {
|
||||||
|
type Err = Error;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
use Opcode::*;
|
||||||
|
//TODO: Reduce allocations here
|
||||||
|
let s = s.to_ascii_lowercase();
|
||||||
|
Ok(match s.as_str() {
|
||||||
|
"rrc" => Rrc,
|
||||||
|
"swpb" => Swpb,
|
||||||
|
"rra" => Rra,
|
||||||
|
"sxt" => Sxt,
|
||||||
|
"push" => Push,
|
||||||
|
"call" => Call,
|
||||||
|
"reti" => Reti,
|
||||||
|
|
||||||
|
"jne" | "jnz" => Jnz,
|
||||||
|
"jeq" | "jz" => Jz,
|
||||||
|
"jnc" | "jlo" => Jnc,
|
||||||
|
"jc" | "jhs" => Jc,
|
||||||
|
"jn" => Jn,
|
||||||
|
"jge" => Jge,
|
||||||
|
"jl" => Jl,
|
||||||
|
"jmp" => Jmp,
|
||||||
|
|
||||||
|
"mov" => Mov,
|
||||||
|
"add" => Add,
|
||||||
|
"addc" => Addc,
|
||||||
|
"subc" => Subc,
|
||||||
|
"sub" => Sub,
|
||||||
|
"cmp" => Cmp,
|
||||||
|
"dadd" => Dadd,
|
||||||
|
"bit" => Bit,
|
||||||
|
"bic" => Bic,
|
||||||
|
"bis" => Bis,
|
||||||
|
"xor" => Xor,
|
||||||
|
"and" => And,
|
||||||
|
|
||||||
|
"nop" => Nop,
|
||||||
|
"pop" => Pop,
|
||||||
|
"br" => Br,
|
||||||
|
"ret" => Ret,
|
||||||
|
"clrc" => Clrc,
|
||||||
|
"setc" => Setc,
|
||||||
|
"clrz" => Clrz,
|
||||||
|
"setz" => Setz,
|
||||||
|
"clrn" => Clrn,
|
||||||
|
"setn" => Setn,
|
||||||
|
"dint" => Dint,
|
||||||
|
"eint" => Eint,
|
||||||
|
"rla" => Rla,
|
||||||
|
"rlc" => Rlc,
|
||||||
|
"inv" => Inv,
|
||||||
|
"clr" => Clr,
|
||||||
|
"tst" => Tst,
|
||||||
|
"dec" => Dec,
|
||||||
|
"decd" => Decd,
|
||||||
|
"inc" => Inc,
|
||||||
|
"incd" => Incd,
|
||||||
|
"adc" => Adc,
|
||||||
|
"dadc" => Dadc,
|
||||||
|
"sbc" => Sbc,
|
||||||
|
_ => Err(Error::UnrecognizedOpcode(s))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Opcode {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
use Opcode::*;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self {
|
||||||
|
Nop => "nop",
|
||||||
|
Pop => "pop",
|
||||||
|
Br => "br",
|
||||||
|
Ret => "ret",
|
||||||
|
Clrc => "clrc",
|
||||||
|
Setc => "setc",
|
||||||
|
Clrz => "clrz",
|
||||||
|
Setz => "setz",
|
||||||
|
Clrn => "clrn",
|
||||||
|
Setn => "setn",
|
||||||
|
Dint => "dint",
|
||||||
|
Eint => "eint",
|
||||||
|
Rla => "rla",
|
||||||
|
Rlc => "rlc",
|
||||||
|
Inv => "inv",
|
||||||
|
Clr => "clr",
|
||||||
|
Tst => "tst",
|
||||||
|
Dec => "dec",
|
||||||
|
Decd => "decd",
|
||||||
|
Inc => "inc",
|
||||||
|
Incd => "incd",
|
||||||
|
Adc => "adc",
|
||||||
|
Dadc => "dadc",
|
||||||
|
Sbc => "sbc",
|
||||||
|
Rrc => "rrc",
|
||||||
|
Swpb => "swpb",
|
||||||
|
Rra => "rra",
|
||||||
|
Sxt => "sxt",
|
||||||
|
Push => "push",
|
||||||
|
Call => "call",
|
||||||
|
Reti => "reti",
|
||||||
|
Jnz => "jnz",
|
||||||
|
Jz => "jz",
|
||||||
|
Jnc => "jnc",
|
||||||
|
Jc => "jc",
|
||||||
|
Jn => "jn",
|
||||||
|
Jge => "jge",
|
||||||
|
Jl => "jl",
|
||||||
|
Jmp => "jmp",
|
||||||
|
Mov => "mov",
|
||||||
|
Add => "add",
|
||||||
|
Addc => "addc",
|
||||||
|
Subc => "subc",
|
||||||
|
Sub => "sub",
|
||||||
|
Cmp => "cmp",
|
||||||
|
Dadd => "dadd",
|
||||||
|
Bit => "bit",
|
||||||
|
Bic => "bic",
|
||||||
|
Bis => "bis",
|
||||||
|
Xor => "xor",
|
||||||
|
And => "and",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LowerHex for Opcode {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:04x}", *self as u16) }
|
||||||
|
}
|
16
src/parser/label.rs
Normal file
16
src/parser/label.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Label(pub Identifier);
|
||||||
|
|
||||||
|
impl Parsable for Label {
|
||||||
|
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where T: TokenStream<'text> {
|
||||||
|
Ok(Self(Identifier::parse(p, stream).and_then(|t| stream.require(Type::Label).and(Ok(t)))?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Label {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}:", self.0) }
|
||||||
|
}
|
44
src/parser/parsable.rs
Normal file
44
src/parser/parsable.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
use super::*;
|
||||||
|
/// Parses tokens from [stream](TokenStream) into Self node
|
||||||
|
pub trait Parsable {
|
||||||
|
/// Parses tokens from [TokenStream](TokenStream) into Self nodes
|
||||||
|
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
T: TokenStream<'text>;
|
||||||
|
|
||||||
|
/// Attempts to parse tokens from [stream](TokenStream) into Self nodes.
|
||||||
|
///
|
||||||
|
/// Masks failed expectations.
|
||||||
|
fn try_parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Option<Self>, Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
T: TokenStream<'text>,
|
||||||
|
{
|
||||||
|
match Self::parse(p, stream).map_err(|e| e.bare()) {
|
||||||
|
Ok(tt) => Ok(Some(tt)),
|
||||||
|
Err(Error::UnexpectedToken { .. }) | Err(Error::AllExpectationsFailed { .. }) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_and<'text, T, R>(p: &Parser, stream: &mut T, f: fn(p: &Parser, &mut T) -> R) -> Result<(Self, R), Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
T: TokenStream<'text>,
|
||||||
|
{
|
||||||
|
Ok((Self::parse(p, stream)?, f(p, stream)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to parse tokens from [stream](TokenStream) into Self nodes.
|
||||||
|
///
|
||||||
|
/// Returns [`Self::default()`](Default::default()) on error
|
||||||
|
fn parse_or_default<'text, T>(p: &Parser, stream: &mut T) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized + Default,
|
||||||
|
T: TokenStream<'text>,
|
||||||
|
{
|
||||||
|
Self::parse(p, stream).unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
193
src/tokenizer.rs
Normal file
193
src/tokenizer.rs
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! Iterates over &[str], producing [Token]s
|
||||||
|
|
||||||
|
// Things we need:
|
||||||
|
// ✔ 1. Lexer/Tokenizer
|
||||||
|
// ✔ 1. Instructions
|
||||||
|
// ✔ 1. Instruction mnemonics /ad.../
|
||||||
|
// ✔ 2. Byte/Word Mode Marker /(.\[bw\])?/
|
||||||
|
// ✔ 2. Src operands
|
||||||
|
// ✔ 1. Registers /(r1[0-5]|r[0-9])/
|
||||||
|
// ✔ 2. Immediate Values /#/
|
||||||
|
// ✔ 3. Absolute addresses /&/
|
||||||
|
// ✔ 4. Numbers /[0-9A-Fa-f]+
|
||||||
|
// ✔ 5. Jump Offsets: basically numbers /$?([+-]?[0-9A-Fa-f]{1,4})/
|
||||||
|
// ✔ 4. Label definitions /(^.*):/
|
||||||
|
// ✔ 5. Comments (may be useful for debugging)
|
||||||
|
|
||||||
|
pub mod context;
|
||||||
|
pub mod token;
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
use context::Context;
|
||||||
|
use token::{Token, Type};
|
||||||
|
|
||||||
|
/// Backtracking through bifurcated timelines
|
||||||
|
pub trait TokenStream<'text>: Iterator<Item = Token<'text>> {
|
||||||
|
/// Gets this stream's [Context]
|
||||||
|
fn context(&self) -> Context;
|
||||||
|
|
||||||
|
/// Creates an iterator that skips [Type::Space] in the input
|
||||||
|
fn ignore_spaces(&'text mut self) -> IgnoreSpaces<'text, Self>
|
||||||
|
where Self: Sized {
|
||||||
|
IgnoreSpaces::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the next [Token] without advancing
|
||||||
|
fn peek(&mut self) -> Self::Item;
|
||||||
|
|
||||||
|
/// Returns the next [Token] if it is of the expected [Type], without advancing
|
||||||
|
fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, Error>;
|
||||||
|
|
||||||
|
/// Consumes and returns a [Token] if it is the expected [Type]
|
||||||
|
///
|
||||||
|
/// Otherwise, does not consume a [Token]
|
||||||
|
fn expect(&mut self, expected: Type) -> Result<Self::Item, Error>;
|
||||||
|
|
||||||
|
/// Ignores a [Token] of the expected [Type], propegating errors.
|
||||||
|
fn require(&mut self, expected: Type) -> Result<(), Error> { self.expect(expected).map(|_| ()) }
|
||||||
|
|
||||||
|
/// Ignores a [Token] of the expected [Type], discarding errors.
|
||||||
|
fn allow(&mut self, expected: Type) { let _ = self.expect(expected); }
|
||||||
|
|
||||||
|
/// Runs a functor on each
|
||||||
|
fn any_of<T, U>(&mut self, f: fn(&mut Self, Type) -> Result<U, Error>, expected: T) -> Result<U, Error>
|
||||||
|
where T: AsRef<[Type]> {
|
||||||
|
for &expected in expected.as_ref() {
|
||||||
|
match f(self, expected).map_err(|e| e.bare()) {
|
||||||
|
Ok(t) => return Ok(t),
|
||||||
|
Err(Error::UnexpectedToken { .. }) => continue,
|
||||||
|
Err(e) => return Err(e.context(self.context())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::expected(expected, self.peek()).context(self.context()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the next [Token] if it is of the expected [Types](Type), without advancing
|
||||||
|
fn peek_expect_any_of<T>(&mut self, expected: T) -> Result<Self::Item, Error>
|
||||||
|
where T: AsRef<[Type]> {
|
||||||
|
self.any_of(Self::peek_expect, expected)
|
||||||
|
}
|
||||||
|
/// Consumes and returns a [Token] if it matches any of the expected [Types](Type)
|
||||||
|
///
|
||||||
|
/// Otherwise, does not consume a [Token]
|
||||||
|
fn expect_any_of<T>(&mut self, expected: T) -> Result<Self::Item, Error>
|
||||||
|
where T: AsRef<[Type]> {
|
||||||
|
self.any_of(Self::expect, expected)
|
||||||
|
}
|
||||||
|
/// Ignores a [Token] of any expected [Type], discarding errors.
|
||||||
|
fn allow_any_of<T>(&mut self, expected: T)
|
||||||
|
where T: AsRef<[Type]> {
|
||||||
|
let _ = self.expect_any_of(expected);
|
||||||
|
}
|
||||||
|
/// Ignores a [Token] of any expected [Type], propegating errors.
|
||||||
|
fn require_any_of<T>(&mut self, expected: T) -> Result<(), Error>
|
||||||
|
where T: AsRef<[Type]> {
|
||||||
|
self.any_of(Self::require, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates over &[str], producing [Token]s
|
||||||
|
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Tokenizer<'t> {
|
||||||
|
text: &'t str,
|
||||||
|
idx: usize,
|
||||||
|
context: Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t> Tokenizer<'t> {
|
||||||
|
/// Produces a new [Tokenizer] from a [str]ing slice
|
||||||
|
pub fn new<T>(text: &'t T) -> Self
|
||||||
|
where T: AsRef<str> + ?Sized {
|
||||||
|
Tokenizer { text: text.as_ref(), idx: 0, context: Default::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count(&mut self, token: &Token) {
|
||||||
|
// update the context
|
||||||
|
self.context.count(token);
|
||||||
|
// advance the index
|
||||||
|
self.idx += token.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'text> Iterator for Tokenizer<'text> {
|
||||||
|
type Item = Token<'text>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.idx >= self.text.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let token = Token::from(&self.text[self.idx..]);
|
||||||
|
// Process [Type::Directive]s
|
||||||
|
self.count(&token);
|
||||||
|
Some(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'text> TokenStream<'text> for Tokenizer<'text> {
|
||||||
|
fn context(&self) -> Context { self.context }
|
||||||
|
// Tokenizer has access to the source buffer, and can implement expect and peek without cloning
|
||||||
|
// itself. This can go wrong, of course, if an [Identifier] is expected, since all instructions and
|
||||||
|
// registers are valid identifiers.
|
||||||
|
fn expect(&mut self, expected: Type) -> Result<Self::Item, Error> {
|
||||||
|
let token = Token::expect(&self.text[self.idx..], expected).map_err(|e| e.context(self.context()))?;
|
||||||
|
self.count(&token);
|
||||||
|
Ok(token)
|
||||||
|
}
|
||||||
|
fn peek(&mut self) -> Self::Item { Token::from(&self.text[self.idx..]) }
|
||||||
|
fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, Error> {
|
||||||
|
Token::expect(&self.text[self.idx..], expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct IgnoreSpaces<'t, T>
|
||||||
|
where T: TokenStream<'t>
|
||||||
|
{
|
||||||
|
inner: &'t mut T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t, T> IgnoreSpaces<'t, T>
|
||||||
|
where T: TokenStream<'t>
|
||||||
|
{
|
||||||
|
pub fn new(t: &'t mut T) -> Self { IgnoreSpaces { inner: t } }
|
||||||
|
/// Gets a mutable reference to the inner [Iterator]
|
||||||
|
pub fn inner_mut(&mut self) -> &mut T { self.inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t, T> Iterator for IgnoreSpaces<'t, T>
|
||||||
|
where T: TokenStream<'t>
|
||||||
|
{
|
||||||
|
type Item = Token<'t>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let next = self.inner.next()?;
|
||||||
|
// Space tokens are greedy, so the next token shouldn't be a Space
|
||||||
|
match next.variant() {
|
||||||
|
Type::Space => self.next(),
|
||||||
|
_ => Some(next),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t, T> TokenStream<'t> for IgnoreSpaces<'t, T>
|
||||||
|
where T: TokenStream<'t>
|
||||||
|
{
|
||||||
|
fn context(&self) -> Context { self.inner.context() }
|
||||||
|
fn expect(&mut self, expected: Type) -> Result<Self::Item, Error> {
|
||||||
|
self.inner.allow_any_of([Type::Space, Type::Endl]);
|
||||||
|
self.inner.expect(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&mut self) -> Self::Item {
|
||||||
|
self.inner.allow_any_of([Type::Space, Type::Endl]);
|
||||||
|
self.inner.peek()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, Error> {
|
||||||
|
self.inner.allow_any_of([Type::Space, Type::Endl]);
|
||||||
|
self.inner.peek_expect(expected)
|
||||||
|
}
|
||||||
|
}
|
36
src/tokenizer/context.rs
Normal file
36
src/tokenizer/context.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//! Stores contextual information about the current tokenizer state, useful for printing errors
|
||||||
|
use super::*;
|
||||||
|
/// Stores contextual information about the current tokenizer state, useful for printing errors
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Context {
|
||||||
|
line: usize,
|
||||||
|
tokens: usize,
|
||||||
|
position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new() -> Self { Default::default() }
|
||||||
|
pub fn line(&self) -> usize { self.line }
|
||||||
|
pub fn tokens(&self) -> usize { self.tokens }
|
||||||
|
pub fn position(&self) -> usize { self.position }
|
||||||
|
pub(super) fn count(&mut self, t: &Token) {
|
||||||
|
match t.variant() {
|
||||||
|
Type::EndOfFile => return,
|
||||||
|
Type::Endl => {
|
||||||
|
self.line += 1;
|
||||||
|
self.position = 0;
|
||||||
|
}
|
||||||
|
_ => self.position += t.len(),
|
||||||
|
}
|
||||||
|
self.tokens += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Default for Context {
|
||||||
|
fn default() -> Self { Self { line: 1, tokens: 0, position: 0 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Context {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}:{}", self.line, self.position)
|
||||||
|
}
|
||||||
|
}
|
309
src/tokenizer/token.rs
Normal file
309
src/tokenizer/token.rs
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
// © 2023 John Breaux
|
||||||
|
//! Defines the [Token]
|
||||||
|
//!
|
||||||
|
//! A [Token] represents all valid sequences of characters,
|
||||||
|
//! sorted by meaning
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
use std::{
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
sync::OnceLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Implements regex matching functions on [`Token`] for each [`Type`],
|
||||||
|
/// and implements [`From<&str>`] for [`Token`]
|
||||||
|
macro_rules! regex_impl {
|
||||||
|
(<$t:lifetime> $type:ty {$(
|
||||||
|
$(#[$meta:meta])*
|
||||||
|
pub fn $func:ident (text: &str) -> Option<Self> {
|
||||||
|
regex!($out:path = $re:literal)
|
||||||
|
}
|
||||||
|
)*}) => {
|
||||||
|
impl<$t> $type {
|
||||||
|
/// Lexes a token only for the expected `variant`
|
||||||
|
///
|
||||||
|
/// Warning: This bypasses precedence rules. Only use for specific patterns.
|
||||||
|
pub fn expect(text: &$t str, expected: Type) -> Result<Self, Error> {
|
||||||
|
match expected {$(
|
||||||
|
$out => Self::$func(text),
|
||||||
|
)*}.ok_or(Error::UnexpectedToken {
|
||||||
|
expected,
|
||||||
|
got: Self::from(text).into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
$(
|
||||||
|
$(#[$meta])*
|
||||||
|
/// Tries to read [`
|
||||||
|
#[doc = stringify!($out)]
|
||||||
|
/// `] from `text`
|
||||||
|
pub fn $func(text: &$t str) -> Option<Self> {
|
||||||
|
static RE: OnceLock<Regex> = OnceLock::new();
|
||||||
|
let lexeme = RE.get_or_init(|| Regex::new($re).unwrap())
|
||||||
|
.find(text)?.into();
|
||||||
|
Some(Self { variant: $out, lexeme })
|
||||||
|
})*
|
||||||
|
}
|
||||||
|
impl<$t> From<&$t str> for $type {
|
||||||
|
fn from (value: &$t str) -> Self {
|
||||||
|
$(
|
||||||
|
if let Some(token) = Self::$func(value) {
|
||||||
|
token
|
||||||
|
} else
|
||||||
|
)*
|
||||||
|
{todo!("Unexpected input: {value:#?}")}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Token<'text> {
|
||||||
|
/// The type of this token
|
||||||
|
variant: Type,
|
||||||
|
/// The sub[str]ing corresponding to this token
|
||||||
|
lexeme: &'text str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'text> Token<'text> {
|
||||||
|
/// Returns the [Type] of this [Token]
|
||||||
|
pub fn variant(&self) -> Type { self.variant }
|
||||||
|
|
||||||
|
/// Returns the Lexeme (originating string slice) of this token
|
||||||
|
pub fn lexeme(&self) -> &str { self.lexeme }
|
||||||
|
|
||||||
|
/// Parses this [Token] into another type
|
||||||
|
pub fn parse<F>(&self) -> Result<F, <F as std::str::FromStr>::Err>
|
||||||
|
where F: std::str::FromStr {
|
||||||
|
self.lexeme.parse()
|
||||||
|
}
|
||||||
|
/// Returns whether the Lexeme is the expected [Type]
|
||||||
|
pub fn is_variant(&self, expected: Type) -> bool { self.variant == expected }
|
||||||
|
|
||||||
|
/// Returns the length of [Self::lexeme] in bytes.
|
||||||
|
pub fn len(&self) -> usize { self.lexeme.len() }
|
||||||
|
|
||||||
|
/// Returns `true` if [Self::lexeme] has a length of zero bytes.
|
||||||
|
pub fn is_empty(&self) -> bool { self.lexeme.is_empty() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'text> Debug for Token<'text> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_list().entry(&self.variant).entry(&self.lexeme).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'text> Display for Token<'text> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self.variant {
|
||||||
|
Type::Endl | Type::EndOfFile => write!(f, "{}", self.variant),
|
||||||
|
v => write!(f, "\"{}\" ({v})", self.lexeme),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum Type {
|
||||||
|
/// contiguous whitespace, excluding newline
|
||||||
|
Space,
|
||||||
|
/// newline and contiguous whitespace
|
||||||
|
Endl,
|
||||||
|
/// A line-comment
|
||||||
|
Comment,
|
||||||
|
/// Jump label *definition*
|
||||||
|
Label,
|
||||||
|
/// Instructions
|
||||||
|
Insn,
|
||||||
|
/// Operand width is byte
|
||||||
|
ByteWidth,
|
||||||
|
/// Operand width is word
|
||||||
|
WordWidth,
|
||||||
|
/// Register mnemonic (i.e. `pc`, `r14`)
|
||||||
|
Register,
|
||||||
|
/// Marker for base-10
|
||||||
|
RadixMarkerDec,
|
||||||
|
/// Marker for base-16
|
||||||
|
RadixMarkerHex,
|
||||||
|
/// Marker for base-8
|
||||||
|
RadixMarkerOct,
|
||||||
|
/// Marker for base-2
|
||||||
|
RadixMarkerBin,
|
||||||
|
/// 1-4 hexadigit numbers only
|
||||||
|
Number,
|
||||||
|
/// Negative number marker
|
||||||
|
Minus,
|
||||||
|
/// post-increment mode marker
|
||||||
|
Plus,
|
||||||
|
/// Open-Indexed-Mode marker
|
||||||
|
LParen,
|
||||||
|
/// Close-Indexed-Mode marker
|
||||||
|
RParen,
|
||||||
|
/// Indirect mode marker
|
||||||
|
Indirect,
|
||||||
|
/// absolute address marker
|
||||||
|
Absolute,
|
||||||
|
/// immediate value marker
|
||||||
|
Immediate,
|
||||||
|
/// Valid identifier. Identifiers must start with a Latin alphabetic character or underline
|
||||||
|
Identifier,
|
||||||
|
/// Assembler directive
|
||||||
|
Directive,
|
||||||
|
/// Separator (comma)
|
||||||
|
Separator,
|
||||||
|
/// End of File marker
|
||||||
|
EndOfFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
regex_impl! {<'text> Token<'text> {
|
||||||
|
pub fn expect_space(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Space = r"^[\s--\n]+")
|
||||||
|
}
|
||||||
|
pub fn expect_endl(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Endl = r"^[\s]+")
|
||||||
|
}
|
||||||
|
pub fn expect_comment(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Comment = r"^(;|//).*")
|
||||||
|
}
|
||||||
|
pub fn expect_label(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Label = r"^:")
|
||||||
|
}
|
||||||
|
pub fn expect_insn(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Insn = r"(?i)^(adc|addc?|and|bi[cs]|bitb?|br|call|clr[cnz]?|cmp|dad[cd]|decd?|[de]int|incd?|inv|j([cz]|eq|ge|hs|lo?|mp|n[cez]?)|mov|[np]op|push|reti?|r[lr][ac]|sbc|set[cnz]|subc?|swpb|sxt|tst|xor)(?-u:\b)")
|
||||||
|
}
|
||||||
|
pub fn expect_byte_width(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::ByteWidth = r"(?i)^\.b")
|
||||||
|
}
|
||||||
|
pub fn expect_word_width(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::WordWidth = r"(?i)^\.w")
|
||||||
|
}
|
||||||
|
pub fn expect_register(text: &str) -> Option<Self> {
|
||||||
|
// old regex regex!(Type::Register = r"(?i)^(r(1[0-5]|[0-9])|pc|s[pr]|cg)")
|
||||||
|
regex!(Type::Register = r"(?i)^(r\d+|pc|s[pr]|cg)")
|
||||||
|
}
|
||||||
|
pub fn expect_radix_marker_dec(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::RadixMarkerDec = r"(?i)^0d")
|
||||||
|
}
|
||||||
|
pub fn expect_radix_marker_hex(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::RadixMarkerHex = r"(?i)^(0x|\$)")
|
||||||
|
}
|
||||||
|
pub fn expect_radix_marker_oct(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::RadixMarkerOct = r"(?i)^0o")
|
||||||
|
}
|
||||||
|
pub fn expect_radix_marker_bin(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::RadixMarkerBin = r"(?i)^0b")
|
||||||
|
}
|
||||||
|
pub fn expect_number(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Number = r"^+?[[:xdigit:]]{1,5}")
|
||||||
|
}
|
||||||
|
pub fn expect_minus(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Minus = r"^-")
|
||||||
|
}
|
||||||
|
pub fn expect_plus(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Plus = r"^\+")
|
||||||
|
}
|
||||||
|
pub fn expect_open_idx(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::LParen = r"^\(")
|
||||||
|
}
|
||||||
|
pub fn expect_close_idx(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::RParen = r"^\)")
|
||||||
|
}
|
||||||
|
pub fn expect_indrect(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Indirect = r"^@")
|
||||||
|
}
|
||||||
|
pub fn expect_absolute(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Absolute = r"^&")
|
||||||
|
}
|
||||||
|
pub fn expect_immediate(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Immediate = r"^#")
|
||||||
|
}
|
||||||
|
pub fn expect_directive(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Directive = r"^\.\w+( .*)?")
|
||||||
|
}
|
||||||
|
pub fn expect_identifier(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Identifier = r"^[A-Za-z_]\w+")
|
||||||
|
}
|
||||||
|
pub fn expect_separator(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::Separator = r"^,")
|
||||||
|
}
|
||||||
|
pub fn expect_end_of_file(text: &str) -> Option<Self> {
|
||||||
|
regex!(Type::EndOfFile = r"^$")
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
impl Display for Type {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Space => write!(f, "space"),
|
||||||
|
Self::Endl => write!(f, "newline"),
|
||||||
|
Self::Comment => write!(f, "comment"),
|
||||||
|
Self::Label => write!(f, "label definition"),
|
||||||
|
Self::Insn => write!(f, "instruction mnemonic"),
|
||||||
|
Self::ByteWidth => write!(f, "byte-width marker"),
|
||||||
|
Self::WordWidth => write!(f, "word-width marker"),
|
||||||
|
Self::Register => write!(f, "register mnemonic"),
|
||||||
|
Self::RadixMarkerDec => write!(f, "decimal radix marker"),
|
||||||
|
Self::RadixMarkerHex => write!(f, "hexadecimal radix marker"),
|
||||||
|
Self::RadixMarkerOct => write!(f, "octal radix marker"),
|
||||||
|
Self::RadixMarkerBin => write!(f, "binary radix marker"),
|
||||||
|
Self::Number => write!(f, "number"),
|
||||||
|
Self::Minus => write!(f, "minus sign"),
|
||||||
|
Self::Plus => write!(f, "plus sign"),
|
||||||
|
Self::LParen => write!(f, "left parenthesis"),
|
||||||
|
Self::RParen => write!(f, "right parenthesis"),
|
||||||
|
Self::Indirect => write!(f, "indirect mode marker"),
|
||||||
|
Self::Absolute => write!(f, "absolute mode marker"),
|
||||||
|
Self::Immediate => write!(f, "immediate mode marker"),
|
||||||
|
Self::Identifier => write!(f, "identifier"),
|
||||||
|
Self::Directive => write!(f, "directive"),
|
||||||
|
Self::Separator => write!(f, "comma"),
|
||||||
|
Self::EndOfFile => write!(f, "EOF"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Owned version of a token, which can outlive its parent buffer
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct OwnedToken {
|
||||||
|
/// The type of this token
|
||||||
|
variant: Type,
|
||||||
|
/// The sub[String] corresponding to this token
|
||||||
|
lexeme: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for OwnedToken {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", Token::from(self)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'t> From<&'t OwnedToken> for Token<'t> {
|
||||||
|
fn from(value: &'t OwnedToken) -> Self { Token { variant: value.variant, lexeme: &value.lexeme } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Token<'_>> for OwnedToken {
|
||||||
|
fn from(value: Token<'_>) -> Self {
|
||||||
|
let Token { variant, lexeme } = value;
|
||||||
|
OwnedToken { variant, lexeme: lexeme.to_owned() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Types(Vec<Type>);
|
||||||
|
|
||||||
|
impl<T: AsRef<[Type]>> From<T> for Types {
|
||||||
|
// TODO: Possibly bad. Check out in rust playground.
|
||||||
|
fn from(value: T) -> Self { Self(value.as_ref().to_owned()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Types {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for (idx, t) in self.0.iter().enumerate() {
|
||||||
|
write!(f, "{t}")?;
|
||||||
|
match idx {
|
||||||
|
i if i < self.0.len() - 2 => write!(f, ", ")?,
|
||||||
|
i if i < self.0.len() - 1 => write!(f, " or ")?,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
260
valid.asm
Executable file
260
valid.asm
Executable file
@ -0,0 +1,260 @@
|
|||||||
|
;© 2023 John Breaux
|
||||||
|
; examples of valid assembly
|
||||||
|
;
|
||||||
|
|
||||||
|
_register_mode:
|
||||||
|
.define numbered r1
|
||||||
|
mov r0, r1
|
||||||
|
mov r1, r2
|
||||||
|
mov r2, r3
|
||||||
|
mov r3, r4
|
||||||
|
mov r4, r5
|
||||||
|
mov r5, r6
|
||||||
|
mov r6, r7
|
||||||
|
mov r7, r8
|
||||||
|
mov r8, r9
|
||||||
|
mov r9, r10
|
||||||
|
mov r10, r11
|
||||||
|
mov r11, r12
|
||||||
|
mov r12, r13
|
||||||
|
mov r13, r14
|
||||||
|
mov r14, r15
|
||||||
|
|
||||||
|
.define special r2
|
||||||
|
mov pc, r15
|
||||||
|
mov sp, r15
|
||||||
|
mov sr, r15
|
||||||
|
mov cg, r15
|
||||||
|
|
||||||
|
|
||||||
|
indirect_mode:
|
||||||
|
.define numbered r3
|
||||||
|
mov @r0, r1
|
||||||
|
mov @r1, r2
|
||||||
|
;mov @r2, r3
|
||||||
|
;mov @r3, r4
|
||||||
|
mov @r4, r5
|
||||||
|
mov @r5, r6
|
||||||
|
mov @r6, r7
|
||||||
|
mov @r7, r8
|
||||||
|
mov @r8, r9
|
||||||
|
mov @r9, r10
|
||||||
|
mov @r10, r11
|
||||||
|
mov @r11, r12
|
||||||
|
mov @r12, r13
|
||||||
|
mov @r13, r14
|
||||||
|
mov @r14, r15
|
||||||
|
|
||||||
|
.define special r4
|
||||||
|
mov @pc, r15
|
||||||
|
mov @sp, r15
|
||||||
|
;mov @sr, r15 ; These are part of encodings for #immediate values [-1, 0, 1, 2, 4, 8]
|
||||||
|
;mov @cg, r15
|
||||||
|
|
||||||
|
indirect_pi_mode:
|
||||||
|
.define numbered r5
|
||||||
|
;mov @r0+, r1
|
||||||
|
mov @r1+, r2
|
||||||
|
;mov @r2+, r3
|
||||||
|
;mov @r3+, r4
|
||||||
|
mov @r4+, r5
|
||||||
|
mov @r5+, r6
|
||||||
|
mov @r6+, r7
|
||||||
|
mov @r7+, r8
|
||||||
|
mov @r8+, r9
|
||||||
|
mov @r9+, r10
|
||||||
|
mov @r10+, r11
|
||||||
|
mov @r11+, r12
|
||||||
|
mov @r12+, r13
|
||||||
|
mov @r13+, r14
|
||||||
|
mov @r14+, r15
|
||||||
|
|
||||||
|
.define special r6
|
||||||
|
;mov @pc+, r15 ; This is how mov-immediate is encoded, and is not valid
|
||||||
|
;mov @sp+, r15 ; pop r15
|
||||||
|
;mov @sr+, r15 ; These are part of encodings for #immediate values [-1, 0, 1, 2, 4, 8]
|
||||||
|
;mov @cg+, r15
|
||||||
|
|
||||||
|
indexed_mode:
|
||||||
|
.define numbered r7
|
||||||
|
mov.b 10(r0), r1
|
||||||
|
mov 10(r1), r2
|
||||||
|
;mov 10(r2), r3 ; Invalid: cannot index relative to sr
|
||||||
|
;mov 10(r3), r4 ; Invalid: cannot index relative to cg
|
||||||
|
mov 10(r4), r5
|
||||||
|
mov 10(r5), r6
|
||||||
|
mov 10(r6), r7
|
||||||
|
mov 10(r7), r8
|
||||||
|
mov 10(r8), r9
|
||||||
|
mov 10(r9), r10
|
||||||
|
mov 10(r10), r11
|
||||||
|
mov 10(r11), r12
|
||||||
|
mov 10(r12), r13
|
||||||
|
mov 10(r13), r14
|
||||||
|
mov 10(r14), r15
|
||||||
|
|
||||||
|
.define special r8
|
||||||
|
mov 10(pc), r15
|
||||||
|
mov 10(sp), r15
|
||||||
|
;mov 10(sr), r15 ; These are part of encodings for #immediate values [-1, 0, 1, 2, 4, 8]
|
||||||
|
;mov 10(cg), r15
|
||||||
|
|
||||||
|
_immediate_mode:
|
||||||
|
.define numbered r9
|
||||||
|
mov #beef, r0
|
||||||
|
mov #beef, r1
|
||||||
|
mov #beef, r2
|
||||||
|
mov #beef, r3
|
||||||
|
mov #beef, r4
|
||||||
|
mov #beef, r5
|
||||||
|
mov #beef, r6
|
||||||
|
mov #beef, r7
|
||||||
|
mov #beef, r8
|
||||||
|
mov #beef, r9
|
||||||
|
mov #beef, r10
|
||||||
|
mov #beef, r11
|
||||||
|
mov #beef, r12
|
||||||
|
mov #beef, r13
|
||||||
|
mov #beef, r14
|
||||||
|
mov #beef, r15
|
||||||
|
|
||||||
|
.define special r10
|
||||||
|
mov #beef, pc
|
||||||
|
mov #beef, sp
|
||||||
|
mov #beef, sr
|
||||||
|
mov #beef, cg
|
||||||
|
|
||||||
|
; jmp _register_mode ; TODO: msp430_asm currently has no support for jump labels.
|
||||||
|
jmp 3fe
|
||||||
|
jmp -3fc
|
||||||
|
ret
|
||||||
|
|
||||||
|
; Funky encodings
|
||||||
|
mov r6, r4
|
||||||
|
mov @r6, r4
|
||||||
|
mov @r6+, r4
|
||||||
|
mov 0(r6), r4
|
||||||
|
mov 4141(r6), r4
|
||||||
|
mov #-1, r4
|
||||||
|
mov #ffff, r4
|
||||||
|
mov #0, r4
|
||||||
|
mov #1, r4
|
||||||
|
mov #2, r4
|
||||||
|
mov #4, r4
|
||||||
|
mov #8, r4
|
||||||
|
mov r6, 0(r4)
|
||||||
|
mov @r6, 0(r4)
|
||||||
|
mov @r6+, 0(r4)
|
||||||
|
mov 0(r6), 0(r4)
|
||||||
|
mov 4141(r6), 0(r4)
|
||||||
|
mov #-1, 0(r4)
|
||||||
|
mov #ffff, 0(r4)
|
||||||
|
mov #0, 0(r4)
|
||||||
|
mov #1, 0(r4)
|
||||||
|
mov #2, 0(r4)
|
||||||
|
mov #4, 0(r4)
|
||||||
|
mov #8, 0(r4)
|
||||||
|
mov r6, 4141(r4)
|
||||||
|
mov @r6, 4141(r4)
|
||||||
|
mov @r6+, 4141(r4)
|
||||||
|
mov 0(r6), 4141(r4)
|
||||||
|
mov 4141(r6), 4141(r4)
|
||||||
|
mov #-1, 4141(r4)
|
||||||
|
mov #ffff, 4141(r4)
|
||||||
|
mov #0, 4141(r4)
|
||||||
|
mov #1, 4141(r4)
|
||||||
|
mov #2, 4141(r4)
|
||||||
|
mov #4, 4141(r4)
|
||||||
|
mov #8, 4141(r4)
|
||||||
|
mov r6, #0
|
||||||
|
mov @r6, #0
|
||||||
|
mov @r6+, #0
|
||||||
|
mov 0(r6), #0
|
||||||
|
mov 4141(r6), #0
|
||||||
|
mov #-1, #0
|
||||||
|
mov #ffff, #0
|
||||||
|
mov #0, #0
|
||||||
|
mov #1, #0
|
||||||
|
mov #2, #0
|
||||||
|
mov #4, #0
|
||||||
|
mov #8, #0
|
||||||
|
mov r6, #1
|
||||||
|
mov @r6, #1
|
||||||
|
mov @r6+, #1
|
||||||
|
mov 0(r6), #1
|
||||||
|
mov 4141(r6), #1
|
||||||
|
mov #-1, #1
|
||||||
|
mov #ffff, #1
|
||||||
|
mov #0, #1
|
||||||
|
mov #1, #1
|
||||||
|
mov #2, #1
|
||||||
|
mov #4, #1
|
||||||
|
mov #8, #1
|
||||||
|
|
||||||
|
; Instruction exercise
|
||||||
|
; Jumps
|
||||||
|
jne 10
|
||||||
|
jeq 10
|
||||||
|
jlo 10
|
||||||
|
jhs 10
|
||||||
|
jn 10
|
||||||
|
jge 10
|
||||||
|
jl 10
|
||||||
|
jmp 10
|
||||||
|
|
||||||
|
; Two-ops
|
||||||
|
mov r14, r15
|
||||||
|
add r14, r15
|
||||||
|
addc r14, r15
|
||||||
|
subc r14, r15
|
||||||
|
sub r14, r15
|
||||||
|
cmp r14, r15
|
||||||
|
dadd r14, r15
|
||||||
|
bit r14, r15
|
||||||
|
bic r14, r15
|
||||||
|
bis r14, r15
|
||||||
|
xor r14, r15
|
||||||
|
and r14, 10(r15)
|
||||||
|
|
||||||
|
; One-ops
|
||||||
|
rrc r15
|
||||||
|
swpb r15
|
||||||
|
rra r15
|
||||||
|
sxt r15
|
||||||
|
push r15
|
||||||
|
call r15
|
||||||
|
reti r15
|
||||||
|
|
||||||
|
; Jump aliases
|
||||||
|
jnc 10
|
||||||
|
jnz 10
|
||||||
|
jc 10
|
||||||
|
jz 10
|
||||||
|
|
||||||
|
; "emulated" no-op instructions
|
||||||
|
ret
|
||||||
|
clrc
|
||||||
|
setc
|
||||||
|
clrz
|
||||||
|
setz
|
||||||
|
clrn
|
||||||
|
setn
|
||||||
|
dint
|
||||||
|
eint
|
||||||
|
nop
|
||||||
|
|
||||||
|
; "emulated" one-op instructions
|
||||||
|
br r15
|
||||||
|
pop r15
|
||||||
|
rla r15
|
||||||
|
rlc r15
|
||||||
|
inv r15
|
||||||
|
clr r15
|
||||||
|
tst r15
|
||||||
|
dec r15
|
||||||
|
decd r15
|
||||||
|
inc r15
|
||||||
|
incd r15
|
||||||
|
adc r15
|
||||||
|
dadc r15
|
||||||
|
sbc r15
|
Loading…
Reference in New Issue
Block a user