diff --git a/src/bin/ponderosa-asm.rs b/src/bin/ponderosa-asm.rs index a334f43..8ae5866 100644 --- a/src/bin/ponderosa-asm.rs +++ b/src/bin/ponderosa-asm.rs @@ -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::().unwrap() } -fn stack_bit(token: &String) -> Result { +fn stack_bit(token: &str) -> Result { let c = token.as_bytes()[0] as char; match c { 'r' => Ok(0), @@ -31,29 +32,31 @@ fn stack_bit(token: &String) -> Result { } } -fn parse_unary_math_format<'a>(tokens: &Vec, opcode: u16, signed: bool) -> Result { - assert_argument_count!(tokens, 4); +// e.g. not r t +fn parse_unary_op(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result { + 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, opcode: u16, signed: bool) -> Result { +// e.g. add r r t +fn parse_binary_op(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result { 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, opcode: u16, signed: bool) -> Result { +fn parse_push(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result { assert_argument_count!(tokens, 3); - let dest = stack_bit(&tokens[1])?; + let dest = stack_bit(tokens[1])?; let signed_immediate = tokens[2].parse::() .map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?; @@ -66,7 +69,33 @@ fn parse_push(tokens: &Vec, opcode: u16, signed: bool) -> Result, opcode: u16) -> Result { + assert_argument_count!(tokens, 4); + + let stack = stack_bit(tokens[1])?; + let relative_x = tokens[2].parse::() + .map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?; + + let relative_y = tokens[3].parse::() + .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 and a u16, push two corresponding u8s +fn push_u16(vec: &mut Vec, 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::(), tokens[1].parse::(), tokens[2].parse::()); 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 = 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(()) } diff --git a/test.rosa b/test.rosa deleted file mode 100644 index 2fd1893..0000000 --- a/test.rosa +++ /dev/null @@ -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