const STACK_SIZE: usize = 1024;
pub const MEMORY_SIZE: usize = 1<<20;
pub const SCREEN_WIDTH: usize = 512;
pub const SCREEN_HEIGHT: usize = 512;
pub const SCREEN_UPDATE_ADDRESS: usize = 0x7FFFF;
use minifb::Key;
#[derive(Debug)]
pub struct Stack {
buffer: [u16;STACK_SIZE],
index: usize
}
impl Stack {
fn push(&mut self, value: u16) {
self.buffer[self.index] = value;
self.index = (self.index + 1) % STACK_SIZE;
}
fn pop(&mut self) -> u16 {
self.index = (self.index + STACK_SIZE - 1) % STACK_SIZE;
self.buffer[self.index]
}
}
pub fn new_stack() -> Stack {
Stack{
buffer: [0u16;STACK_SIZE],
index: 0
}
}
pub struct Processor {
pub stack_a: Stack,
pub stack_b: Stack,
pub memory: [u16;MEMORY_SIZE],
pub carry: bool,
pub pc: usize
}
impl Processor {
pub fn run(&mut self) -> bool {
let instruction = self.memory[self.pc] & 0x3FF;
let (mut stack, off_stack) = {
if (instruction & 0x200) == 0 {
(&mut self.stack_a, &mut self.stack_b)
}
else {(&mut self.stack_b, &mut self.stack_a)}
};
let mut is_jump = false;
if instruction & 0x100 == 0 { //calculation
self.carry = calc(&mut stack, instruction, self.carry);
}
else if instruction & 0x80 == 0 { //jump
let push_pc = (instruction & 0x40) != 0;
let conditional = (instruction & 0x20) != 0;
if !conditional || self.carry {
let address = (stack.pop() as usize) | ((stack.pop() as usize) << 10);
is_jump = true;
if push_pc {
let a = self.pc + 1;
let upper = a & 0b11111111110000000000 >> 10;
let lower = a & 0b1111111111;
stack.push(upper as u16);
stack.push(lower as u16);
}
self.pc = address;
}
}
else {
match instruction & 0x7F {
0 => { //immediate value
self.pc += 1;
stack.push(self.memory[self.pc] & 0x3FF);
}
1 => { //push pc
let upper = self.pc & 0b11111111110000000000 >> 10;
let lower = self.pc & 0b1111111111;
stack.push(upper as u16);
stack.push(lower as u16);
}
2 => { //swap
let a = stack.pop();
let b = stack.pop();
stack.push(a);
stack.push(b);
}
3 => off_stack.push(stack.pop()), //move between stacks
4 => { //push from memory
let address = (stack.pop() as usize) | ((stack.pop() as usize) << 10);
stack.push(self.memory[address]);
}
5 => { //pop to memory
let address = (stack.pop() as usize) | ((stack.pop() as usize) << 10);
self.memory[address] = stack.pop();
}
6 => { //dup
let a = stack.pop();
stack.push(a);
stack.push(a);
}
7 => { //over
let a = stack.pop();
let b = stack.pop();
stack.push(b);
stack.push(a);
stack.push(b);
}
8 => self.carry = false, //clear/set carry
9 => self.carry = true,
10 => _ = stack.pop(), //drop value
0x7F => return false,
_ => ()
}
}
if !is_jump {self.pc += 1};
true
}
}
fn calc(stack: &mut Stack, instruction: u16, carry_in: bool) -> bool {
let operand_a = stack.pop();
let operand_b = stack.pop();
let mut carry = carry_in;
let mut result = 0;
for i in 0..10 {
let count = ((operand_a >> i) & 1) + ((operand_b >> i) & 1) + (carry as u16);
result += ((instruction >> (count+4)) & 1) << i;
carry = ((instruction >> count) & 1) != 0;
}
stack.push(result);
carry
}
pub fn mem_to_buf(memory: &[u16]) -> Vec<u32> {
let mut result = Vec::new();
for byte in &memory[(MEMORY_SIZE-SCREEN_WIDTH*SCREEN_HEIGHT)..MEMORY_SIZE] {
result.push((((byte & 0b111000000) as u32) << 15) | (((byte & 0b111000) as u32) << 10) | (((byte & 0b111) as u32) << 5)); //shifts 3 bits of each 10 bit value into the rgb channels of the buffer, from the last region of memory
}
result
}
pub fn keys_to_state(keys: Vec<Key>) -> Vec<u16> { //output is using only 10 bits each to match with
let mut result = 0u128; //rest of system
for key in keys { //see minifb docs for what number each key is
result &= 1 << (key as usize);
}
let mut output = Vec::new();
for i in 0..11 {
let slice = ((result>>(10*i))&0x3FF) as u16;
output.push(slice);
}
output
}