feat: complete assembler implementation, minus some instructions

This commit is contained in:
Nat 2024-12-10 15:48:14 -08:00
parent 961725d9d7
commit 5db045990c
2 changed files with 116 additions and 27 deletions

View File

@ -1,6 +1,7 @@
use std::fs;
use std::env;
use std::io;
use std::io::prelude::*;
use std::collections::{HashMap, BTreeMap, VecDeque};
@ -22,7 +23,7 @@ fn parse_number(string: &str) -> u16 {
string.parse::<u16>().unwrap()
}
fn stack_bit(token: &String) -> Result<u16, String> {
fn stack_bit(token: &str) -> Result<u16, String> {
let c = token.as_bytes()[0] as char;
match c {
'r' => Ok(0),
@ -31,29 +32,31 @@ fn stack_bit(token: &String) -> Result<u16, String> {
}
}
fn parse_unary_math_format<'a>(tokens: &Vec<String>, opcode: u16, signed: bool) -> Result<u16, String> {
assert_argument_count!(tokens, 4);
// e.g. not r t
fn parse_unary_op(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result<u16, String> {
assert_argument_count!(tokens, 3);
let src = stack_bit(&tokens[1])?;
let dest = stack_bit(&tokens[2])?;
let src = stack_bit(tokens[1])?;
let dest = stack_bit(tokens[2])?;
Ok(opcode<<11 & src<<9 & dest<<8 & (signed as u16)<<7)
}
fn parse_binary_math_format<'a>(tokens: &Vec<String>, opcode: u16, signed: bool) -> Result<u16, String> {
// e.g. add r r t
fn parse_binary_op(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result<u16, String> {
assert_argument_count!(tokens, 4);
let left = stack_bit(&tokens[1])?;
let right = stack_bit(&tokens[2])?;
let dest = stack_bit(&tokens[3])?;
let left = stack_bit(tokens[1])?;
let right = stack_bit(tokens[2])?;
let dest = stack_bit(tokens[3])?;
Ok(opcode<<11 & left<<10 & right<<9 & dest<<8 & (signed as u16)<<7)
Ok(opcode<<11 | left<<10 | right<<9 | dest<<8 | (signed as u16)<<7)
}
fn parse_push(tokens: &Vec<String>, opcode: u16, signed: bool) -> Result<u16, String> {
fn parse_push(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result<u16, String> {
assert_argument_count!(tokens, 3);
let dest = stack_bit(&tokens[1])?;
let dest = stack_bit(tokens[1])?;
let signed_immediate = tokens[2].parse::<i16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
@ -66,7 +69,33 @@ fn parse_push(tokens: &Vec<String>, opcode: u16, signed: bool) -> Result<u16, St
return Err("Cannot push number larger than 511 due to bit width limitations".to_owned());
}
Ok(opcode<<11 & dest<<10 & (signed as u16) << 9 & immediate)
Ok(opcode<<11 | dest<<10 | (signed as u16) << 9 | immediate)
}
// e.g. sow t -2 3
fn parse_forestry_op(tokens: &Vec<&str>, opcode: u16) -> Result<u16, String> {
assert_argument_count!(tokens, 4);
let stack = stack_bit(tokens[1])?;
let relative_x = tokens[2].parse::<i16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
let relative_y = tokens[3].parse::<i16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
if relative_x > 15 || relative_x < -16 {
Err(format!("Cannot reach cell at {relative_x} along the X-axis; trees may only access cells in the range of -16 to 15"))
} else if relative_y > 15 || relative_y < -16 {
Err(format!("Cannot reach cell at {relative_y} along the Y-axis; trees may only access cells in the range of -16 to 15"))
} else {
Ok(opcode<<11 | stack<<10 | (relative_x as u16)<<5 | (relative_y as u16))
}
}
// Take a Vec<u8> and a u16, push two corresponding u8s
fn push_u16(vec: &mut Vec<u8>, value: u16) {
vec.push(((value & 0b11111111_00000000) >> 8) as u8);
vec.push((value & 0b00000000_11111111) as u8);
}
fn main() -> io::Result<()> {
@ -90,6 +119,7 @@ fn main() -> io::Result<()> {
let mut current_rule_name: String = String::from("");
let mut current_rule: Rule = VecDeque::new();
let mut line_number = 1;
for source_line in source_code.lines() {
let tokens: Vec<&str> = source_line
.split_whitespace()
@ -102,7 +132,6 @@ fn main() -> io::Result<()> {
if reading_land_values {
if tokens.len() < 3 {
reading_land_values = false;
break;
} else {
let (value, x_pos, y_pos) = (tokens[0].parse::<u16>(), tokens[1].parse::<u16>(), tokens[2].parse::<u16>());
match (value, x_pos, y_pos) {
@ -125,14 +154,46 @@ fn main() -> io::Result<()> {
rules.insert(current_rule_name, new_rule);
}
reading_rule = true;
current_rule_name = String::from(&tokens[0][1..]);
continue;
}
if reading_rule {
let parse_instruction_result = match tokens[0] {
"add" => Ok(1),
"and" => parse_binary_op(&tokens, 0b00001, false),
"nand" => parse_binary_op(&tokens, 0b00010, false),
"or" => parse_binary_op(&tokens, 0b00011, false),
"nor" => parse_binary_op(&tokens, 0b00100, false),
"xor" => parse_binary_op(&tokens, 0b00101, false),
"add" => parse_binary_op(&tokens, 0b00110, false),
"mult" => parse_binary_op(&tokens, 0b00111, false),
"div" => parse_binary_op(&tokens, 0b01000, false),
"slt" => parse_binary_op(&tokens, 0b01001, false),
"not" => parse_unary_op(&tokens, 0b01010, false),
"sll" => parse_binary_op(&tokens, 0b01011, false),
"srl" => parse_binary_op(&tokens, 0b01100, false),
"push" => parse_push(&tokens, 0b01101, false),
"sow" => parse_forestry_op(&tokens, 0b10101),
"reap" => parse_forestry_op(&tokens, 0b10110),
"plant" => parse_forestry_op(&tokens, 0b10111),
"replant" => parse_forestry_op(&tokens, 0b11000),
"yield" => Ok(0b11001_0_00000_00000),
"compost" => Ok(0b11010_0_00000_00000),
_ => Err(format!("Unknown instruction: {token}", token=tokens[0])),
};
let instruction = match parse_instruction_result {
Ok(ins) => ins,
Err(msg) => panic!("Error on line {line_number}:\n\n{source_line}\n\n{msg}"),
};
current_rule.push_back(instruction);
}
match tokens[0] {
@ -143,11 +204,51 @@ fn main() -> io::Result<()> {
"land" => reading_land_values = true,
_ => continue,
};
line_number += 1;
}
// Save the last rule
rules.insert(current_rule_name, current_rule);
println!("Original stack size: {}", original_stack_size);
println!("Land dimensions: {}x{}", land.width, land.height);
println!("Seed tree position: ({}, {})", seed_cell_position.0, seed_cell_position.1);
let mut rom_bytes: Vec<u8> = Vec::new();
push_u16(&mut rom_bytes, original_stack_size);
push_u16(&mut rom_bytes, land.width);
push_u16(&mut rom_bytes, land.height);
push_u16(&mut rom_bytes, seed_cell_position.0);
push_u16(&mut rom_bytes, seed_cell_position.1);
push_u16(&mut rom_bytes, land.map.len() as u16);
for position in land.map.keys() {
push_u16(&mut rom_bytes, *land.map.get(position).unwrap());
push_u16(&mut rom_bytes, position.0);
push_u16(&mut rom_bytes, position.1);
}
push_u16(&mut rom_bytes, 0b0);
for rule in rules.values() {
for instruction in rule {
push_u16(&mut rom_bytes, *instruction);
}
push_u16(&mut rom_bytes, 0b0);
}
for byte_index in 0..rom_bytes.len() {
print!("{:08b}", rom_bytes[byte_index]);
if byte_index % 2 != 0 {
println!();
}
}
let mut output_file = fs::File::create("a.out")?;
output_file.write_all(rom_bytes.as_slice())?;
Ok(())
}

View File

@ -1,12 +0,0 @@
stack_size 20
land_width 8
land_height 8
seed_cell 0 0
land
8 0 0
.seed
reap r 0 0
push r 2
mult r r t
sow t 0 0