Compare commits
8 Commits
5db045990c
...
551b4468aa
Author | SHA1 | Date |
---|---|---|
Nat | 551b4468aa | |
Nat | cf1330bb21 | |
Nat | 621166e8c6 | |
Nat | ff6e95a33b | |
Nat | ba5a1b6256 | |
Nat | 89c9d273e2 | |
Nat | cb693c16e7 | |
Nat | ca66fbc895 |
4
SPEC.md
4
SPEC.md
|
@ -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`
|
||||
|
|
|
@ -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])),
|
||||
};
|
||||
|
|
|
@ -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!();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
45
src/state.rs
45
src/state.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue