Implement "boosted" Int-code computer
* Support for 64bit integer values and non-continguous memory regions * Add support for relative mode parameters * New opcode to adjust relative mode base value * Legacy 32bit integer API is preserved via simulate()
This commit is contained in:
parent
065d2d8192
commit
25cd57559e
|
@ -4,4 +4,4 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icc"
|
name = "icc"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "icc"
|
name = "icc"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
460
icc/src/lib.rs
460
icc/src/lib.rs
|
@ -1,7 +1,9 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
Running,
|
Running,
|
||||||
Finished,
|
Finished,
|
||||||
|
@ -13,174 +15,269 @@ pub struct Result {
|
||||||
pub instruction: usize,
|
pub instruction: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simulate(v: &mut Vec<i32>, input: &mut VecDeque<i32>, output: &mut Vec<i32>, start: usize) -> Result {
|
pub struct Computer {
|
||||||
let mut pos = start;
|
memory: HashMap<usize, i64>,
|
||||||
loop {
|
position: usize,
|
||||||
let mut code = v[pos];
|
relative_base: i64,
|
||||||
|
input: VecDeque<i64>,
|
||||||
|
output: Vec<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum Operation {
|
||||||
|
Add,
|
||||||
|
Mul,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
JumpIfTrue,
|
||||||
|
JumpIfFalse,
|
||||||
|
LessThan,
|
||||||
|
Equals,
|
||||||
|
AdjustRelativeBase,
|
||||||
|
End, // 99
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum ParameterMode {
|
||||||
|
Position, // 0
|
||||||
|
Immediate, // 1
|
||||||
|
Relative, // 2
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParameterMode {
|
||||||
|
pub fn from_int(i: i64) -> ParameterMode {
|
||||||
|
if i == 0 {
|
||||||
|
ParameterMode::Position
|
||||||
|
}
|
||||||
|
else if i == 1 {
|
||||||
|
ParameterMode::Immediate
|
||||||
|
}
|
||||||
|
else if i == 2 {
|
||||||
|
ParameterMode::Relative
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic!("Unknown parameter mode integer value: {}", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Computer {
|
||||||
|
pub fn initialize(program: Vec<i64>, input: Vec<i64>) -> Self {
|
||||||
|
let mut computer = Computer {
|
||||||
|
memory: HashMap::new(),
|
||||||
|
position: 0,
|
||||||
|
relative_base: 0,
|
||||||
|
input: VecDeque::from(input),
|
||||||
|
output: Vec::new(),
|
||||||
|
};
|
||||||
|
for i in 0..program.len() {
|
||||||
|
computer.mem_put(i, program[i]);
|
||||||
|
}
|
||||||
|
return computer;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_put(&mut self, address: usize, value: i64) {
|
||||||
|
self.memory.insert(address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mem_get(&mut self, address: usize) -> i64 {
|
||||||
|
let value = self.memory.get(&address);
|
||||||
|
if value.is_none() {
|
||||||
|
self.mem_put(address, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return *value.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) -> Status {
|
||||||
|
loop {
|
||||||
|
let (op, p1_mode, p2_mode, p3_mode) = Self::decode_instruction(
|
||||||
|
self.mem_get(self.position)
|
||||||
|
);
|
||||||
|
match op {
|
||||||
|
Operation::Add => {
|
||||||
|
let d1 = self.fetch(self.position + 1, p1_mode);
|
||||||
|
let d2 = self.fetch(self.position + 2, p2_mode);
|
||||||
|
// The original implementation didn't check p3_mode for the Add
|
||||||
|
// operation.
|
||||||
|
let dest = self.mem_get(self.position + 3) as usize;
|
||||||
|
self.mem_put(dest, d1 + d2);
|
||||||
|
self.position += 4;
|
||||||
|
},
|
||||||
|
Operation::Mul => {
|
||||||
|
let d1 = self.fetch(self.position + 1, p1_mode);
|
||||||
|
let d2 = self.fetch(self.position + 2, p2_mode);
|
||||||
|
// The original implementation didn't check p3_mode for the Mul
|
||||||
|
// operation
|
||||||
|
let dest = self.mem_get(self.position + 3) as usize;
|
||||||
|
self.mem_put(dest, d1 * d2);
|
||||||
|
self.position += 4;
|
||||||
|
},
|
||||||
|
Operation::Input => {
|
||||||
|
if self.input.len() == 0 {
|
||||||
|
return Status::WaitingForInput;
|
||||||
|
}
|
||||||
|
let dest = self.mem_get(self.position + 1) as usize;
|
||||||
|
let input = self.input.pop_front().expect("Input queue empty... weird");
|
||||||
|
self.mem_put(dest, input);
|
||||||
|
println!("Input: {}", input);
|
||||||
|
self.position += 2;
|
||||||
|
},
|
||||||
|
Operation::Output => {
|
||||||
|
let value = self.fetch(self.position + 1, p1_mode);
|
||||||
|
println!("Output: {}", value);
|
||||||
|
self.output.push(value);
|
||||||
|
self.position += 2;
|
||||||
|
},
|
||||||
|
Operation::JumpIfTrue => {
|
||||||
|
let d1 = self.fetch(self.position + 1, p1_mode);
|
||||||
|
let d2 = self.fetch(self.position + 2, p2_mode);
|
||||||
|
if d1 != 0 {
|
||||||
|
self.position = d2 as usize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.position += 3;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Operation::JumpIfFalse => {
|
||||||
|
let d1 = self.fetch(self.position + 1, p1_mode);
|
||||||
|
let d2 = self.fetch(self.position + 2, p2_mode);
|
||||||
|
if d1 == 0 {
|
||||||
|
self.position = d2 as usize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.position += 3;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Operation::LessThan => {
|
||||||
|
let d1 = self.fetch(self.position + 1, p1_mode);
|
||||||
|
let d2 = self.fetch(self.position + 2, p2_mode);
|
||||||
|
// The original implementation didn't check p3_mode
|
||||||
|
let dest = self.mem_get(self.position + 3) as usize;
|
||||||
|
self.mem_put(dest, if d1 < d2 { 1 } else { 0 });
|
||||||
|
self.position += 4;
|
||||||
|
},
|
||||||
|
Operation::Equals => {
|
||||||
|
let d1 = self.fetch(self.position + 1, p1_mode);
|
||||||
|
let d2 = self.fetch(self.position + 2, p2_mode);
|
||||||
|
// The original implementation didn't check p3_mode
|
||||||
|
let dest = self.mem_get(self.position + 3) as usize;
|
||||||
|
self.mem_put(dest, if d1 == d2 { 1 } else { 0 });
|
||||||
|
self.position += 4;
|
||||||
|
},
|
||||||
|
Operation::AdjustRelativeBase => {
|
||||||
|
self.relative_base += self.mem_get(self.position + 1);
|
||||||
|
self.position += 2;
|
||||||
|
},
|
||||||
|
Operation::End => {
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
other => {
|
||||||
|
panic!("'{:?}' not implemented", other);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Status::Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch(&mut self, pos: usize, mode: ParameterMode) -> i64 {
|
||||||
|
let addr = self.absolute_address(pos, mode);
|
||||||
|
return self.mem_get(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn absolute_address(&mut self, pos: usize, mode: ParameterMode) -> usize {
|
||||||
|
match mode {
|
||||||
|
ParameterMode::Position => {
|
||||||
|
self.mem_get(
|
||||||
|
pos
|
||||||
|
) as usize
|
||||||
|
},
|
||||||
|
ParameterMode::Immediate => {
|
||||||
|
pos
|
||||||
|
},
|
||||||
|
ParameterMode::Relative => {
|
||||||
|
let p = self.mem_get(pos) as i64 + self.relative_base;
|
||||||
|
if p < 0 {
|
||||||
|
panic!("Attempting to resolve illegale address: {}", p);
|
||||||
|
}
|
||||||
|
return p as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_instruction(code: i64) -> (Operation, ParameterMode, ParameterMode, ParameterMode) {
|
||||||
// Opcodes are ABCDE, where the op is DE
|
// Opcodes are ABCDE, where the op is DE
|
||||||
// the modes for params 1, 2, and 3 as C, B, A
|
// the modes for params 1, 2, and 3 as C, B, A
|
||||||
// respectively.
|
// respectively.
|
||||||
let op = code % 100;
|
let mut c = code;
|
||||||
//println!("pos {}, code {}, op {}", pos, code, op);
|
let op_int = code % 100;
|
||||||
let param_3_mode = code / 10000;
|
let op = if op_int == 1 {
|
||||||
if param_3_mode == 1 {
|
Operation::Add
|
||||||
code -= 10000;
|
} else if op_int == 2 {
|
||||||
}
|
Operation::Mul
|
||||||
let param_2_mode = code / 1000;
|
} else if op_int == 3 {
|
||||||
if param_2_mode == 1 {
|
Operation::Input
|
||||||
code -= 1000;
|
} else if op_int == 4 {
|
||||||
}
|
Operation::Output
|
||||||
let param_1_mode = code / 100;
|
} else if op_int == 5 {
|
||||||
if param_1_mode == 1 {
|
Operation::JumpIfTrue
|
||||||
code -= 100;
|
} else if op_int == 6 {
|
||||||
}
|
Operation::JumpIfFalse
|
||||||
if op == 1 {
|
} else if op_int == 7 {
|
||||||
// Add d1 + d2 -> dest
|
Operation::LessThan
|
||||||
let d1: i32 = if param_1_mode == 1 {
|
} else if op_int == 8 {
|
||||||
v[pos+1]
|
Operation::Equals
|
||||||
} else {
|
} else if op_int == 9 {
|
||||||
v[v[pos+1] as usize]
|
Operation::AdjustRelativeBase
|
||||||
};
|
}else if op_int == 99 {
|
||||||
let d2: i32 = if param_2_mode == 1 {
|
Operation::End
|
||||||
v[pos+2]
|
} else {
|
||||||
} else {
|
panic!("Unknown opcode: {}", op_int);
|
||||||
v[v[pos+2] as usize]
|
};
|
||||||
};
|
|
||||||
let dest = v[pos+3] as usize;
|
let param_3_mode_int = c / 10000;
|
||||||
v[dest] = d1 + d2;
|
c -= param_3_mode_int * 10000;
|
||||||
pos += 4;
|
let param_2_mode_int = c / 1000;
|
||||||
continue;
|
c -= param_2_mode_int * 1000;
|
||||||
}
|
let param_1_mode_int = c / 100;
|
||||||
else if op == 2 {
|
return (
|
||||||
// Mult d1 * d2 -> dest
|
op,
|
||||||
let d1: i32 = if param_1_mode == 1 {
|
ParameterMode::from_int(param_1_mode_int),
|
||||||
v[pos+1]
|
ParameterMode::from_int(param_2_mode_int),
|
||||||
} else {
|
ParameterMode::from_int(param_3_mode_int),
|
||||||
v[v[pos+1] as usize]
|
);
|
||||||
};
|
}
|
||||||
let d2: i32 = if param_2_mode == 1 {
|
}
|
||||||
v[pos+2]
|
|
||||||
}
|
pub fn simulate(v: &mut Vec<i32>, input: &mut VecDeque<i32>, output: &mut Vec<i32>, start: usize) -> Result {
|
||||||
else {
|
let mut program: Vec<i64> = Vec::new();
|
||||||
v[v[pos+2] as usize]
|
for i in 0..v.len() {
|
||||||
};
|
program.push(i64::from(v[i]));
|
||||||
let dest = v[pos+3] as usize;
|
}
|
||||||
v[dest] = d1 * d2;
|
let mut ip: VecDeque<i64> = VecDeque::new();
|
||||||
pos += 4;
|
for i in 0..input.len() {
|
||||||
continue;
|
ip.push_front(i64::from(input[i]));
|
||||||
}
|
}
|
||||||
else if op == 3 {
|
let mut computer = Computer::initialize(program, vec![]);
|
||||||
// Input
|
computer.input = ip;
|
||||||
let dest = v[pos+1] as usize;
|
computer.position = start;
|
||||||
v[dest] = if input.len() == 0 {
|
let r = computer.run();
|
||||||
return Result {
|
for i in 0..computer.output.len() {
|
||||||
status: Status::WaitingForInput,
|
if computer.output[i] <= i64::from(i32::MAX) && computer.output[i] >= i64::from(i32::MIN) {
|
||||||
instruction: pos
|
output.push(computer.output[i] as i32);
|
||||||
};
|
|
||||||
} else {
|
|
||||||
input.pop_front().expect("Input stack empty... weird")
|
|
||||||
};
|
|
||||||
println!("Input: {}", v[dest]);
|
|
||||||
pos += 2;
|
|
||||||
}
|
|
||||||
else if op == 4 {
|
|
||||||
// Output
|
|
||||||
let d = if param_1_mode == 1 {
|
|
||||||
v[pos+1]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
v[v[pos+1] as usize]
|
|
||||||
};
|
|
||||||
println!("Output: {}", d);
|
|
||||||
output.push(d);
|
|
||||||
pos += 2;
|
|
||||||
}
|
|
||||||
else if op == 5 {
|
|
||||||
// Jump if true
|
|
||||||
let d1 = if param_1_mode == 1 {
|
|
||||||
v[pos+1] as usize
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
v[v[pos+1] as usize] as usize
|
|
||||||
};
|
|
||||||
let d2 = if param_2_mode == 1 {
|
|
||||||
v[pos+2] as usize
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
v[v[pos+2] as usize] as usize
|
|
||||||
};
|
|
||||||
if d1 != 0 {
|
|
||||||
pos = d2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pos += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if op == 6 {
|
|
||||||
// Jump if false
|
|
||||||
let d1 = if param_1_mode == 1 {
|
|
||||||
v[pos+1] as usize
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
v[v[pos+1] as usize] as usize
|
|
||||||
};
|
|
||||||
let d2 = if param_2_mode == 1 {
|
|
||||||
v[pos+2] as usize
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
v[v[pos+2] as usize] as usize
|
|
||||||
};
|
|
||||||
if d1 == 0 {
|
|
||||||
pos = d2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pos += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if op == 7 {
|
|
||||||
// Less than
|
|
||||||
let d1: i32 = if param_1_mode == 1 {
|
|
||||||
v[pos+1]
|
|
||||||
} else {
|
|
||||||
v[v[pos+1] as usize]
|
|
||||||
};
|
|
||||||
let d2: i32 = if param_2_mode == 1 {
|
|
||||||
v[pos+2]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
v[v[pos+2] as usize]
|
|
||||||
};
|
|
||||||
let dest = v[pos+3] as usize;
|
|
||||||
v[dest] = if d1 < d2 { 1 } else { 0 };
|
|
||||||
pos += 4;
|
|
||||||
}
|
|
||||||
else if op == 8 {
|
|
||||||
// equals
|
|
||||||
let d1: i32 = if param_1_mode == 1 {
|
|
||||||
v[pos+1]
|
|
||||||
} else {
|
|
||||||
v[v[pos+1] as usize]
|
|
||||||
};
|
|
||||||
let d2: i32 = if param_2_mode == 1 {
|
|
||||||
v[pos+2]
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
v[v[pos+2] as usize]
|
|
||||||
};
|
|
||||||
let dest = v[pos+3] as usize;
|
|
||||||
v[dest] = if d1 == d2 { 1 } else { 0 };
|
|
||||||
pos += 4;
|
|
||||||
}
|
|
||||||
else if op == 99 {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
panic!("Unknown opcode: {}", code);
|
panic!("Output value '{}' out of range for legacy i32 int-code computer", computer.output[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i in 0..v.len() {
|
||||||
|
v[i] = computer.mem_get(i) as i32;
|
||||||
|
}
|
||||||
return Result {
|
return Result {
|
||||||
status: Status::Finished,
|
status: r,
|
||||||
instruction: 0
|
instruction: computer.position,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,4 +442,47 @@ mod tests {
|
||||||
assert_eq!(result.status, Status::WaitingForInput);
|
assert_eq!(result.status, Status::WaitingForInput);
|
||||||
assert_eq!(result.instruction, 6);
|
assert_eq!(result.instruction, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn relative_base_adjust() {
|
||||||
|
let mut c = Computer::initialize(vec![109, 1, 99], vec![]);
|
||||||
|
c.run();
|
||||||
|
assert_eq!(c.relative_base, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn negative_memory_access() {
|
||||||
|
let mut c = Computer::initialize(vec![109, -5000, 204, 0, 99], vec![]);
|
||||||
|
c.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn relative_example_1() {
|
||||||
|
// The program produces an output of itself
|
||||||
|
let program = vec![109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99];
|
||||||
|
let mut c = Computer::initialize(program.clone(), vec![]);
|
||||||
|
c.run();
|
||||||
|
for i in 0..program.len() {
|
||||||
|
assert_eq!(program[i], c.output[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn relative_example_2() {
|
||||||
|
// Outputs a 16 digit numbers
|
||||||
|
let program = vec![1102,34915192,34915192,7,4,7,99,0];
|
||||||
|
let mut c = Computer::initialize(program, vec![]);
|
||||||
|
c.run();
|
||||||
|
assert_eq!(c.output[0].to_string().len(), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn relative_example_3() {
|
||||||
|
// Outputs 1125899906842624
|
||||||
|
let program = vec![104,1125899906842624,99];
|
||||||
|
let mut c = Computer::initialize(program, vec![]);
|
||||||
|
c.run();
|
||||||
|
assert_eq!(c.output[0], 1125899906842624);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue