Compare commits

...

8 Commits

6 changed files with 174 additions and 70 deletions

View File

@ -63,7 +63,9 @@ op code | stack | use literal? | signed? | literal
--------|-------|--------------|---------|----------
00000 | 000 | 0 | 0 | 000000
- `and`; 0000 1
If a literal value is used, the literal value replaces the right operand
- `and`
- `nand`
- `or`
- `nor`

View File

@ -17,6 +17,14 @@ macro_rules! assert_argument_count {
return Err("Too many arguments".to_owned());
}
};
($tokens:expr, $count:expr, $body:block) => {
if $tokens.len() < $count {
Err("Too few arguments".to_owned());
} else if $tokens.len() > $count {
Err("Too many arguments".to_owned());
} else $body
};
}
fn parse_number(string: &str) -> u16 {
@ -32,16 +40,6 @@ fn stack_bit(token: &str) -> Result<u16, String> {
}
}
// 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])?;
Ok(opcode<<11 & src<<9 & dest<<8 & (signed as u16)<<7)
}
// 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);
@ -53,6 +51,33 @@ fn parse_binary_op(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result<u16,
Ok(opcode<<11 | left<<10 | right<<9 | dest<<8 | (signed as u16)<<7)
}
fn parse_binary_immediate_op(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result<u16, String> {
assert_argument_count!(tokens, 4);
let signed_immediate = tokens[2].parse::<i16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
let unsigned_immediate = tokens[2].parse::<u16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
if signed {
if signed_immediate < -32 || signed_immediate > 31 {
return Err("Signed immediate values for math operations must be between -32 and 31".to_owned());
}
} else {
if unsigned_immediate > 63 {
return Err("Unsigned immediate values for math operations must be between 0 and 61".to_owned());
}
}
let right = if signed { signed_immediate as u16 } else { unsigned_immediate };
let left = stack_bit(tokens[1])?;
let dest = stack_bit(tokens[3])?;
Ok(opcode<<11 | right<<9 | dest<<8 | (signed as u16)<<7 | (left & 0b111111))
}
fn parse_push(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result<u16, String> {
assert_argument_count!(tokens, 3);
@ -72,6 +97,15 @@ fn parse_push(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result<u16, Stri
Ok(opcode<<11 | dest<<10 | (signed as u16) << 9 | immediate)
}
fn parse_stack_op(tokens: &Vec<&str>, opcode: u16) -> Result<u16, String> {
assert_argument_count!(tokens, 3);
let src = stack_bit(tokens[1])?;
let dest= stack_bit(tokens[2])?;
Ok(opcode<<11 | src<<10 | dest<<9)
}
// e.g. sow t -2 3
fn parse_forestry_op(tokens: &Vec<&str>, opcode: u16) -> Result<u16, String> {
assert_argument_count!(tokens, 4);
@ -88,7 +122,13 @@ fn parse_forestry_op(tokens: &Vec<&str>, opcode: u16) -> Result<u16, String> {
} 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))
Ok(
(opcode<<11)
| (stack<<10)
| (((relative_x as u16) & 0b11111)<<5)
| ((relative_y as u16) & 0b11111)
)
}
}
@ -160,30 +200,82 @@ fn main() -> io::Result<()> {
}
if reading_rule {
let parse_instruction_result = match tokens[0] {
let parse_instruction_result: Result<u16, String> = match tokens[0] {
"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),
"add" => parse_binary_op(&tokens, 0b00110, true),
"mult" => parse_binary_op(&tokens, 0b00111, true),
"div" => parse_binary_op(&tokens, 0b01000, true),
"slt" => parse_binary_op(&tokens, 0b01001, true),
"not" => assert_argument_count!(tokens, 3, {
let opcode = 0b10010;
let (src, dest) = (stack_bit(tokens[1])?, stack_bit(tokens[2])?);
Ok(opcode<<11 & src<<9 & dest<<8)
}),
"sll" => parse_binary_op(&tokens, 0b01011, false),
"srl" => parse_binary_op(&tokens, 0b01100, false),
"push" => parse_push(&tokens, 0b01101, false),
"addu" => parse_binary_op(&tokens, 0b00110, false),
"multu" => parse_binary_op(&tokens, 0b00111, false),
"divu" => parse_binary_op(&tokens, 0b01000, false),
"sltu" => parse_binary_op(&tokens, 0b01001, 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),
"andi" => parse_binary_immediate_op(&tokens, 0b00001, false),
"nandi" => parse_binary_immediate_op(&tokens, 0b00010, false),
"ori" => parse_binary_immediate_op(&tokens, 0b00011, false),
"nori" => parse_binary_immediate_op(&tokens, 0b00100, false),
"xori" => parse_binary_immediate_op(&tokens, 0b00101, false),
"addi" => parse_binary_immediate_op(&tokens, 0b00110, true),
"multi" => parse_binary_immediate_op(&tokens, 0b00111, true),
"divi" => parse_binary_immediate_op(&tokens, 0b01000, true),
"slti" => parse_binary_immediate_op(&tokens, 0b01001, true),
"addiu" => parse_binary_immediate_op(&tokens, 0b00110, false),
"multiu" => parse_binary_immediate_op(&tokens, 0b00111, false),
"diviu" => parse_binary_immediate_op(&tokens, 0b01000, false),
"sltiu" => parse_binary_immediate_op(&tokens, 0b01001, false),
"push" => parse_push(&tokens, 0b01101, true),
"pushu" => parse_push(&tokens, 0b01101, false),
"pull" => parse_stack_op(&tokens, 0b01110),
"swap" => parse_stack_op(&tokens, 0b01111),
"rotate" => parse_stack_op(&tokens, 0b10000),
"flip" => parse_stack_op(&tokens, 0b10001),
"clear" => {
let opcode = 0b10010;
assert_argument_count!(tokens, 2);
let to_clear = stack_bit(tokens[1])?;
Ok(opcode<<11 | to_clear<<10)
},
"skip" => {
let opcode = 0b10011;
assert_argument_count!(tokens, 2);
Ok(opcode<<11 | (tokens[1].parse::<u16>() & 0b111111111))
},
"skim" => {
let opcode = 0b10100;
assert_argument_count!(tokens, 2);
Ok(opcode<<11 | stack_bit(tokens[1])? << 9)
},
"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),
"define" => {
let opcode = 0b11011;
assert_argument_count!(tokens, 2);
Ok(opcode<<11 | stack_bit(tokens[1])? << 10)
},
"wait" => parse_forestry_op(&tokens, 0b11100),
"signal" => parse_forestry_op(&tokens, 0b11101),
_ => Err(format!("Unknown instruction: {token}", token=tokens[0])),
};

View File

@ -2,7 +2,7 @@ use std::io;
use std::env;
use std::fs::File;
use std::io::Read;
use std::collections::{VecDeque, HashMap, BTreeSet};
use std::collections::{VecDeque, HashMap, BTreeSet, BTreeMap};
use ponderosa::{math, stack, state, control, forestry};
@ -67,9 +67,9 @@ fn parse_rom(path: &String) -> io::Result<(state::Land, Vec<state::Rule>, HashMa
println!("World\n=====");
for col_idx in 0..land_width {
for row_idx in 0..land_height {
print!("{}\t", land.map.get(&(col_idx, row_idx)).unwrap_or(&0u16));
for y in (0..land.height).rev() {
for x in 0..land.width {
print!("{}\t", land.map.get(&(x, y)).unwrap_or(&0u16));
}
println!();
}
@ -99,7 +99,7 @@ fn parse_rom(path: &String) -> io::Result<(state::Land, Vec<state::Rule>, HashMa
resume_from: 0,
stack_size: original_stack_size,
position: (seed_cell_x % land.width, seed_cell_y % land.height),
span: [[0u16; 8]; 8],
buffer: BTreeMap::new(),
});
Ok((land, rules, trees))
@ -166,8 +166,8 @@ fn main() -> io::Result<()> {
18 => stack::op_clear(word, &mut tree),
19 => control::op_skip(word, &mut instruction_pointer),
20 => control::op_skim(word, &mut tree, &mut instruction_pointer),
21 => forestry::op_sow(word, tree),
22 => forestry::op_reap(word, tree),
21 => forestry::op_sow(word, tree, &land),
22 => forestry::op_reap(word, tree, &land),
23 => forestry::op_plant(word, tree, &mut next_tree_id, &mut trees_to_plant, &mut land),
24 => forestry::op_replant(word, tree, &land),
25 => break, // yeild
@ -183,6 +183,18 @@ fn main() -> io::Result<()> {
instruction_pointer += 1;
}
// Update the land with the new state of every tree.
for tree_index in 0..scheduled_trees.len() {
let tree_id = &scheduled_trees[tree_index];
let tree = trees.get_mut(tree_id).unwrap();
for coordinates in tree.buffer.keys() {
land.sow(&coordinates, *tree.buffer.get(&coordinates).unwrap());
}
tree.buffer.clear();
}
}
while let Some(tree_index) = trees_to_compost_by_index.pop_first() {
@ -216,9 +228,9 @@ fn main() -> io::Result<()> {
println!("Resulting World\n=====");
for col_idx in 0..land.width {
for row_idx in 0..land.height {
print!("{}\t", land.map.get(&(col_idx, row_idx)).unwrap_or(&0u16));
for y in (0..land.height).rev() {
for x in 0..land.width {
print!("{}\t", land.map.get(&(x, y)).unwrap_or(&0u16));
}
println!();
}

View File

@ -1,4 +1,4 @@
use std::collections::VecDeque;
use std::collections::{VecDeque, BTreeMap};
use crate::state::{Land, Rule, Tree};
fn extract_forestry_format(instruction: &u16) -> (u16, i16, i16) {
@ -25,17 +25,22 @@ fn extract_forestry_format(instruction: &u16) -> (u16, i16, i16) {
);
}
pub fn op_sow(instruction: &u16, tree: &mut Tree) {
pub fn op_sow(instruction: &u16, tree: &mut Tree, land: &Land) {
let (stack_code, relative_x, relative_y) = extract_forestry_format(instruction);
let value = tree.pull_by_stack_code(stack_code);
tree.sow(relative_x, relative_y, value);
tree.sow(land, relative_x, relative_y, value);
}
pub fn op_reap(instruction: &u16, tree: &mut Tree) {
pub fn op_reap(instruction: &u16, tree: &mut Tree, land: &Land) {
let (stack_code, relative_x, relative_y) = extract_forestry_format(instruction);
let value = tree.reap(relative_x, relative_y);
let coordinates = land.derelativize(tree.position, relative_x, relative_y);
if !tree.buffer.contains_key(&coordinates) {
tree.buffer.insert(coordinates, land.get(&coordinates));
}
let value = tree.reap(land, relative_x, relative_y);
if stack_code == 0 {
tree.push_root(value);
@ -66,7 +71,7 @@ pub fn op_plant(instruction: &u16, tree: &mut Tree, next_tree_id: &mut u16, tree
asleep: false,
resume_from: 0,
position: land.derelativize(tree.position, relative_x, relative_y),
span: [[0u16; 8]; 8], // TODO: should take from current state of land!
buffer: BTreeMap::new(),
});
*next_tree_id += 1;

View File

@ -12,7 +12,7 @@ macro_rules! define_binary_op {
let $left = tree.pull_by_stack_code((stack_code & 0b100) >> 2);
let $right = tree.pull_by_stack_code((stack_code & 0b010) >> 1);
let $left = if !use_literal { $left } else { literal };
let $right = if !use_literal { $left } else { literal };
if is_signed {
let $left = $left as i16;

View File

@ -1,5 +1,4 @@
use std::collections::HashMap;
use std::collections::VecDeque;
use std::collections::{BTreeMap, HashMap, VecDeque};
pub struct Semaphore {
queue: VecDeque<u16>, // Positions -> tree ID
@ -52,20 +51,16 @@ impl Land {
// because land itself cannot exceed integer limits in size and
// so that check's implicitly handled with the modulo operator.
// Underflows are a bit trickier to catch, though
let absolute_x = if relative_x < 0 && position.0 - relative_x.unsigned_abs() > position.0 {
let absolute_x = if relative_x < 0 && position.0.checked_sub(relative_x.unsigned_abs()).is_none() {
(self.width - (relative_x.unsigned_abs() - position.0)) % self.width
} else if relative_x < 0 {
(position.0 - relative_x.unsigned_abs()) % self.height
} else {
(position.0 + relative_x.unsigned_abs()) % self.height
(((position.0 as i16) + relative_x) % self.width as i16) as u16
};
let absolute_y = if relative_y < 0 && position.1 - relative_y.unsigned_abs() > position.1 {
let absolute_y = if relative_y < 0 && position.1.checked_sub(relative_y.unsigned_abs()).is_none() {
(self.height - (relative_y.unsigned_abs() - position.1)) % self.height
} else if relative_x < 0 {
(position.1 - relative_y.unsigned_abs()) % self.height
} else {
(position.1 + relative_y.unsigned_abs()) % self.height
(((position.1 as i16) + relative_y) % self.height as i16) as u16
};
return (absolute_x, absolute_y);
@ -91,6 +86,10 @@ impl Land {
pub fn reap(&mut self, position: &(u16, u16)) -> u16 {
self.map.insert((position.0 % self.width, position.1 % self.height), 0).unwrap_or(0)
}
pub fn get(&self, position: &(u16, u16)) -> u16 {
*self.map.get(&(position.0 % self.width, position.1 % self.height)).unwrap_or(&0)
}
}
pub type Rule = VecDeque<u16>;
@ -105,11 +104,7 @@ pub struct Tree {
pub resume_from: u16,
pub stack_size: usize,
pub position: (u16, u16),
pub span: [[u16; 8]; 8],
}
fn derelativize_span_coordinates(x: i16, y: i16) -> (usize, usize) {
return ((x + 4) as usize, (y + 4) as usize);
pub buffer: BTreeMap<(u16, u16), u16>,
}
impl Tree {
@ -132,14 +127,14 @@ impl Tree {
pub fn pull_root(&mut self) -> u16 {
match self.root.pop() {
Some(v) => v,
None => panic!("Cannot pop from empty root stack in {}", self.rule_id)
None => panic!("Cannot pop from empty root stack in rule #{}", self.rule_id)
}
}
pub fn pull_trunk(&mut self) -> u16 {
match self.root.pop() {
match self.trunk.pop() {
Some(v) => v,
None => panic!("Cannot pop from empty trunk stack in {}", self.rule_id)
None => panic!("Cannot pop from empty trunk stack in rule #{}", self.rule_id)
}
}
@ -159,15 +154,13 @@ impl Tree {
}
}
pub fn sow(&mut self, relative_x: i16, relative_y: i16, value: u16) {
let (x, y) = derelativize_span_coordinates(relative_x, relative_y);
self.span[x][y] = value;
pub fn sow(&mut self, land: &Land, relative_x: i16, relative_y: i16, value: u16) {
let coordinates = land.derelativize(self.position, relative_x, relative_y);
self.buffer.insert(coordinates, value);
}
pub fn reap(&mut self, relative_x: i16, relative_y: i16) -> u16 {
let (x, y) = derelativize_span_coordinates(relative_x, relative_y);
let value = self.span[x][y];
self.sow(relative_x, relative_y, 0);
return value;
pub fn reap(&mut self, land: &Land, relative_x: i16, relative_y: i16) -> u16 {
let coordinates = land.derelativize(self.position, relative_x, relative_y);
return self.buffer.insert(coordinates, 0u16).unwrap_or(0u16);
}
}