Some refactoring
This commit is contained in:
@@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
|
||||
|
||||
use vcore::opcodes::InvalidMnemonic;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ParseError<'a> {
|
||||
LineParseFailure,
|
||||
InvalidInstruction(&'a str),
|
||||
|
||||
+103
-142
@@ -1,10 +1,13 @@
|
||||
use crate::ast::{Label, VASMLine};
|
||||
use crate::parse_error::ParseError;
|
||||
use crate::vasm_evaluator::{eval, EvalError, Scope};
|
||||
use crate::vasm_parser::parse_vasm_line;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum AssembleError<'a> {
|
||||
ParseError(i32, ParseError<'a>),
|
||||
EquResolveError(i32, &'a str, EvalError<'a>),
|
||||
EquDuplicateError(i32, &'a str),
|
||||
OrgResolveError(i32, EvalError<'a>),
|
||||
@@ -30,6 +33,9 @@ impl<'a> Display for AssembleError<'a> {
|
||||
AssembleError::NoCode => {
|
||||
write!(f, "No output would be generated by this code")
|
||||
}
|
||||
AssembleError::ParseError(line, err) => {
|
||||
write!(f, "Parse error on line {}: {}", line, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,13 +43,13 @@ impl<'a> Display for AssembleError<'a> {
|
||||
/// This will solve all the .equ directives and return a symbol table of them.
|
||||
/// .equ directives must be able to be solved in order, that is, in terms of
|
||||
/// only preceding .equ directives. Anything else is an error.
|
||||
pub fn solve_equs<'a>(lines: &[VASMLine<'a>]) -> Result<Scope<'a>, AssembleError<'a>> {
|
||||
fn solve_equs<'a>(lines: &Vec<VASMLine<'a>>) -> Result<Scope<'a>, AssembleError<'a>> {
|
||||
let mut scope: Scope = Scope::new();
|
||||
let line_nums: BTreeMap<i32, i32> = BTreeMap::new();
|
||||
for (line_idx, line) in lines.iter().enumerate() {
|
||||
let line_num = (line_idx + 1) as i32;
|
||||
if let VASMLine::Equ(Label(name), expr) = line {
|
||||
let value = eval(expr, line_num as i32, &line_nums, &scope)
|
||||
let value = eval(&expr, line_num as i32, &line_nums, &scope)
|
||||
.map_err(|e| AssembleError::EquResolveError(line_num as i32, name, e))?;
|
||||
|
||||
if let Some(_old_value) = scope.insert(name, value) {
|
||||
@@ -56,7 +62,6 @@ pub fn solve_equs<'a>(lines: &[VASMLine<'a>]) -> Result<Scope<'a>, AssembleError
|
||||
|
||||
type LineLengths = BTreeMap<i32, usize>;
|
||||
type LineAddresses = BTreeMap<i32, i32>;
|
||||
type LineArgs = BTreeMap<i32, i32>;
|
||||
|
||||
fn arg_length(val: i32) -> usize {
|
||||
if val < 0 {
|
||||
@@ -84,7 +89,7 @@ fn arg_length(val: i32) -> usize {
|
||||
/// what we know right now (.equs), are however long that argument is. If we don't
|
||||
/// know right now (based on a label, say) then we'll set aside the full 3 bytes (so it's
|
||||
/// 4 bytes long, with the instruction byte).
|
||||
pub fn measure_instructions<'a>(lines: &[VASMLine<'a>], scope: &Scope) -> LineLengths {
|
||||
fn measure_instructions<'a>(lines: &Vec<VASMLine<'a>>, scope: &Scope) -> LineLengths {
|
||||
let line_nums: BTreeMap<i32, i32> = BTreeMap::new();
|
||||
let mut lengths = LineLengths::new();
|
||||
for (line_idx, line) in lines.iter().enumerate() {
|
||||
@@ -94,7 +99,7 @@ pub fn measure_instructions<'a>(lines: &[VASMLine<'a>], scope: &Scope) -> LineLe
|
||||
lengths.insert(line_num, 1);
|
||||
}
|
||||
VASMLine::Instruction(_, _, Some(node)) => {
|
||||
let len = eval(node, line_num as i32, &line_nums, scope).map_or(3, arg_length);
|
||||
let len = eval(&node, line_num as i32, &line_nums, scope).map_or(3, arg_length);
|
||||
lengths.insert(line_num, len + 1);
|
||||
}
|
||||
VASMLine::Db(_, _) => {
|
||||
@@ -122,7 +127,7 @@ pub fn measure_instructions<'a>(lines: &[VASMLine<'a>], scope: &Scope) -> LineLe
|
||||
/// But, we'll skip labels that come before .equs: that would make every .equ set to its address,
|
||||
/// rather than the argument.
|
||||
fn place_labels<'a>(
|
||||
lines: &[VASMLine<'a>],
|
||||
lines: &Vec<VASMLine<'a>>,
|
||||
scope: Scope<'a>,
|
||||
lengths: &LineLengths,
|
||||
) -> Result<(LineAddresses, Scope<'a>), AssembleError<'a>> {
|
||||
@@ -133,7 +138,7 @@ fn place_labels<'a>(
|
||||
let line_num = (line_idx + 1) as i32;
|
||||
|
||||
if let VASMLine::Org(_, expr) = line {
|
||||
address = eval(expr, line_num, &addresses, &scope)
|
||||
address = eval(&expr, line_num, &addresses, &scope)
|
||||
.map_err(|err| AssembleError::OrgResolveError(line_num, err))?;
|
||||
addresses.insert(line_num, address);
|
||||
}
|
||||
@@ -155,33 +160,6 @@ fn place_labels<'a>(
|
||||
Ok((addresses, scope))
|
||||
}
|
||||
|
||||
/// At this point we have everything we need to calculate the arguments, so we'll do that.
|
||||
/// This will take the lines, addresses, and scope we've calculated so far, and iterate through
|
||||
/// each line. If the line has an argument, evaluate it based on the scope, and store the number
|
||||
/// that it evaluates to
|
||||
fn calculate_args<'a>(
|
||||
lines: &[VASMLine<'a>],
|
||||
scope: Scope,
|
||||
line_addresses: &LineAddresses,
|
||||
) -> Result<LineArgs, AssembleError<'a>> {
|
||||
let mut args = LineArgs::new();
|
||||
|
||||
for (line_idx, line) in lines.iter().enumerate() {
|
||||
let line_num = (line_idx + 1) as i32;
|
||||
|
||||
match line {
|
||||
VASMLine::Instruction(_, _, Some(arg)) | VASMLine::Db(_, arg) => {
|
||||
let num = eval(arg, line_num, line_addresses, &scope)
|
||||
.map_err(|err| AssembleError::ArgError(line_num, err))?;
|
||||
args.insert(line_num, num);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
fn poke_word(code: &mut Vec<u8>, at: usize, word: i32) {
|
||||
let [low, mid, high, _] = word.to_le_bytes();
|
||||
code[at] = low;
|
||||
@@ -191,7 +169,7 @@ fn poke_word(code: &mut Vec<u8>, at: usize, word: i32) {
|
||||
|
||||
/// Find the lower and upper bounds where this program will place memory
|
||||
fn code_bounds<'a>(
|
||||
lines: &[VASMLine<'a>],
|
||||
lines: &Vec<VASMLine<'a>>,
|
||||
line_addresses: &LineAddresses,
|
||||
line_lengths: &LineLengths,
|
||||
) -> Result<(usize, usize), AssembleError<'a>> {
|
||||
@@ -213,6 +191,18 @@ fn code_bounds<'a>(
|
||||
Ok((start, end + end_length - 1))
|
||||
}
|
||||
|
||||
pub fn assemble<'a, T: IntoIterator<Item = &'a str>>(
|
||||
lines: T,
|
||||
) -> Result<Vec<u8>, AssembleError<'a>> {
|
||||
let mut parsed = Vec::new();
|
||||
for (line_idx, line) in lines.into_iter().enumerate() {
|
||||
let line_num = (line_idx + 1) as i32;
|
||||
parsed.push(parse_vasm_line(line).map_err(|err| AssembleError::ParseError(line_num, err))?)
|
||||
}
|
||||
|
||||
generate_code(parsed)
|
||||
}
|
||||
|
||||
/// At this point all lines have addresses and lengths, and all arguments are reduced to
|
||||
/// numeric constants. It's time to generate code.
|
||||
///
|
||||
@@ -221,21 +211,20 @@ fn code_bounds<'a>(
|
||||
/// - .db instructions turn into byte values starting at `address - start`
|
||||
/// - Opcodes turn into instruction bytes at `address - start` followed (maybe) by
|
||||
/// arguments.
|
||||
/// - Any other directive is skipped (even .orgs, we already have the addresses calculated)
|
||||
/// - .orgs cause us to skip ahead some in the output
|
||||
///
|
||||
/// The instruction bytes are formed of six bits defining the instruction followed by two
|
||||
/// bits denoting how many bytes of argument follow it.
|
||||
///
|
||||
/// Vulcan is a little-endian architecture: multi-byte arguments / .dbs will store the
|
||||
/// least-significant byte at the lowest address, then the more significant bytes following.
|
||||
fn generate_code<'a>(
|
||||
lines: &[VASMLine<'a>],
|
||||
scope: Scope,
|
||||
line_addresses: LineAddresses,
|
||||
line_lengths: LineLengths,
|
||||
) -> Result<Vec<u8>, AssembleError<'a>> {
|
||||
let line_args = calculate_args(lines, scope, &line_addresses)?;
|
||||
let (start, end) = code_bounds(lines, &line_addresses, &line_lengths)?;
|
||||
fn generate_code<'a>(lines: Vec<VASMLine<'a>>) -> Result<Vec<u8>, AssembleError<'a>> {
|
||||
let lines: Vec<VASMLine<'a>> = lines.into_iter().collect();
|
||||
let scope = solve_equs(&lines)?;
|
||||
let line_lengths = measure_instructions(&lines, &scope);
|
||||
let (line_addresses, scope) = place_labels(&lines, scope, &line_lengths)?;
|
||||
let (start, end) = code_bounds(&lines, &line_addresses, &line_lengths)?;
|
||||
|
||||
let mut code = vec![0u8; end - start + 1];
|
||||
let mut current_addr = start;
|
||||
|
||||
@@ -246,8 +235,9 @@ fn generate_code<'a>(
|
||||
code[current_addr] = u8::from(*opcode) << 2;
|
||||
current_addr += 1;
|
||||
}
|
||||
VASMLine::Instruction(_, opcode, Some(_)) => {
|
||||
let arg = line_args[&line_num];
|
||||
VASMLine::Instruction(_, opcode, Some(arg)) => {
|
||||
let arg = eval(arg, line_num, &line_addresses, &scope)
|
||||
.map_err(|err| AssembleError::ArgError(line_num, err))?;
|
||||
let len = arg_length(arg);
|
||||
let instr = (u8::from(*opcode) << 2) + len as u8;
|
||||
code[current_addr - start] = instr;
|
||||
@@ -261,8 +251,9 @@ fn generate_code<'a>(
|
||||
}
|
||||
current_addr += len + 1;
|
||||
}
|
||||
VASMLine::Db(_, _) => {
|
||||
let arg = line_args[&line_num];
|
||||
VASMLine::Db(_, arg) => {
|
||||
let arg = eval(arg, line_num, &line_addresses, &scope)
|
||||
.map_err(|err| AssembleError::ArgError(line_num, err))?;
|
||||
poke_word(&mut code, current_addr - start, arg);
|
||||
current_addr += 3;
|
||||
}
|
||||
@@ -288,61 +279,51 @@ mod test {
|
||||
use super::EvalError::*;
|
||||
use super::*;
|
||||
use crate::ast::VASMLine;
|
||||
use crate::parse_error;
|
||||
use crate::vasm_parser::parse_vasm_line;
|
||||
|
||||
fn parse<'a>(lines: &'a [&str]) -> Vec<VASMLine<'a>> {
|
||||
fn parse<'a, T: IntoIterator<Item = &'a str>>(lines: T) -> Vec<VASMLine<'a>> {
|
||||
lines
|
||||
.iter()
|
||||
.map(|line| parse_vasm_line(*line).unwrap())
|
||||
.into_iter()
|
||||
.map(|line| parse_vasm_line(line).unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn place_labels_pass<'a>(
|
||||
lines: &'a [&str],
|
||||
fn place_labels_pass<'a, T: IntoIterator<Item = &'a str>>(
|
||||
lines: T,
|
||||
) -> Result<(LineAddresses, Scope<'a>), AssembleError<'a>> {
|
||||
let lines = parse(lines);
|
||||
let scope = solve_equs(&lines).unwrap();
|
||||
let lengths = measure_instructions(&lines, &scope);
|
||||
place_labels(&lines, scope, &lengths)
|
||||
let parsed_lines = parse(lines);
|
||||
let scope = solve_equs(&parsed_lines).unwrap();
|
||||
let lengths = measure_instructions(&parsed_lines, &scope);
|
||||
place_labels(&parsed_lines, scope, &lengths)
|
||||
}
|
||||
|
||||
fn calculate_args_pass<'a>(lines: &'a [&str]) -> Result<LineArgs, AssembleError<'a>> {
|
||||
let (line_addresses, scope) = place_labels_pass(lines)?;
|
||||
let lines = parse(lines);
|
||||
calculate_args(&lines, scope, &line_addresses)
|
||||
}
|
||||
|
||||
fn bounds<'a>(lines: &'a [&str]) -> Result<(usize, usize), AssembleError<'a>> {
|
||||
let (line_addresses, scope) = place_labels_pass(lines)?;
|
||||
let lines = parse(lines);
|
||||
let lengths = measure_instructions(&lines, &scope);
|
||||
code_bounds(&lines, &line_addresses, &lengths)
|
||||
}
|
||||
|
||||
fn generate_code_pass<'a>(lines: &'a [&str]) -> Result<Vec<u8>, AssembleError<'a>> {
|
||||
let lines = parse(lines);
|
||||
let scope = solve_equs(&lines).unwrap();
|
||||
let lengths = measure_instructions(&lines, &scope);
|
||||
let (line_addresses, scope) = place_labels(&lines, scope, &lengths)?;
|
||||
generate_code(&lines, scope, line_addresses, lengths)
|
||||
fn bounds<'a, T: IntoIterator<Item = &'a str>>(
|
||||
lines: T,
|
||||
) -> Result<(usize, usize), AssembleError<'a>> {
|
||||
let parsed_lines = parse(lines);
|
||||
let scope = solve_equs(&parsed_lines).unwrap();
|
||||
let lengths = measure_instructions(&parsed_lines, &scope);
|
||||
let (line_addresses, _scope) = place_labels(&parsed_lines, scope, &lengths)?;
|
||||
code_bounds(&parsed_lines, &line_addresses, &lengths)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equs() {
|
||||
assert_eq!(
|
||||
solve_equs(&parse(&["blah: .equ 5+3"])),
|
||||
solve_equs(&parse(["blah: .equ 5+3"])),
|
||||
Ok([("blah", 8)].into())
|
||||
);
|
||||
assert_eq!(
|
||||
solve_equs(&parse(&["blah: .equ 5", "foo: .equ 3"])),
|
||||
solve_equs(&parse(["blah: .equ 5", "foo: .equ 3"])),
|
||||
Ok([("blah", 5), ("foo", 3)].into())
|
||||
);
|
||||
assert_eq!(
|
||||
solve_equs(&parse(&["blah: .equ 5", "foo: .equ blah + 7"])),
|
||||
solve_equs(&parse(["blah: .equ 5", "foo: .equ blah + 7"])),
|
||||
Ok([("blah", 5), ("foo", 12)].into())
|
||||
);
|
||||
assert_eq!(
|
||||
solve_equs(&parse(&["add", "blah: .equ 5"])),
|
||||
solve_equs(&parse(["add", "blah: .equ 5"])),
|
||||
Ok([("blah", 5)].into())
|
||||
);
|
||||
}
|
||||
@@ -350,15 +331,15 @@ mod test {
|
||||
#[test]
|
||||
fn test_unsolvable_equs() {
|
||||
assert_eq!(
|
||||
solve_equs(&parse(&["blah: .equ 5", "foo: .equ banana"])),
|
||||
solve_equs(&parse(["blah: .equ 5", "foo: .equ banana"])),
|
||||
Err(EquResolveError(2, "foo", MissingLabel("banana")))
|
||||
);
|
||||
assert_eq!(
|
||||
solve_equs(&parse(&["blah: .equ foo+3", "foo: .equ 7"])),
|
||||
solve_equs(&parse(["blah: .equ foo+3", "foo: .equ 7"])),
|
||||
Err(EquResolveError(1, "blah", MissingLabel("foo")))
|
||||
);
|
||||
assert_eq!(
|
||||
solve_equs(&parse(&["blah: .equ 3", "blah: .equ 7"])),
|
||||
solve_equs(&parse(["blah: .equ 3", "blah: .equ 7"])),
|
||||
Err(EquDuplicateError(2, "blah"))
|
||||
);
|
||||
}
|
||||
@@ -367,22 +348,22 @@ mod test {
|
||||
fn test_lengths() {
|
||||
assert_eq!(
|
||||
measure_instructions(
|
||||
&parse(&["add", "add 1", "add 500", "add 70000", "add -7"]),
|
||||
&parse(["add", "add 1", "add 500", "add 70000", "add -7"]),
|
||||
&[].into()
|
||||
),
|
||||
[(1, 1), (2, 2), (3, 3), (4, 4), (5, 4)].into()
|
||||
);
|
||||
assert_eq!(
|
||||
measure_instructions(&parse(&[".db 7", ".db \"hello\\0\""]), &[].into()),
|
||||
measure_instructions(&parse([".db 7", ".db \"hello\\0\""]), &[].into()),
|
||||
[(1, 3), (2, 6)].into()
|
||||
);
|
||||
assert_eq!(
|
||||
measure_instructions(&parse(&[".org 256", "blah:", "foo: .equ 7"]), &[].into()),
|
||||
measure_instructions(&parse([".org 256", "blah:", "foo: .equ 7"]), &[].into()),
|
||||
[(1, 0), (2, 0), (3, 0)].into()
|
||||
);
|
||||
assert_eq!(
|
||||
measure_instructions(
|
||||
&parse(&["add 2 + foo", "add 3 + blah", "jmpr @foo"]),
|
||||
&parse(["add 2 + foo", "add 3 + blah", "jmpr @foo"]),
|
||||
&[("blah", 300)].into()
|
||||
),
|
||||
[(1, 4), (2, 3), (3, 4)].into()
|
||||
@@ -392,25 +373,25 @@ mod test {
|
||||
#[test]
|
||||
fn test_place_labels() {
|
||||
assert_eq!(
|
||||
place_labels_pass(&["start: .org 256", "add", "dup"]),
|
||||
place_labels_pass(["start: .org 256", "add", "dup"]),
|
||||
Ok((
|
||||
[(1, 256), (2, 256), (3, 257)].into(),
|
||||
[("start", 256)].into()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
place_labels_pass(&["push 70000", "dup"]),
|
||||
place_labels_pass(["push 70000", "dup"]),
|
||||
Ok(([(1, 0), (2, 4)].into(), [].into()))
|
||||
);
|
||||
assert_eq!(
|
||||
place_labels_pass(&["start: .equ 256", "blah: .org start + 4", "add"]),
|
||||
place_labels_pass(["start: .equ 256", "blah: .org start + 4", "add"]),
|
||||
Ok((
|
||||
[(1, 0), (2, 260), (3, 260)].into(),
|
||||
[("blah", 260), ("start", 256)].into()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
place_labels_pass(&["start: .org 256", "blah: .org start + 10", "add"]),
|
||||
place_labels_pass(["start: .org 256", "blah: .org start + 10", "add"]),
|
||||
Ok((
|
||||
[(1, 256), (2, 266), (3, 266)].into(),
|
||||
[("blah", 266), ("start", 256)].into()
|
||||
@@ -421,81 +402,61 @@ mod test {
|
||||
#[test]
|
||||
fn test_unresolvable_orgs() {
|
||||
assert_eq!(
|
||||
place_labels_pass(&[".org 0xffffff - blah"]),
|
||||
place_labels_pass([".org 0xffffff - blah"]),
|
||||
Err(OrgResolveError(1, EvalError::MissingLabel("blah")))
|
||||
);
|
||||
assert_eq!(
|
||||
place_labels_pass(&["blah: .org blah"]),
|
||||
place_labels_pass(["blah: .org blah"]),
|
||||
Err(OrgResolveError(1, EvalError::MissingLabel("blah")))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_args() {
|
||||
assert_eq!(
|
||||
calculate_args_pass(&["add 3", "add 5"]),
|
||||
Ok([(1, 3), (2, 5)].into())
|
||||
);
|
||||
assert_eq!(
|
||||
calculate_args_pass(&["foo: .equ 4", "add 3+foo", "add foo+7"]),
|
||||
Ok([(2, 7), (3, 11)].into())
|
||||
);
|
||||
assert_eq!(
|
||||
calculate_args_pass(&[".org 0x400", "blah:", "jmp blah + 10"]),
|
||||
Ok([(3, 1034)].into())
|
||||
);
|
||||
assert_eq!(
|
||||
calculate_args_pass(&[".org 0x400", "blah:", "add -4", "jmp @blah + 10"]),
|
||||
Ok([(3, -4), (4, 6)].into()) // blah is 1024, address of line 4 is 1028, so 6
|
||||
);
|
||||
assert_eq!(
|
||||
calculate_args_pass(&[".org 0x400", "sub", "jmp $-1"]),
|
||||
Ok([(3, 0x400)].into())
|
||||
);
|
||||
assert_eq!(
|
||||
calculate_args_pass(&[".org 0x400", "sub", "agt -3", "brz @-2"]),
|
||||
Ok([(3, -3), (4, -5)].into())
|
||||
);
|
||||
assert_eq!(
|
||||
calculate_args_pass(&["add banana + 3"]),
|
||||
Err(AssembleError::ArgError(
|
||||
1,
|
||||
EvalError::MissingLabel("banana")
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bounds() {
|
||||
assert_eq!(bounds(&["add"]), Ok((0, 0)));
|
||||
assert_eq!(bounds(&["add -4"]), Ok((0, 3)));
|
||||
assert_eq!(bounds(&["add 7"]), Ok((0, 1)));
|
||||
assert_eq!(bounds(&[".org 0x400", "add"]), Ok((1024, 1024)));
|
||||
assert_eq!(bounds(["add"]), Ok((0, 0)));
|
||||
assert_eq!(bounds(["add -4"]), Ok((0, 3)));
|
||||
assert_eq!(bounds(["add 7"]), Ok((0, 1)));
|
||||
assert_eq!(bounds([".org 0x400", "add"]), Ok((1024, 1024)));
|
||||
assert_eq!(
|
||||
bounds(&["start: .equ 1024", ".org start", "add"]),
|
||||
bounds(["start: .equ 1024", ".org start", "add"]),
|
||||
Ok((1024, 1024))
|
||||
);
|
||||
assert_eq!(
|
||||
bounds(&[".org 0x400", "add", ".org 0x800"]),
|
||||
bounds([".org 0x400", "add", ".org 0x800"]),
|
||||
Ok((1024, 1024))
|
||||
);
|
||||
assert_eq!(
|
||||
bounds(&[".org 0x400", "add", ".org 0x800", ".db 5", "blah:"]),
|
||||
bounds([".org 0x400", "add", ".org 0x800", ".db 5", "blah:"]),
|
||||
Ok((1024, 2050))
|
||||
);
|
||||
assert_eq!(bounds(&[]), Err(AssembleError::NoCode));
|
||||
assert_eq!(bounds(&[".org 0x400"]), Err(AssembleError::NoCode));
|
||||
assert_eq!(bounds([]), Err(AssembleError::NoCode));
|
||||
assert_eq!(bounds([".org 0x400"]), Err(AssembleError::NoCode));
|
||||
assert_eq!(
|
||||
bounds(&["foo: .equ 3", ".org 0x400"]),
|
||||
bounds(["foo: .equ 3", ".org 0x400"]),
|
||||
Err(AssembleError::NoCode)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_code() {
|
||||
assert_eq!(generate_code_pass(&["add"]), Ok(vec![4]));
|
||||
assert_eq!(generate_code_pass(&[".org 0x400", "add 7"]), Ok(vec![5, 7]));
|
||||
assert_eq!(generate_code_pass(&[".db 57"]), Ok(vec![57, 0, 0]));
|
||||
assert_eq!(generate_code_pass(&[".db \"AZ\0\""]), Ok(vec![65, 90, 0]));
|
||||
assert_eq!(generate_code(parse(["add"])), Ok(vec![4]));
|
||||
assert_eq!(
|
||||
generate_code(parse([".org 0x400", "add 7"])),
|
||||
Ok(vec![5, 7])
|
||||
);
|
||||
assert_eq!(generate_code(parse([".db 57"])), Ok(vec![57, 0, 0]));
|
||||
assert_eq!(generate_code(parse([".db \"AZ\0\""])), Ok(vec![65, 90, 0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_assemble() {
|
||||
assert_eq!(assemble(["add"]), Ok(vec![4]));
|
||||
assert_eq!(
|
||||
assemble(["apple"]),
|
||||
Err(ParseError(
|
||||
1,
|
||||
parse_error::ParseError::InvalidInstruction("apple")
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user