use std::io;
pub struct Processor<'a> {
reg: [u16;32],
flags: [bool;2], //Flag A, then B
itr_toggle: bool, //Interrupt toggle
pub memory: &'a mut [u16], //Public since memory should be modifiable by the system.
pub disk: &'a mut [u16],
pub vram: &'a mut [u16],
stdin_buf: Vec<u16>,
}
pub fn new<'a>(memory: &'a mut [u16], disk: &'a mut [u16], vram: &'a mut [u16]) -> Processor<'a> {
Processor {
reg: [0;32],
flags: [false;2],
itr_toggle: false,
memory,
disk,
vram,
stdin_buf: Vec::new(),
}
}
impl <'a> Processor<'a> {
///Runs the processor through one instruction.
///Returns false for normal operation, and true to indicate that it should halt.
pub fn run(&mut self) -> bool {
let instruction: (u16,u16) = (self.memory[self.reg[31] as usize],self.memory[self.reg[31] as usize +1]);
let conditions: ((bool,bool),(bool,bool)) = (( //.0.x is activations, .1.x is conditions
(instruction.0 & 0x8000) != 0,
(instruction.0 & 0x4000) != 0),(
(instruction.0 & 0x2000) != 0,
(instruction.0 & 0x1000) != 0)
);
if (conditions.0.0 && (conditions.1.0 != self.flags[0])) || (conditions.0.1 && (conditions.1.1 != self.flags[1])) {
self.reg[31] += 2; //update the program counter at the end of instruction
//execution.
return false; //if the conditions are not met, perform no further calculations.
}
match instruction.0 & 0x0F00 {
0x0100 => {
//Arithmetic operations.
let toggles: (bool,bool,bool,bool) = ( //S,C,F,I, see ISA for meanings
(instruction.0 & 0x0080) != 0,
(instruction.0 & 0x0040) != 0,
(instruction.0 & 0x0020) != 0,
(instruction.0 & 0x0010) != 0,
);
let operands: (usize, usize, usize) = (
((instruction.1 & 0xF800) >> 11) as usize,
((instruction.1 & 0x07C0) >> 6) as usize,
((instruction.1 & 0x003E) >> 1) as usize,
);
let mut pc_overwrite: Option<u16> = None;
if operands.2 == 31 { //program counter is unwritable for non-control flow
//instructions, but the overflow checks should still occur,
//so hold onto the next value and replace it later.
pc_overwrite = Some(self.reg[31]);
}
match instruction.0 & 0x000F {
0x0000 => { //Addition
if toggles.1 { //If C=1, then extra logic is required to correctly identify
//carries
(self.reg[operands.2], self.flags[toggles.2 as usize]) = {
if toggles.0 { //If dealing with signed values, cast the register
//values before passing them to overflowing_add.
let (uncast_return, flag) = (self.reg[operands.0] as i16).overflowing_add(self.reg[operands.1] as i16);
(uncast_return as u16, flag)
}
else { //Otherwise just pass them straight to overflowing_add.
self.reg[operands.0].overflowing_add(self.reg[operands.1])
}
};
}
else {self.reg[operands.2] = self.reg[operands.0].wrapping_add(self.reg[operands.1])};
}
0x0001 => { //Subtraction
if toggles.1 {
(self.reg[operands.2], self.flags[toggles.2 as usize]) = {
if toggles.0 {
let (uncast_return, flag) = (self.reg[operands.0] as i16).overflowing_add(-(self.reg[operands.1] as i16));
(uncast_return as u16, flag)
}
else {
(self.reg[operands.0].wrapping_sub(self.reg[operands.1]), self.reg[operands.0] < self.reg[operands.1])
}
}
}
else {
self.reg[operands.2] = self.reg[operands.0].wrapping_sub(self.reg[operands.1]);
}
}
0x0002 => { //Multiplication
if toggles.1 {
(self.reg[operands.2], self.flags[toggles.2 as usize]) = {
if toggles.0 {
let (uncast_return, flag) = (self.reg[operands.0] as i16).overflowing_mul(self.reg[operands.1] as i16);
(uncast_return as u16, flag)
}
else {
self.reg[operands.0].overflowing_mul(self.reg[operands.1])
}
}
}
else {
self.reg[operands.2] = self.reg[operands.0].wrapping_mul(self.reg[operands.1]);
}
}
0x0003 => { //Division
if self.reg[operands.1] == 0 && toggles.1 {self.flags[toggles.2 as usize] = true}
else if self.reg[operands.1] != 0 {
if toggles.0 {
self.reg[operands.2] = ((self.reg[operands.0] as i16)/(self.reg[operands.1] as i16)) as u16;
}
else {
self.reg[operands.2] = self.reg[operands.0]/self.reg[operands.1];
}
}
}
_ => return true
}
if toggles.3 {self.reg[operands.2] = !self.reg[operands.2]};
if let Some(addr) = pc_overwrite {self.reg[31] = addr};
self.reg[31] += 2;
return false
}
0x0200 => { //Logical
let toggles: (bool,bool,bool,bool) = ( //Z,F,R,I, see ISA for meanings
(instruction.0 & 0x0080) != 0,
(instruction.0 & 0x0040) != 0,
(instruction.0 & 0x0020) != 0,
(instruction.0 & 0x0010) != 0,
);
let operands: (usize, usize, usize) = (
((instruction.1 & 0xF800) >> 11) as usize,
((instruction.1 & 0x07C0) >> 6) as usize,
((instruction.1 & 0x003E) >> 1) as usize,
);
let mut pc_overwrite: Option<u16> = None;
if operands.2 == 31 { //program counter is unwritable for non-control flow
//instructions, but the non-zero checks should still occur,
//so hold onto the next value and replace it later.
pc_overwrite = Some(self.reg[31]);
}
match instruction.0 & 0x000F {
0x0000 => {
self.reg[operands.2] = self.reg[operands.0] & self.reg[operands.1];
}
0x0001 => {
self.reg[operands.2] = self.reg[operands.0] | self.reg[operands.1];
}
0x0002 => {
self.reg[operands.2] = self.reg[operands.0] ^ self.reg[operands.1];
}
_ => return true
}
if toggles.3 {
self.reg[operands.2] = !self.reg[operands.2];
};
if toggles.0 {
self.flags[toggles.1 as usize] = (self.reg[operands.2] == 0) ^ toggles.2;
}
if let Some(addr) = pc_overwrite {
self.reg[31] = addr;
}
self.reg[31] += 2;
return false
}
0x0300 => { //Condition check
let toggles: (bool, bool) = ( //Flag specifier, Invert
(instruction.0 & 0x0080) != 0,
(instruction.0 & 0x0040) != 0
);
let operands: (usize, usize) = (
((instruction.1 & 0xF800) >> 11) as usize,
((instruction.1 & 0x07C0) >> 6) as usize,
);
match instruction.0 & 0x0030 {
0x0000 => {
self.flags[toggles.0 as usize] = (self.reg[operands.0] == self.reg[operands.1]) ^ toggles.1;
}
0x0010 => {
self.flags[toggles.0 as usize] = (self.reg[operands.0] < self.reg[operands.1]) ^ toggles.1;
}
0x0020 => {
self.flags[toggles.0 as usize] = (self.reg[operands.0] > self.reg[operands.1]) ^ toggles.1;
}
_ => return true
}
self.reg[31] += 2;
return false
}
0x0400 => { //Control flow
let toggles: (bool,bool,bool,bool,bool,bool) = ( //J,S,P,I,TT, see ISA for meanings
(instruction.0 & 0x0080) != 0,
(instruction.0 & 0x0040) != 0,
(instruction.0 & 0x0020) != 0,
(instruction.0 & 0x0010) != 0,
(instruction.0 & 0x0008) != 0,
(instruction.0 & 0x0004) != 0,
);
if self.itr_toggle && toggles.3 { //Software interrupt
if toggles.1 { //Push what would have been the next address to stack
self.memory[self.reg[30] as usize] = self.reg[31] + 2;
self.reg[30] -= 1;
}
self.reg[31] = 0; //Jump to zero
if toggles.4 { //Set the interrupt toggle
self.itr_toggle = toggles.5;
}
return false
}
let address = {
if toggles.2 { //Pop from the stack
self.reg[30] = self.reg[30].wrapping_add(1); //Operations influenced by
//code should be unchecked.
self.memory[self.reg[30] as usize]
}
else { //Use a register value
let operand: usize = ((instruction.1 & 0xF800) >> 11) as usize;
self.reg[operand]
}
};
if toggles.1 { //Push to stack
self.memory[self.reg[30] as usize] = self.reg[31] + 2;
self.reg[30] = self.reg[30].wrapping_sub(1);
}
if toggles.0 { //Relative jump
self.reg[31] = self.reg[31].wrapping_add(address);
}
else { //Static jump
self.reg[31] = address;
}
if toggles.4 { //Set the interrupt enable bit
self.itr_toggle = toggles.5;
}
return false
}
0x0500 => { //Memory Access
let rw_toggle: bool = (instruction.0 & 0x0080) != 0; //False for read, true for
//write
let access_type: u16 = (instruction.0 & 0x0070) >> 4;
let operands: (usize, usize) = (
((instruction.1 & 0xF800) >> 11) as usize,
((instruction.1 & 0x07C0) >> 6) as usize,
);
if rw_toggle {
match access_type {
0 => { //Stack
self.memory[self.reg[30] as usize] = self.reg[operands.0];
self.reg[30] -= 1;
}
1 => { //Main Memory
self.memory[self.reg[operands.1] as usize] = self.reg[operands.0];
}
2 => { //Disk
self.disk[self.reg[operands.1] as usize + ((self.reg[29] as usize) << 16)] = self.reg[operands.0];
}
3 => { //VRAM
self.vram[self.reg[operands.1] as usize] = self.reg[operands.0];
}
4 => { //Serial port
print!("{}", self.reg[operands.0] as u8 as char);
}
_ => return true
}
}
else {
if operands.0 == 31 { //Skip the operation if writing to program counter, as
//program counter is inaccessible for non-control flow
//instructions
self.reg[31] += 2;
return false
}
match access_type {
0 => { //Stack
self.reg[30] += 1;
self.reg[operands.0] = self.memory[self.reg[30] as usize];
}
1 => self.reg[operands.0] = self.memory[self.reg[operands.1] as usize], //Main memory
2 => self.reg[operands.0] = self.disk[self.reg[operands.1] as usize + ((self.reg[29] as usize) << 16)], //Disk
3 => self.reg[operands.0] = self.vram[self.reg[operands.1] as usize], //VRAM
4 => { //Serial port
if self.stdin_buf == Vec::new() { //Fill stdin_buf if empty
let mut buffer = String::new();
io::stdin().read_line(&mut buffer).expect("STDIN read failure");
for byte in buffer.as_bytes().iter().rev() { //Reversed so values
//can be simply popped
//from the vec
self.stdin_buf.push(*byte as u16);
}
}
self.reg[operands.0] = self.stdin_buf.pop().unwrap_or(0); //If stdin is
//empty, pass
//in 0
}
_ => return true
}
}
self.reg[31] += 2;
return false
}
0x0600 => {
let operand = ((instruction.0 & 0x00F8) >> 3) as usize;
if operand != 31 {
self.reg[operand] = instruction.1;
}
self.reg[31] += 2;
return false
}
_ => return true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn immediate() {
let mut memory = [0x0600, 45, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[0], 45);
}
#[test]
fn conditional() {
let mut memory = [0x0600, 17, 0xF600, 69, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[0], 17);
}
#[test]
fn add() {
let mut memory = [0x0600, 3, 0x0608, 4, 0x0100, 0x0044, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 7);
}
#[test]
fn add_overflow () {
let mut memory = [0x0600, u16::MAX, 0x0608, 5, 0x0140, 0x0044, 0xA618, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 4);
assert_eq!(processor.reg[3], 5);
}
#[test]
fn add_signed_overflow () {
let mut memory = [0x0600, i16::MAX as u16, 0x0608, 5, 0x01E0, 0x0044, 0x5618, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], i16::MIN as u16 + 4);
assert_eq!(processor.reg[3], 5);
}
#[test]
fn arithmetic_invert () {
let mut memory = [0x0600, 3, 0x0608, 4, 0x0110, 0x0044, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], !7);
}
#[test]
fn sub () {
let mut memory = [0x0600, 5, 0x0608, 3, 0x0101, 0x0044, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 2);
}
#[test]
fn underflow () {
let mut memory = [0x0600, 3, 0x0608, 5, 0x0141, 0x0044, 0xA618, 7, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], u16::MAX - 1);
assert_eq!(processor.reg[3], 7);
}
#[test]
fn signed_underflow () {
let mut memory = [0x0600, 3, 0x0608, 5, 0x01C1, 0x0044, 0x0600, i16::MIN as u16, 0x01E1, 0x0046, 0xD620, 7, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], -2i16 as u16);
assert_eq!(processor.reg[3], i16::MAX as u16 - 4);
assert_eq!(processor.reg[4], 7);
}
#[test]
fn mult () {
let mut memory = [0x0600, 3, 0x0608, 5, 0x0102, 0x0044, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 15);
}
#[test]
fn mult_overflow () {
let mut memory = [0x0600, 3, 0x0608, u16::MAX, 0x0142, 0x0044, 0xA618, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], u16::MAX - 2);
assert_eq!(processor.reg[3], 5);
}
#[test]
fn mult_signed_overflow () {
let mut memory = [0x0600, 2, 0x0608, i16::MAX as u16, 0x01C2, 0x0044, 0xA618, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], u16::MAX - 1);
assert_eq!(processor.reg[3], 5);
}
#[test]
fn div () {
let mut memory = [0x0600, 7, 0x0608, 3, 0x0103, 0x0044, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 2);
}
#[test]
fn signed_div () {
let mut memory = [0x0600, 13, 0x0608, -4i16 as u16, 0x0183, 0x0044, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], -3i16 as u16);
}
#[test]
fn div_by_0 () {
let mut memory = [0x0600, 5, 0x0143, 0x0044, 0xA618, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 0);
assert_eq!(processor.reg[3], 5);
}
#[test]
fn logic_and () {
let mut memory = [0x0600, 0b11010, 0x0608, 0b10110, 0x0200, 0x0044, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 0b10010);
}
#[test]
fn logic_or () {
let mut memory = [0x0600, 0b10010, 0x0608, 0b11000, 0x0201, 0x0044, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 0b11010);
}
#[test]
fn logic_xor () {
let mut memory = [0x0600, 0b01101, 0x0608, 0b01011, 0x0202, 0x0044, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 0b00110);
}
#[test]
fn logic_zero() {
let mut memory = [0x0600, 5, 0x02A0, 0x0000, 0x02C0, 0x1112, 0xF608, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[1], 5);
}
#[test]
fn logic_invert() {
let mut memory = [0x02B0, 0x0002, 0xA610, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[1], u16::MAX);
assert_eq!(processor.reg[2], 5);
}
#[test]
fn cond_eq() {
let mut memory = [0x0600, 3, 0x0608, 5, 0x0340, 0x0040, 0x0380, 0x0000, 0xF610, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 5);
}
#[test]
fn cond_lt() {
let mut memory = [0x0600, 3, 0x0608, 5, 0x0310, 0x0040, 0xA610, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 5);
}
#[test]
fn cond_lte() {
let mut memory = [0x0600, 3, 0x0608, 5, 0x0360, 0x0040, 0x03E0, 0x0000, 0xF610, 5, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 5);
}
#[test]
fn cf_static_jump() {
let mut memory = [0x0600, 6, 0x0400, 0x0000, 0x0600, 13, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[0], 6);
}
#[test]
fn cf_dyn_jump() {
let mut memory = [0x0600, 4, 0x0480, 0x0000, 0x0600, 13, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[0], 4);
}
#[test]
fn cf_push_pop() {
let mut memory = [0xA420, 0x0000, 0x0300, 0x0000, 0x06F0, 10, 0x0440, 0x0000, 0, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
let mut counter = 0;
loop {
if processor.run() {break};
counter += 1;
if counter > 20 {panic!()};
}
}
#[test]
fn cf_interrupt() {
let mut memory = [0xA420, 0x0000, 0x0300, 0x0000, 0x06F0, 14, 0x0600, 2, 0x049C, 0x0000, 0x0450, 0x0000, 0, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
let mut counter = 0;
loop {
if processor.run() {break};
counter += 1;
if counter > 30 {panic!()};
}
}
#[test]
fn mem_stack() {
let mut memory = [0x06F0, 17, 0x0600, 5, 0x0580, 0x0000, 0x0600, 3, 0x0580, 0x0000, 0x0500, 0x0800, 0x0500, 0x1000, 0, 0, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[1], 3);
assert_eq!(processor.reg[2], 5);
}
#[test]
fn mem_main() {
let mut memory = [0x0600, 5, 0x0608, 10, 0x0590, 0x0040, 0x0510, 0x1040, 0, 0, 0];
let mut disk = [0];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 5);
}
#[test]
fn mem_disk() {
let mut memory = [0x0600, 5, 0x0608, 3, 0x06E8, 1, 0x05A0, 0x0040, 0x0520, 0x1040, 0x06E8, 0, 0x0520, 0x1840, 0, 0];
let mut disk = [0;66000];
let mut vram = [0];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 5);
assert_eq!(processor.reg[3], 0);
}
#[test]
fn mem_vram() {
let mut memory = [0x0600, 5, 0x0608, 10, 0x05B0, 0x0040, 0x0530, 0x1040, 0, 0, 0];
let mut disk = [0];
let mut vram = [0;30];
let mut processor = new(&mut memory, &mut disk, &mut vram);
loop {
if processor.run() {break};
}
assert_eq!(processor.reg[2], 5);
}
}