Commit 2dfa1549 authored by Kai Rese's avatar Kai Rese
Browse files

Upgrade to specification version 3

parent 959076ca
# This program searches for 2048 prime numbers.
# It does so by saving found prime numbers in a list and checking each
# new number against all already found primes.
# reg0 -> function parameter & return value
alias reg0 fnParam0
alias reg0 retVal
# reg1 -> function parameter
alias reg0 fnParam1
alias reg7 testNum
alias reg8 numLeft
alias reg9 listIter
# points beyond the last element
alias reg10 listEnd
alias reg11 prime
alias reg12 lenLeft
alias reg13 retAddr
:Initialization
add reg1 1
add reg0 1
shftl reg1 reg1 11
stg reg3 :ListStart
:NextNumber
add reg0 1
stg reg2 :ListStart
:ListLoop
sub reg7 reg3 reg2
bez reg7 :FoundNumber
load reg4 reg2 reg15
stg reg6 +6
store reg6 reg14 reg15
add reg14 -2
jmp :Modulo
bez reg8 :NextNumber
add reg2 2
jmp :ListLoop
:FoundNumber
store reg0 reg2 reg15
add reg1 -1
add reg3 2
bgz reg1 :NextNumber
add numLeft 1
add testNum 1
shiftl numLeft 11
target listEnd :ListStart
# foot controlled loop
:FindPrimeBegin
add testNum 1
target listIter :ListStart
# head controlled loop
:CheckPrimeBegin
sub lenLeft listEnd listIter
bzero lenLeft :CheckPrimeEnd
load fnParam0 listIter
add fnParam1 testNum
target retAddr +3
store retAddr sp
add sp -1
jump :Modulo
# Not a prime, next number
bzero retVal :FindPrimeBegin
add listIter 1
jump :CheckPrimeBegin
:CheckPrimeEnd
add numLeft -1
bzero numLeft :FindPrimeEnd
jump :FindPrimeBegin
:FindPrimeEnd
stop
# Modulo in logarithmic runtime:
# Find the largest power-of-two of the divisor fitting inside the
# dividend, then subtract it from the dividend (if possible) and half
# the divisor until it's at the original value again. The remainder is
# our result.
# This does: reg0 = reg0 % reg1
alias reg0 remainder
alias reg1 divisor
alias reg2 dividend
alias reg3 shftCnt
:Modulo
add reg10 reg4 reg15
add reg11 reg15 reg15
:ShiftLeft
shftl reg10 reg10 1
add reg11 1
sub reg8 reg0 reg10
bgz reg8 :ShiftLeft
bez reg8 :ShiftLeft
shftr reg10 reg10 1
add reg8 reg0 reg15
:ShiftRight
sub reg8 reg8 reg10
bgz reg8 :Skip
bez reg8 :Skip
add reg8 reg8 reg10
:Skip
add reg11 -1
shftr reg10 reg10 1
bgz reg11 :ShiftRight
add reg14 2
load reg9 reg14 reg15
jmp reg9 0
:ListStart
\ No newline at end of file
add dividend fnParam0 zero
add shftCnt zero zero
# loop
:ShiftLeftBegin
shiftl divisor
# use retVal as temp value
sub remainder dividend divisor
bneg remainder :ShiftLeftEnd
add shftCnt 1
jump :ShiftLeftBegin
:ShiftLeftEnd
# undo last step
shiftr divisor
add remainder dividend zero
# loop
:SubtrationBegin
sub remainder divisor
add shftCnt -1
bneg remainder :Undo
:UndoReturn
bneg shftCnt :SubtractionEnd
shiftr divisor
jump :SubtrationBegin
:SubtractionEnd
add sp 1
load retAddr sp
jump retAddr
# if
:Undo
add remainder divisor
jump :UndoReturn
# begin of data section
:ListStart
/*!
# Arschitek_zero_assembler
This is an assembler for the arschitek_zero instruction set architecture.
*/
mod program;
use program::assembler::assemble;
use program::parser;
use program::scanner;
pub fn run(filename: &str) -> Result<(), Box<dyn std::error::Error>> {
let raw = std::fs::read_to_string(filename)?;
let tokens = scanner::scan(&raw);
let statements = parser::parse(&tokens)?;
let code = assemble(&statements)?;
let output_filename = String::from(filename) + ".out";
std::fs::write(output_filename, code)?;
Ok(())
}
extern crate arschitek_zero_assembler;
mod program;
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() == 2 {
if let Err(e) = arschitek_zero_assembler::run(args[1].as_str()) {
println!("{}", e);
if let Err(e) = program::run(args[1].as_str()) {
eprintln!("Failed!");
eprintln!("{}", e);
}
} else {
println!("Usage: {} <filename>", args[0]);
eprintln!("Usage: {} <filename>", args[0]);
}
}
pub mod error;
pub mod instruction;
pub mod statement;
pub mod token;
mod opcodes;
mod statement;
pub mod assembler;
pub mod parser;
pub mod scanner;
mod assemble;
mod parse;
mod scan;
mod token;
pub fn run(filename: &str) -> Result<(), Box<dyn std::error::Error>> {
eprint!("Reading input file... ");
let raw = std::fs::read_to_string(filename)?;
eprintln!("Done!");
eprint!("Scanning file... ");
let tokens = scan::scan(raw)?;
eprintln!("Done!");
eprint!("Parsing tokens... ");
let statements = parse::parse(tokens)?;
eprintln!("Done!");
eprint!("Assembling program... ");
let code = assemble::assemble(statements)?;
eprintln!("Done!");
eprint!("Outputting file... ");
let output_filename = String::from(filename) + ".out";
std::fs::write(output_filename, code)?;
println!("Done!");
Ok(())
}
use crate::program::opcodes::*;
use crate::program::statement::{AddressOffset, Statement};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
pub fn assemble(statements: Vec<Statement>) -> Result<Vec<u8>, AssemblingFailed> {
let mut output = Vec::<u8>::with_capacity(statements.len() * 2);
let mut errors = Vec::new();
let mut register_symbols: HashMap<String, u16> = [
("reg0".to_string(), 0x0),
("reg1".to_string(), 0x1),
("reg2".to_string(), 0x2),
("reg3".to_string(), 0x3),
("reg4".to_string(), 0x4),
("reg5".to_string(), 0x5),
("reg6".to_string(), 0x6),
("reg7".to_string(), 0x7),
("reg8".to_string(), 0x8),
("reg9".to_string(), 0x9),
("reg10".to_string(), 0xa),
("reg11".to_string(), 0xb),
("reg12".to_string(), 0xc),
("reg13".to_string(), 0xd),
("reg14".to_string(), 0xe),
("reg15".to_string(), 0xf),
("sp".to_string(), 0xe),
("zero".to_string(), 0xf),
]
.iter()
.cloned()
.collect();
let mut label_symbols = HashMap::<String, usize>::new();
let mut curr_position = 0;
// pass 1
// get labels
for (line, statement) in statements.iter().enumerate() {
match statement {
Statement::Empty => {}
Statement::SetAlias { .. } => {}
Statement::SetLabel(name) => {
if let Entry::Vacant(entry) = label_symbols.entry(name.clone()) {
entry.insert(curr_position);
} else {
errors.push(AssembleError {
line,
kind: AssembleErrorKind::RedefinedLabel(name.clone()),
})
}
}
_ => {
curr_position += 1;
}
}
}
let mut curr_position = 0;
// pass 2
// assembling with defined labels
for (line, statement) in statements.into_iter().enumerate() {
match statement {
Statement::Empty => {}
Statement::SetLabel(_) => {}
Statement::SetAlias {
source,
destination,
} => {
if let Some(&register_index) = register_symbols.get(source.as_str()) {
register_symbols.insert(destination, register_index);
} else {
errors.push(AssembleError {
line,
kind: AssembleErrorKind::UndefinedRegisterIdentifier(source),
});
}
}
statement => {
match assemble_statement(
&register_symbols,
&label_symbols,
statement,
curr_position,
) {
Ok(data) => {
output.extend_from_slice(&data.to_le_bytes());
}
Err(error) => {
errors.push(AssembleError { line, kind: error });
}
}
curr_position += 1;
}
}
}
if !errors.is_empty() {
Err(AssemblingFailed { errors })
} else {
Ok(output)
}
}
#[derive(Debug)]
pub struct AssemblingFailed {
errors: Vec<AssembleError>,
}
#[derive(Debug)]
pub struct AssembleError {
line: usize,
kind: AssembleErrorKind,
}
#[derive(Debug)]
pub enum AssembleErrorKind {
AddressOutOfRange {
value: isize,
bits: u8,
signed: bool,
},
RedefinedLabel(String),
UndefinedLabel(String),
UndefinedRegisterIdentifier(String),
ValueOutOfRange {
value: isize,
bits: u8,
signed: bool,
},
}
fn assemble_statement(
register_symbols: &HashMap<String, u16>,
label_symbols: &HashMap<String, usize>,
statement: Statement,
position: usize,
) -> Result<u16, AssembleErrorKind> {
match statement {
Statement::Data(value) => {
assert_in_range(value, 16, false, false)?;
Ok(value as u16)
}
Statement::LoadRelative {
destination,
offset,
} => {
let destination = get_register(register_symbols, destination)?;
let offset = get_offset(label_symbols, position, offset)?;
assert_in_range(offset, 8, true, true)?;
Ok((OPCODE_LOAD_RELATIVE << 12) | (destination << 8) | offset as u16)
}
Statement::LoadBaseOffset {
destination,
base,
offset,
} => {
let destination = get_register(register_symbols, destination)?;
let base = get_register(register_symbols, base)?;
assert_in_range(offset, 4, true, true)?;
Ok((OPCODE_LOAD_BASE_OFFSET << 12) | (destination << 8) | (base << 4) | offset as u16)
}
Statement::StoreRelative { data, offset } => {
let data = get_register(register_symbols, data)?;
let offset = get_offset(label_symbols, position, offset)?;
assert_in_range(offset, 8, true, true)?;
Ok((OPCODE_STORE_RELATIVE << 12) | (data << 8) | offset as u16)
}
Statement::StoreBaseOffset { data, base, offset } => {
let data = get_register(register_symbols, data)?;
let base = get_register(register_symbols, base)?;
assert_in_range(offset, 4, true, true)?;
Ok((OPCODE_STORE_BASE_OFFSET << 12) | (data << 8) | (base << 4) | offset as u16)
}
Statement::JumpRelative(offset) => {
let offset = get_offset(label_symbols, position, offset)?;
assert_in_range(offset, 12, true, true)?;
Ok((OPCODE_JUMP_RELATIVE << 12) | offset as u16)
}
Statement::JumpBaseOffset { base, offset } => {
let base = get_register(register_symbols, base)?;
assert_in_range(offset, 8, true, true)?;
Ok((OPCODE_JUMP_BASE_OFFSET << 12) | (base << 8) | offset as u16)
}
Statement::BranchEqualZero { test_value, offset } => {
let test_value = get_register(register_symbols, test_value)?;
let offset = get_offset(label_symbols, position, offset)?;
assert_in_range(offset, 8, true, true)?;
Ok((OPCODE_BRANCH_EQUAL_ZERO << 12) | (test_value << 8) | offset as u16)
}
Statement::BranchNegative { test_value, offset } => {
let test_value = get_register(register_symbols, test_value)?;
let offset = get_offset(label_symbols, position, offset)?;
assert_in_range(offset, 8, true, true)?;
Ok((OPCODE_BRANCH_NEGATIVE << 12) | (test_value << 8) | offset as u16)
}
Statement::AddRegister {
destination,
addend_a,
addend_b,
} => {
let destination = get_register(register_symbols, destination)?;
let addend_a = get_register(register_symbols, addend_a)?;
let addend_b = get_register(register_symbols, addend_b)?;
Ok((OPCODE_ADD_REGISTER << 12) | (destination << 8) | (addend_a << 4) | addend_b)
}
Statement::AddImmediate { target, addend } => {
let target = get_register(register_symbols, target)?;
assert_in_range(addend, 8, true, false)?;
Ok((OPCODE_ADD_IMMEDIATE << 12) | (target << 8) | addend as u16)
}
Statement::Subtract {
destination,
minuend,
subtrahend,
} => {
let destination = get_register(register_symbols, destination)?;
let minuend = get_register(register_symbols, minuend)?;
let subtrahend = get_register(register_symbols, subtrahend)?;
Ok((OPCODE_SUBTRACT << 12) | (destination << 8) | (minuend << 4) | subtrahend)
}
Statement::SetToTarget {
destination,
offset,
} => {
let destination = get_register(register_symbols, destination)?;
let offset = get_offset(label_symbols, position, offset)?;
assert_in_range(offset, 8, true, true)?;
Ok((OPCODE_TARGET << 12) | (destination << 8) | offset as u16)
}
Statement::ShiftLeft {
destination,
base_value,
amount,
} => {
let destination = get_register(register_symbols, destination)?;
let base_value = get_register(register_symbols, base_value)?;
assert_in_range(amount, 4, false, false)?;
Ok((OPCODE_SHIFT_LEFT << 12) | (destination << 8) | (base_value << 4) | amount as u16)
}
Statement::ShiftRight {
destination,
base_value,
amount,
} => {
let destination = get_register(register_symbols, destination)?;
let base_value = get_register(register_symbols, base_value)?;
assert_in_range(amount, 4, false, false)?;
Ok((OPCODE_SHIFT_RIGHT << 12) | (destination << 8) | (base_value << 4) | amount as u16)
}
Statement::BooleanTable {
target,
input_b,
mode_bits,
} => {
let target = get_register(register_symbols, target)?;
let input_b = get_register(register_symbols, input_b)?;
assert_in_range(mode_bits, 4, false, false)?;
Ok((OPCODE_BOOLEAN_TABLE << 12) | (target << 8) | (input_b << 4) | mode_bits as u16)
}
Statement::Stop => Ok(OPCODE_STOP << 12),
_ => unreachable!(),
}
}
fn assert_in_range(
test_value: isize,
bits: u8,
signed: bool,
is_address: bool,
) -> Result<(), AssembleErrorKind> {
if (signed
& (test_value < (1 << (bits - 1)) as isize)
& (test_value >= (-1 << (bits - 1)) as isize))
|| (!signed & (test_value < (1 << bits) as isize))
{
Ok(())
} else if is_address {
Err(AssembleErrorKind::AddressOutOfRange {
value: test_value,
bits,
signed,
})
} else {
Err(AssembleErrorKind::ValueOutOfRange {
value: test_value,
bits,
signed,
})
}
}
fn get_offset(
label_symbols: &HashMap<String, usize>,
position: usize,
offset: AddressOffset,
) -> Result<isize, AssembleErrorKind> {
if let Some(target_label) = offset.target_label {
if let Some(&label_position) = label_symbols.get(target_label.as_str()) {
Ok(label_position as isize - position as isize - 1 + offset.offset)
} else {
Err(AssembleErrorKind::UndefinedLabel(target_label))
}
} else {
Ok(position as isize + 1 + offset.offset)
}
}
fn get_register(
register_symbols: &HashMap<String, u16>,
identifier: String,
) -> Result<u16, AssembleErrorKind> {
if let Some(&register) = register_symbols.get(identifier.as_str()) {
Ok(register)
} else {
Err(AssembleErrorKind::UndefinedRegisterIdentifier(identifier))
}
}
impl std::fmt::Display for AssemblingFailed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.errors[0])?;
for e in self.errors.iter().skip(1) {
write!(f, "\n{}", e)?;
}
Ok(())
}
}
impl std::error::Error for AssemblingFailed {}
impl std::fmt::Display for AssembleError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[line {:04}] {}", self.line, self.kind)
}
}
impl std::fmt::Display for AssembleErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AssembleErrorKind::AddressOutOfRange {
value,
bits,
signed,
} => {
write!(
f,
"Address out of range, is {}, valid range is from {} to {}.",
value,
0 - (1isize << (bits - 1)) * *signed as isize,
(1isize << bits) - 1 - (1isize << (bits - 1)) * *signed as isize,
)
}
AssembleErrorKind::RedefinedLabel(name) => {
write!(f, "The label \"{}\" has been defined multiple times.", name)
}
AssembleErrorKind::UndefinedLabel(name) => {
write!(f, "The label \"{}\" isn't defined.", name)
}
AssembleErrorKind::UndefinedRegisterIdentifier(name) => {
write!(f, "\"{}\" is no valid register identifier.", name)
}
AssembleErrorKind::ValueOutOfRange {
value,
bits,
signed,
} => {
write!(
f,
"Value out of range, is {}, valid range is from {} to {}.",
value,
0 - (1isize << (bits - 1)) * *signed as isize,
(1isize << bits) - 1 - (1isize << (bits - 1)) * *signed as isize,
)
}
}
}
}