Couple bugfixes, integration tests ported
This commit is contained in:
Generated
+1
@@ -6,6 +6,7 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/vcore/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/vcore/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/vemu/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/vemu/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/vasm/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/vasm/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/vtest/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|||||||
Generated
+8
@@ -1227,6 +1227,14 @@ version = "0.9.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vtest"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"vasm",
|
||||||
|
"vcore",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
|||||||
+2
-1
@@ -3,5 +3,6 @@ resolver = "2"
|
|||||||
members = [
|
members = [
|
||||||
"vcore",
|
"vcore",
|
||||||
"vemu",
|
"vemu",
|
||||||
"vasm"
|
"vasm",
|
||||||
|
"vtest"
|
||||||
]
|
]
|
||||||
@@ -73,6 +73,7 @@ pub enum VASMLine {
|
|||||||
Equ(Label, Node),
|
Equ(Label, Node),
|
||||||
LabelDef(Label),
|
LabelDef(Label),
|
||||||
Macro(Macro),
|
Macro(Macro),
|
||||||
|
Blank,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VASMLine {
|
impl VASMLine {
|
||||||
|
|||||||
+7
-5
@@ -2,9 +2,11 @@ extern crate pest;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate pest_derive;
|
extern crate pest_derive;
|
||||||
|
|
||||||
pub mod ast;
|
mod ast;
|
||||||
pub mod parse_error;
|
pub mod parse_error;
|
||||||
pub mod vasm_assembler;
|
mod vasm_assembler;
|
||||||
pub mod vasm_evaluator;
|
mod vasm_evaluator;
|
||||||
pub mod vasm_parser;
|
mod vasm_parser;
|
||||||
pub mod vasm_preprocessor;
|
mod vasm_preprocessor;
|
||||||
|
|
||||||
|
pub use vasm_assembler::assemble_snippet;
|
||||||
|
|||||||
+3
-1
@@ -72,5 +72,7 @@ include = { "include" ~ string }
|
|||||||
control = { "if" | "unless" | "while" | "until" | "do" | "else" | "end" }
|
control = { "if" | "unless" | "while" | "until" | "do" | "else" | "end" }
|
||||||
preprocessor = {"#" ~ (control | include) }
|
preprocessor = {"#" ~ (control | include) }
|
||||||
|
|
||||||
|
blank = { WHITESPACE? ~ COMMENT? }
|
||||||
|
|
||||||
// Finally the entire pattern for an assembly line:
|
// Finally the entire pattern for an assembly line:
|
||||||
line = { SOI ~ (preprocessor | db_word | db_string | org_directive | equ_directive | instruction | label_def) ~ EOI }
|
line = { SOI ~ (preprocessor | db_word | db_string | org_directive | equ_directive | instruction | label_def | blank) ~ EOI }
|
||||||
+87
-16
@@ -1,7 +1,7 @@
|
|||||||
use crate::ast::{Label, Scope, VASMLine};
|
use crate::ast::{Label, Scope, VASMLine};
|
||||||
use crate::parse_error::AssembleError;
|
use crate::parse_error::AssembleError;
|
||||||
use crate::vasm_evaluator::eval;
|
use crate::vasm_evaluator::eval;
|
||||||
use crate::vasm_parser::parse_vasm_line;
|
use crate::vasm_preprocessor::{Line, LineSource};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// This will solve all the .equ directives and return a symbol table of them.
|
/// This will solve all the .equ directives and return a symbol table of them.
|
||||||
@@ -72,7 +72,7 @@ fn measure_instructions(lines: &[VASMLine], scope: &Scope) -> LineLengths {
|
|||||||
VASMLine::StringDb(_, value) => {
|
VASMLine::StringDb(_, value) => {
|
||||||
lengths.insert(line_num, value.len());
|
lengths.insert(line_num, value.len());
|
||||||
}
|
}
|
||||||
VASMLine::Org(_, _) | VASMLine::Equ(_, _) | VASMLine::LabelDef(_) => {
|
VASMLine::Org(_, _) | VASMLine::Equ(_, _) | VASMLine::LabelDef(_) | VASMLine::Blank => {
|
||||||
lengths.insert(line_num, 0);
|
lengths.insert(line_num, 0);
|
||||||
}
|
}
|
||||||
VASMLine::Macro(_) => unreachable!(),
|
VASMLine::Macro(_) => unreachable!(),
|
||||||
@@ -156,14 +156,37 @@ fn code_bounds(
|
|||||||
Ok((start, end + end_length - 1))
|
Ok((start, end + end_length - 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble<'a, T: IntoIterator<Item = &'a str>>(lines: T) -> Result<Vec<u8>, AssembleError> {
|
/// Turn an iterable of strs into an assembled binary. This supports macros, but not
|
||||||
let mut parsed = Vec::new();
|
/// the `#include` macro. The resulting Vec is only as large as it needs to be; if your
|
||||||
for (line_idx, line) in lines.into_iter().enumerate() {
|
/// code starts with `.org 0x400` and is five bytes long then the Vec will be five
|
||||||
let line_num = line_idx + 1;
|
/// bytes long and index 0 will represent 0x400.
|
||||||
parsed.push(parse_vasm_line(line).map_err(|err| AssembleError::ParseError(line_num, err))?)
|
/// ```
|
||||||
}
|
/// assert_eq!(
|
||||||
|
/// vasm::assemble_snippet(vec![".org 0x400", "push 5", "add 7"]),
|
||||||
|
/// Ok(vec![0x01, 0x05, 0x05, 0x07])
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
pub fn assemble_snippet<'a, T: IntoIterator<Item = &'a str>>(
|
||||||
|
lines: T,
|
||||||
|
) -> Result<Vec<u8>, AssembleError> {
|
||||||
|
let mut line_results: Vec<Result<Line, AssembleError>> =
|
||||||
|
LineSource::new("_snippet", lines, |_file| {
|
||||||
|
Err(AssembleError::IncludeError(
|
||||||
|
0,
|
||||||
|
"Including is not supported in assembling snippets".to_string(),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
generate_code(parsed)
|
if let Some(Err(error)) = line_results.iter().find(|line| line.is_err()) {
|
||||||
|
Err(error.clone())
|
||||||
|
} else {
|
||||||
|
generate_code(
|
||||||
|
line_results
|
||||||
|
.iter_mut()
|
||||||
|
.map(|line| line.clone().unwrap().line),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// At this point all lines have addresses and lengths, and all arguments are reduced to
|
/// At this point all lines have addresses and lengths, and all arguments are reduced to
|
||||||
@@ -181,7 +204,7 @@ pub fn assemble<'a, T: IntoIterator<Item = &'a str>>(lines: T) -> Result<Vec<u8>
|
|||||||
///
|
///
|
||||||
/// Vulcan is a little-endian architecture: multi-byte arguments / .dbs will store the
|
/// 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.
|
/// least-significant byte at the lowest address, then the more significant bytes following.
|
||||||
fn generate_code(lines: Vec<VASMLine>) -> Result<Vec<u8>, AssembleError> {
|
fn generate_code<T: IntoIterator<Item = VASMLine>>(lines: T) -> Result<Vec<u8>, AssembleError> {
|
||||||
let lines: Vec<VASMLine> = lines.into_iter().collect();
|
let lines: Vec<VASMLine> = lines.into_iter().collect();
|
||||||
let scope = solve_equs(&lines)?;
|
let scope = solve_equs(&lines)?;
|
||||||
let line_lengths = measure_instructions(&lines, &scope);
|
let line_lengths = measure_instructions(&lines, &scope);
|
||||||
@@ -195,13 +218,13 @@ fn generate_code(lines: Vec<VASMLine>) -> Result<Vec<u8>, AssembleError> {
|
|||||||
let line_num = line_idx + 1;
|
let line_num = line_idx + 1;
|
||||||
match line {
|
match line {
|
||||||
VASMLine::Instruction(_, opcode, None) => {
|
VASMLine::Instruction(_, opcode, None) => {
|
||||||
code[current_addr] = u8::from(*opcode) << 2;
|
code[current_addr - start] = u8::from(*opcode) << 2;
|
||||||
current_addr += 1;
|
current_addr += 1;
|
||||||
}
|
}
|
||||||
VASMLine::Instruction(_, opcode, Some(arg)) => {
|
VASMLine::Instruction(_, opcode, Some(arg)) => {
|
||||||
let arg = eval(arg, line_num, &line_addresses, &scope)
|
let arg = eval(arg, line_num, &line_addresses, &scope)
|
||||||
.map_err(|err| AssembleError::ArgError(line_num, err))?;
|
.map_err(|err| AssembleError::ArgError(line_num, err))?;
|
||||||
let len = arg_length(arg);
|
let len = line_lengths[&line_num] - 1;
|
||||||
let instr = (u8::from(*opcode) << 2) + len as u8;
|
let instr = (u8::from(*opcode) << 2) + len as u8;
|
||||||
code[current_addr - start] = instr;
|
code[current_addr - start] = instr;
|
||||||
let [low, mid, high, _] = arg.to_le_bytes();
|
let [low, mid, high, _] = arg.to_le_bytes();
|
||||||
@@ -229,7 +252,7 @@ fn generate_code(lines: Vec<VASMLine>) -> Result<Vec<u8>, AssembleError> {
|
|||||||
VASMLine::Org(_, _) => {
|
VASMLine::Org(_, _) => {
|
||||||
current_addr = line_addresses[&(line_num + 1)] as usize;
|
current_addr = line_addresses[&(line_num + 1)] as usize;
|
||||||
}
|
}
|
||||||
VASMLine::Equ(_, _) | VASMLine::LabelDef(_) => {}
|
VASMLine::Equ(_, _) | VASMLine::LabelDef(_) | VASMLine::Blank => {}
|
||||||
VASMLine::Macro(_) => unreachable!(),
|
VASMLine::Macro(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -333,6 +356,13 @@ mod test {
|
|||||||
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()
|
[(1, 0), (2, 0), (3, 0)].into()
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
measure_instructions(
|
||||||
|
&parse([".org 0x400", "push 3", "call blah", "hlt", "blah: mul 2"]),
|
||||||
|
&[].into()
|
||||||
|
),
|
||||||
|
[(1, 0), (2, 2), (3, 4), (4, 1), (5, 2)].into()
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
measure_instructions(
|
measure_instructions(
|
||||||
&parse(["add 2 + foo", "add 3 + blah", "jmpr @foo"]),
|
&parse(["add 2 + foo", "add 3 + blah", "jmpr @foo"]),
|
||||||
@@ -369,6 +399,28 @@ mod test {
|
|||||||
[("blah".to_string(), 266), ("start".to_string(), 256)].into()
|
[("blah".to_string(), 266), ("start".to_string(), 256)].into()
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
place_labels_pass([
|
||||||
|
".org 1024",
|
||||||
|
"nop 3",
|
||||||
|
"call blah",
|
||||||
|
"hlt",
|
||||||
|
"blah: mul 2",
|
||||||
|
"ret"
|
||||||
|
]),
|
||||||
|
Ok((
|
||||||
|
[
|
||||||
|
(1, 1024),
|
||||||
|
(2, 1024),
|
||||||
|
(3, 1026),
|
||||||
|
(4, 1030),
|
||||||
|
(5, 1031),
|
||||||
|
(6, 1033)
|
||||||
|
]
|
||||||
|
.into(),
|
||||||
|
[("blah".to_string(), 1031)].into()
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -421,14 +473,33 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_assemble() {
|
fn test_assemble_snippet() {
|
||||||
assert_eq!(assemble(["add"]), Ok(vec![4]));
|
assert_eq!(assemble_snippet(["add"]), Ok(vec![4]));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assemble(["apple"]),
|
assemble_snippet(["apple"]),
|
||||||
Err(ParseError(
|
Err(ParseError(
|
||||||
1,
|
1,
|
||||||
parse_error::ParseError::InvalidInstruction("apple".into())
|
parse_error::ParseError::InvalidInstruction("apple".into())
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
assemble_snippet(
|
||||||
|
".org 0x400
|
||||||
|
nop 3
|
||||||
|
call blah
|
||||||
|
hlt
|
||||||
|
blah: mul 2
|
||||||
|
ret"
|
||||||
|
.lines()
|
||||||
|
),
|
||||||
|
Ok(vec![
|
||||||
|
0x01, 0x03, // nop 3
|
||||||
|
0x67, 0x07, 0x04, 0x00, // call blah (arg defaults to 3 bytes long)
|
||||||
|
0x74, // hlt
|
||||||
|
0x0d, 0x02, // mul 2
|
||||||
|
0x68
|
||||||
|
])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ pub fn parse_vasm_line(line: &str) -> Result<VASMLine, ParseError> {
|
|||||||
}
|
}
|
||||||
Rule::label_def => Ok(VASMLine::LabelDef(Label::from(line.only().as_str()))),
|
Rule::label_def => Ok(VASMLine::LabelDef(Label::from(line.only().as_str()))),
|
||||||
Rule::preprocessor => Ok(VASMLine::Macro(parse_macro(line))),
|
Rule::preprocessor => Ok(VASMLine::Macro(parse_macro(line))),
|
||||||
|
Rule::blank => Ok(VASMLine::Blank),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,6 +202,7 @@ fn parse_macro(line: Pair) -> Macro {
|
|||||||
match pre.as_rule() {
|
match pre.as_rule() {
|
||||||
Rule::control => match pre.as_str() {
|
Rule::control => match pre.as_str() {
|
||||||
"if" => Macro::If,
|
"if" => Macro::If,
|
||||||
|
"unless" => Macro::Unless,
|
||||||
"else" => Macro::Else,
|
"else" => Macro::Else,
|
||||||
"while" => Macro::While,
|
"while" => Macro::While,
|
||||||
"until" => Macro::Until,
|
"until" => Macro::Until,
|
||||||
@@ -420,9 +422,21 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_macros() {
|
fn test_parse_macros() {
|
||||||
assert_eq!(parse_vasm_line("#if"), Ok(VASMLine::Macro(Macro::If)));
|
assert_eq!(parse_vasm_line("#if"), Ok(VASMLine::Macro(Macro::If)));
|
||||||
|
assert_eq!(
|
||||||
|
parse_vasm_line("#unless"),
|
||||||
|
Ok(VASMLine::Macro(Macro::Unless))
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_vasm_line("#include \"blah\""),
|
parse_vasm_line("#include \"blah\""),
|
||||||
Ok(VASMLine::Macro(Macro::Include("blah".to_string())))
|
Ok(VASMLine::Macro(Macro::Include("blah".to_string())))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_blank() {
|
||||||
|
assert_eq!(parse_vasm_line(""), Ok(VASMLine::Blank));
|
||||||
|
assert_eq!(parse_vasm_line(" "), Ok(VASMLine::Blank));
|
||||||
|
assert_eq!(parse_vasm_line("; foo"), Ok(VASMLine::Blank));
|
||||||
|
assert_eq!(parse_vasm_line(" ;foo"), Ok(VASMLine::Blank));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use crate::vasm_parser::parse_vasm_line;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::iter::Enumerate;
|
use std::iter::Enumerate;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Line {
|
pub struct Line {
|
||||||
line: VASMLine,
|
pub line: VASMLine,
|
||||||
line_num: usize,
|
line_num: usize,
|
||||||
file: String,
|
file: String,
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-22
@@ -32,6 +32,12 @@ impl PeekPoke for CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for CPU {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(Memory::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn new(memory: Memory) -> Self {
|
pub fn new(memory: Memory) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -270,6 +276,33 @@ impl CPU {
|
|||||||
pub fn running(&self) -> bool {
|
pub fn running(&self) -> bool {
|
||||||
!self.halted
|
!self.halted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_to_halt(&mut self) {
|
||||||
|
self.start();
|
||||||
|
while !self.halted && self.pc < 1100 {
|
||||||
|
self.tick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_stack(&self) -> Vec<Word> {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
let mut curr = Word::from(256);
|
||||||
|
while curr < self.dp {
|
||||||
|
v.push(self.memory.peek24(curr));
|
||||||
|
curr += 3
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_call(&self) -> Vec<Word> {
|
||||||
|
let mut v = Vec::new();
|
||||||
|
let mut curr = Word::from(1024);
|
||||||
|
while curr > self.sp {
|
||||||
|
curr -= 3;
|
||||||
|
v.push(self.memory.peek24(curr));
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Opcode {
|
impl Opcode {
|
||||||
@@ -305,28 +338,6 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use Opcode::*;
|
use Opcode::*;
|
||||||
|
|
||||||
impl CPU {
|
|
||||||
fn get_stack(&self) -> Vec<Word> {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
let mut curr = Word::from(256);
|
|
||||||
while curr < self.dp {
|
|
||||||
v.push(self.memory.peek24(curr));
|
|
||||||
curr += 3
|
|
||||||
}
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_call(&self) -> Vec<Word> {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
let mut curr = Word::from(1024);
|
|
||||||
while curr > self.sp {
|
|
||||||
curr -= 3;
|
|
||||||
v.push(self.memory.peek24(curr));
|
|
||||||
}
|
|
||||||
v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn predicate_opcode_test<P, Q>(opcode: Opcode, given: P, pred: Q)
|
fn predicate_opcode_test<P, Q>(opcode: Opcode, given: P, pred: Q)
|
||||||
where
|
where
|
||||||
P: FnOnce(&mut CPU),
|
P: FnOnce(&mut CPU),
|
||||||
|
|||||||
@@ -2,3 +2,7 @@ pub mod cpu;
|
|||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod opcodes;
|
pub mod opcodes;
|
||||||
pub mod word;
|
pub mod word;
|
||||||
|
|
||||||
|
pub use cpu::CPU;
|
||||||
|
pub use memory::Memory;
|
||||||
|
pub use word::Word;
|
||||||
|
|||||||
@@ -20,6 +20,26 @@ impl<R: Rng> From<R> for Memory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Memory {
|
||||||
|
/// Create a memory containing the given contents (probably code) starting at `0x400` (the
|
||||||
|
/// starting address for code). The rest of the memory is zeroes.
|
||||||
|
/// ```
|
||||||
|
/// # use crate::vcore::memory::PeekPokeExt;
|
||||||
|
/// # use crate::vcore::Word;
|
||||||
|
/// # use crate::vcore::Memory;
|
||||||
|
/// let mem = Memory::with_program(vec![0x02, 0x12, 0x34]);
|
||||||
|
/// assert_eq!(mem.peek8(Word::from(0x400)), 0x02);
|
||||||
|
/// assert_eq!(mem.peek8(Word::from(0x401)), 0x12); // etc
|
||||||
|
/// ```
|
||||||
|
pub fn with_program<T: IntoIterator<Item = u8>>(code: T) -> Memory {
|
||||||
|
let mut m = Memory::default();
|
||||||
|
for (addr, byte) in code.into_iter().enumerate() {
|
||||||
|
m.poke8(addr + 0x400, byte)
|
||||||
|
}
|
||||||
|
m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Index<Word> for Memory {
|
impl std::ops::Index<Word> for Memory {
|
||||||
type Output = u8;
|
type Output = u8;
|
||||||
fn index(&self, index: Word) -> &Self::Output {
|
fn index(&self, index: Word) -> &Self::Output {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
// 128k, the amount of memory in a standard Vulcan machine
|
// 128k, the amount of memory in a standard Vulcan machine
|
||||||
pub const MEM_SIZE: u32 = 128 * 1024;
|
pub const MEM_SIZE: u32 = 128 * 1024;
|
||||||
|
|
||||||
@@ -45,6 +47,11 @@ impl From<Word> for u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Word {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", i32::from(*self))
|
||||||
|
}
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn to_from_u32() {
|
fn to_from_u32() {
|
||||||
assert_eq!(u32::from(Word::from(0x123456u32)), 0x123456u32);
|
assert_eq!(u32::from(Word::from(0x123456u32)), 0x123456u32);
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "vtest"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
vcore = { path = "../vcore" }
|
||||||
|
vasm = { path = "../vasm" }
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
use vasm::assemble_snippet;
|
||||||
|
use vcore::memory::{Memory, PeekPokeExt};
|
||||||
|
use vcore::CPU;
|
||||||
|
|
||||||
|
fn cpu_test<'a, T: IntoIterator<Item = &'a str>>(code: T) -> CPU {
|
||||||
|
let bin = assemble_snippet(code).unwrap();
|
||||||
|
let mut cpu = vcore::cpu::CPU::new(Memory::with_program(bin));
|
||||||
|
cpu.run_to_halt();
|
||||||
|
cpu
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_stack<'a, T: IntoIterator<Item = &'a str>>(code: T, expected_stack: Vec<i32>) {
|
||||||
|
let cpu = cpu_test(code);
|
||||||
|
assert_eq!(cpu.get_stack(), expected_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_rstack<'a, T: IntoIterator<Item = &'a str>>(
|
||||||
|
code: T,
|
||||||
|
expected_stack: Vec<i32>,
|
||||||
|
expected_rstack: Vec<i32>,
|
||||||
|
) {
|
||||||
|
let cpu = cpu_test(code);
|
||||||
|
assert_eq!(cpu.get_stack(), expected_stack);
|
||||||
|
assert_eq!(cpu.get_call(), expected_rstack);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mem<'a, T: IntoIterator<Item = &'a str>>(code: T, expected_memory: BTreeMap<u32, u8>) {
|
||||||
|
let cpu = cpu_test(code);
|
||||||
|
for (addr, byte) in expected_memory {
|
||||||
|
assert_eq!(cpu.peek8(addr), byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arithmetic() {
|
||||||
|
// Basic arithmetic
|
||||||
|
test_stack(
|
||||||
|
".org 0x400
|
||||||
|
push 2
|
||||||
|
add 3
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![5],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Multibyte values
|
||||||
|
test_stack(
|
||||||
|
".org 0x400
|
||||||
|
push 7
|
||||||
|
mul 1000
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![7000],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_call_stack() {
|
||||||
|
test_stack(
|
||||||
|
".org 0x400
|
||||||
|
nop 3
|
||||||
|
call blah
|
||||||
|
hlt
|
||||||
|
blah: mul 2
|
||||||
|
ret"
|
||||||
|
.lines(),
|
||||||
|
vec![6],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_rstack(
|
||||||
|
".org 0x400
|
||||||
|
push 10
|
||||||
|
push 4
|
||||||
|
push 3
|
||||||
|
pushr
|
||||||
|
pushr
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![10],
|
||||||
|
vec![3, 4],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_rstack(
|
||||||
|
".org 0x400
|
||||||
|
push 10
|
||||||
|
pushr 20
|
||||||
|
pushr 4
|
||||||
|
push 3
|
||||||
|
popr
|
||||||
|
add
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![10, 7],
|
||||||
|
vec![20],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_rstack(
|
||||||
|
".org 0x400
|
||||||
|
pushr 5
|
||||||
|
call blah
|
||||||
|
blah: pushr 3
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![],
|
||||||
|
vec![5, 0x406, 3],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_rstack(
|
||||||
|
".org 0x400
|
||||||
|
push 5
|
||||||
|
pushr 10
|
||||||
|
pushr 20
|
||||||
|
peekr
|
||||||
|
popr
|
||||||
|
popr
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![5, 20, 20, 10],
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_comparison() {
|
||||||
|
test_stack(
|
||||||
|
".org 0x400
|
||||||
|
push 10
|
||||||
|
gt 20
|
||||||
|
push 20
|
||||||
|
gt 5
|
||||||
|
push 10
|
||||||
|
lt 20
|
||||||
|
push 10
|
||||||
|
lt 5
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![0, 1, 1, 0],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_stack(
|
||||||
|
".org 1024
|
||||||
|
push 10
|
||||||
|
mul 0xffffff
|
||||||
|
agt 20
|
||||||
|
push 20
|
||||||
|
agt -5
|
||||||
|
push -10
|
||||||
|
alt 20
|
||||||
|
push 10
|
||||||
|
alt -5
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![0, 1, 1, 0],
|
||||||
|
);
|
||||||
|
|
||||||
|
test_stack(
|
||||||
|
".org 1024
|
||||||
|
not 10
|
||||||
|
not 0
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![0, 1],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pick() {
|
||||||
|
test_stack(
|
||||||
|
".org 1024
|
||||||
|
nop 10
|
||||||
|
nop 20
|
||||||
|
pick 1
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![10, 20, 10],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_store() {
|
||||||
|
test_mem(
|
||||||
|
".org 0x400
|
||||||
|
push 10
|
||||||
|
store 201
|
||||||
|
push 0x123456
|
||||||
|
storew 203
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
[(201, 10), (203, 0x56), (204, 0x34), (205, 0x12)].into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load() {
|
||||||
|
test_stack(
|
||||||
|
".org 0x400
|
||||||
|
load 0x501
|
||||||
|
loadw 0x500
|
||||||
|
hlt
|
||||||
|
.org 0x500
|
||||||
|
.db 0x123456"
|
||||||
|
.lines(),
|
||||||
|
vec![0x34, 0x123456],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rotate() {
|
||||||
|
test_stack(
|
||||||
|
".org 0x400
|
||||||
|
push 10
|
||||||
|
push 100
|
||||||
|
push 200
|
||||||
|
push 300
|
||||||
|
rot
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![10, 200, 300, 100],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sdp() {
|
||||||
|
test_stack(
|
||||||
|
".org 0x400
|
||||||
|
push 10
|
||||||
|
pushr 20
|
||||||
|
sdp
|
||||||
|
hlt"
|
||||||
|
.lines(),
|
||||||
|
vec![10, 1021, 265],
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod integration_tests;
|
||||||
Reference in New Issue
Block a user