use std::collections::HashMap;
use crate::{asm::msize, ast::*};
use thiserror::Error;
type LabelMap<'a> = HashMap<Label<'a>, usize>;
pub type Computer64<'a> = Computer<'a, 64_000, 256>;
macro_rules! expect {
($expression:expr, $status:expr) => {
match $expression {
Ok(value) => {
$status = Status::Ready;
Some(value)
}
Err(e) => {
$status = Status::Error(e);
None
}
}
};
}
fn raise_arg_error(status: &mut Status, opcode: &Opcode, args: &[Argument]) {
let args: &'static str = Box::leak(Box::new(format!("{:?}", args)));
*status = Status::Error(Error::ArgumentError {
opcode: *opcode,
args,
});
}
fn log<T: std::fmt::Debug>(program_counter: usize, value: T) {
println!("LOG @{} -> {:#?}", program_counter, value)
}
#[allow(clippy::enum_variant_names)]
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
#[error("invalid arguments `{args:?}` for opcode `{opcode:?}`")]
ArgumentError { opcode: Opcode, args: &'static str },
#[error("attempted push to full stack")]
StackOverflowError,
#[error("attempted read from empty stack")]
StackUnderflowError,
#[error("tried to read from empty input buffer")]
InputError,
#[error("attempted to use undefined label {0:?}")]
UndefinedLabelError(&'static str),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
Ready,
Error(Error),
Yield,
Finished,
}
impl Default for Status {
fn default() -> Self {
Self::Ready
}
}
impl Status {
pub fn is_stopped(&self) -> bool {
matches!(self, Self::Finished) || matches!(self, Self::Error(_))
}
pub fn is_ready(&self) -> bool {
matches!(self, Self::Ready)
}
pub fn is_yield(&self) -> bool {
matches!(self, Self::Yield)
}
pub fn is_finished(&self) -> bool {
matches!(self, Self::Finished)
}
pub fn is_error(&self) -> bool {
matches!(self, Self::Error(_))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Comparison {
Less,
Equal,
Greater,
}
impl Default for Comparison {
fn default() -> Self {
Self::Equal
}
}
impl Comparison {
fn is_less(&self) -> bool {
matches!(self, Self::Less)
}
fn is_not_equal(&self) -> bool {
!matches!(self, Self::Equal)
}
fn is_equal(&self) -> bool {
matches!(self, Self::Equal)
}
fn is_greater(&self) -> bool {
matches!(self, Self::Greater)
}
}
impl From<std::cmp::Ordering> for Comparison {
fn from(ord: std::cmp::Ordering) -> Self {
match ord {
std::cmp::Ordering::Less => Self::Less,
std::cmp::Ordering::Equal => Self::Equal,
std::cmp::Ordering::Greater => Self::Greater,
}
}
}
trait Read<const R: usize, const S: usize> {
fn read(&self, memory: &Memory<R, S>) -> msize;
}
trait Write<const R: usize, const S: usize>: Read<R, S> {
fn write(&self, memory: &mut Memory<R, S>, value: msize);
}
trait AsRead<const R: usize, const S: usize> {
fn as_read(&self) -> Option<&dyn Read<R, S>>;
}
impl<const R: usize, const S: usize> AsRead<R, S> for Option<&Argument<'_>> {
fn as_read(&self) -> Option<&dyn Read<R, S>> {
self.and_then(|inside| inside.as_read())
}
}
impl<const R: usize, const S: usize> AsRead<R, S> for Argument<'_> {
fn as_read(&self) -> Option<&dyn Read<R, S>> {
Some(match self {
Argument::Register(register) => register,
Argument::Address(address) => address,
Argument::IntLiteral(literal) => literal,
_ => return None,
})
}
}
trait AsWrite<const R: usize, const S: usize> {
fn as_write(&self) -> Option<&dyn Write<R, S>>;
}
impl<const R: usize, const S: usize> AsWrite<R, S> for Option<&Argument<'_>> {
fn as_write(&self) -> Option<&dyn Write<R, S>> {
self.and_then(|inside| inside.as_write())
}
}
impl<const R: usize, const S: usize> AsWrite<R, S> for Argument<'_> {
fn as_write(&self) -> Option<&dyn Write<R, S>> {
Some(match self {
Argument::Register(register) => register,
Argument::Address(address) => address,
_ => return None,
})
}
}
impl<const R: usize, const S: usize> Read<R, S> for IntLiteral {
fn read(&self, _: &Memory<R, S>) -> msize {
self.value()
}
}
impl<const R: usize, const S: usize> Read<R, S> for Register {
fn read(&self, memory: &Memory<R, S>) -> msize {
memory.registers[self.index]
}
}
impl<const R: usize, const S: usize> Read<R, S> for Address {
fn read(&self, memory: &Memory<R, S>) -> msize {
let address = match self {
Address::IntLiteral(literal) => literal.read(memory),
Address::Register(register) => register.read(memory),
};
memory.ram[address as usize]
}
}
impl<const R: usize, const S: usize> Write<R, S> for Register {
fn write(&self, memory: &mut Memory<R, S>, value: msize) {
memory.registers[self.index] = value;
}
}
impl<const R: usize, const S: usize> Write<R, S> for Address {
fn write(&self, memory: &mut Memory<R, S>, value: msize) {
let address = match self {
Address::IntLiteral(literal) => literal.read(memory),
Address::Register(register) => register.read(memory),
};
memory.ram[address as usize] = value;
}
}
pub struct Stack<const S: usize> {
content: [msize; S],
pointer: usize,
}
impl<const S: usize> Default for Stack<S> {
fn default() -> Self {
let content = [0; S];
Self {
content,
pointer: content.len(),
}
}
}
impl<const S: usize> Stack<S> {
pub fn push(&mut self, value: msize) -> Result<(), Error> {
if self.pointer > 0 {
self.pointer -= 1;
self.content[self.pointer] = value;
Ok(())
} else {
Err(Error::StackOverflowError)
}
}
pub fn pop(&mut self) -> Result<msize, Error> {
if self.pointer < self.content.len() {
let value = self.content[self.pointer];
self.pointer += 1;
Ok(value)
} else {
Err(Error::StackUnderflowError)
}
}
}
pub struct Memory<const R: usize, const S: usize> {
accumulator: msize,
registers: [msize; 16],
stack: Stack<S>,
ram: [msize; R],
comparitor: Comparison,
}
impl<const R: usize, const S: usize> Default for Memory<R, S> {
fn default() -> Self {
Self {
accumulator: Default::default(),
registers: Default::default(),
stack: Default::default(),
ram: [0; R],
comparitor: Default::default(),
}
}
}
#[derive(Default)]
pub struct BufStream<T> {
content: Vec<T>,
subscribers: Vec<Box<dyn Fn(&T)>>,
provider: Option<Box<dyn Fn() -> Option<T>>>,
}
impl<T: std::fmt::Debug> std::fmt::Debug for BufStream<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.content.fmt(f)
}
}
impl<T> BufStream<T> {
pub fn read(&mut self) -> Option<T> {
let mut value = (!self.content.is_empty()).then(|| self.content.remove(0));
if let Some(provider) = &self.provider {
value = value.or_else(provider);
}
value
}
pub fn read_all(&mut self) -> Vec<T> {
self.content.drain(..).collect()
}
pub fn write(&mut self, value: T) {
self.subscribers.iter().for_each(|f| f(&value));
self.content.push(value);
}
pub fn write_iter(&mut self, iter: impl IntoIterator<Item = T>) {
let mut new_content: Vec<T> = iter.into_iter().collect();
self.subscribers
.iter()
.for_each(|f| new_content.iter().for_each(|v| f(v)));
self.content.append(&mut new_content)
}
pub fn subscribe(&mut self, callback: impl Fn(&T) + 'static) {
self.subscribers.push(Box::new(callback))
}
pub fn provide(&mut self, provider: impl Fn() -> Option<T> + 'static) {
self.provider = Some(Box::new(provider));
}
}
#[derive(Default)]
pub struct Computer<'a, const R: usize, const S: usize> {
program_counter: usize,
labels: LabelMap<'a>,
statements: Vec<Statement<'a>>,
memory: Memory<R, S>,
status: Status,
output: BufStream<msize>,
input: BufStream<msize>,
}
impl<'a, const R: usize, const S: usize> Computer<'a, R, S> {
pub fn load_program(&mut self, program: File<'a>) {
self.statements = program.statements;
self.labels = self
.statements
.iter()
.enumerate()
.filter_map(|(idx, statement)| {
if let Statement::LabelDefinition(LabelDefinition { label }) = statement {
Some((*label, idx))
} else {
None
}
})
.collect();
self.program_counter = self
.labels
.get(&Label { value: ".start" })
.copied()
.unwrap_or_default();
}
pub fn with_program(mut self, program: File<'a>) -> Self {
self.load_program(program);
self
}
pub fn reset(&mut self) {
self.memory = Memory::default();
self.program_counter = self
.labels
.get(&Label { value: ".start" })
.copied()
.unwrap_or_default();
}
pub fn with_reset(mut self) -> Self {
self.reset();
self
}
pub fn status(&self) -> Status {
self.status
}
pub fn program_counter(&self) -> usize {
self.program_counter
}
pub fn execute(&mut self) -> Status {
loop {
let status = self.step();
if status.is_stopped() {
break status;
}
}
}
pub fn execute_until_yield(&mut self) -> Status {
while self.step().is_ready() {}
self.status
}
pub fn read(&mut self) -> Option<msize> {
self.output.read()
}
pub fn read_all(&mut self) -> Vec<msize> {
self.output.read_all()
}
pub fn write(&mut self, value: msize) {
self.input.write(value)
}
pub fn write_iter(&mut self, iter: impl IntoIterator<Item = msize>) {
self.input.write_iter(iter)
}
pub fn subscribe(&mut self, callback: impl Fn(&msize) + 'static) {
self.output.subscribe(callback)
}
pub fn provide(&mut self, provider: impl Fn() -> Option<msize> + 'static) {
self.input.provide(provider)
}
pub fn step(&mut self) -> Status {
if self.status.is_yield() {
self.status = Status::Ready;
} else if self.status.is_stopped() {
return self.status;
}
let statement = &self.statements[self.program_counter];
self.program_counter += 1;
match statement {
Statement::Instruction(Instruction { opcode, arguments }) => match opcode {
Opcode::MOV => {
if let (Some(dest), Some(src)) =
(arguments.get(0).as_write(), arguments.get(1).as_read())
{
let value = src.read(&self.memory);
dest.write(&mut self.memory, value);
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::LOG => {
for arg in arguments {
match arg {
Argument::Label(label) => log(self.program_counter, self.labels[label]),
Argument::IntLiteral(literal) => {
log(self.program_counter, literal.read(&self.memory))
}
Argument::Register(register) => {
log(self.program_counter, register.read(&self.memory))
}
Argument::Address(address) => {
log(self.program_counter, address.read(&self.memory))
}
}
}
}
Opcode::PSH => {
if let Some(src) = arguments.get(0).as_read() {
let value = src.read(&self.memory);
expect!(self.memory.stack.push(value), self.status);
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::POP => {
if let Some(dest) = arguments.get(0).as_write() {
if let Some(value) = expect!(self.memory.stack.pop(), self.status) {
dest.write(&mut self.memory, value);
}
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::OUT => {
if let Some(src) = arguments.get(0).as_read() {
let value = src.read(&self.memory);
self.output.write(value);
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::INP => {
if let Some(dest) = arguments.get(0).as_write() {
if let Some(value) = self.input.read() {
dest.write(&mut self.memory, value);
} else {
self.status = Status::Error(Error::InputError);
}
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::ADD => {
if let (Some(dest), Some(src)) =
(arguments.get(0).as_write(), arguments.get(1).as_read())
{
let value = dest.read(&self.memory) + src.read(&self.memory);
dest.write(&mut self.memory, value);
}
else if let Some(src) = arguments.get(0).as_read() {
self.memory.accumulator += src.read(&self.memory);
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::SUB => {
if let (Some(dest), Some(src)) =
(arguments.get(0).as_write(), arguments.get(1).as_read())
{
let value = dest.read(&self.memory) - src.read(&self.memory);
dest.write(&mut self.memory, value);
}
else if let Some(src) = arguments.get(0).as_read() {
self.memory.accumulator -= src.read(&self.memory);
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::MUL => {
if let (Some(dest), Some(src)) =
(arguments.get(0).as_write(), arguments.get(1).as_read())
{
let value = dest.read(&self.memory) * src.read(&self.memory);
dest.write(&mut self.memory, value);
}
else if let Some(src) = arguments.get(0).as_read() {
self.memory.accumulator *= src.read(&self.memory);
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::DIV => {
if let (Some(dest), Some(src)) =
(arguments.get(0).as_write(), arguments.get(1).as_read())
{
let value = dest.read(&self.memory) / src.read(&self.memory);
dest.write(&mut self.memory, value);
}
else if let Some(src) = arguments.get(0).as_read() {
self.memory.accumulator /= src.read(&self.memory);
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::MOD => {
if let (Some(dest), Some(src)) =
(arguments.get(0).as_write(), arguments.get(1).as_read())
{
let value = dest.read(&self.memory) % src.read(&self.memory);
dest.write(&mut self.memory, value);
}
else if let Some(src) = arguments.get(0).as_read() {
self.memory.accumulator %= src.read(&self.memory);
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::CMP => {
if let (Some(lhs), Some(rhs)) =
(arguments.get(0).as_read(), arguments.get(1).as_read())
{
self.memory.comparitor =
lhs.read(&self.memory).cmp(&rhs.read(&self.memory)).into();
} else {
raise_arg_error(&mut self.status, opcode, arguments)
}
}
Opcode::RUN => {
match &arguments[..] {
[Argument::Label(label)] => {
if expect!(
self.memory
.stack
.push((self.program_counter & 0xFFFF) as msize),
self.status
)
.is_some()
{
if expect!(
self.memory
.stack
.push((self.program_counter >> 16) as msize),
self.status
)
.is_some()
{
self.program_counter = self.labels[label];
}
}
}
_ => raise_arg_error(&mut self.status, opcode, arguments),
}
}
Opcode::RET => {
if let (Some(upper_bits), Some(lower_bits)) = (
expect!(self.memory.stack.pop(), self.status),
expect!(self.memory.stack.pop(), self.status),
) {
let program_counter =
(((upper_bits as u32) << 16) | lower_bits as u32) as usize;
self.program_counter = program_counter;
}
}
Opcode::YLD => {
self.status = Status::Yield;
}
Opcode::JMP => {
match &arguments[..] {
[Argument::Label(label)] => {
if let Some(&label_ptr) = self.labels.get(label) {
self.program_counter = label_ptr;
} else {
let label: &'static str =
Box::leak(Box::new(label.value.to_string()));
self.status = Status::Error(Error::UndefinedLabelError(label));
return self.status;
}
}
_ => raise_arg_error(&mut self.status, opcode, arguments),
}
}
Opcode::JLT => {
match &arguments[..] {
[Argument::Label(label)] => {
if self.memory.comparitor.is_less() {
if let Some(&label_ptr) = self.labels.get(label) {
self.program_counter = label_ptr;
} else {
let label: &'static str =
Box::leak(Box::new(label.value.to_string()));
self.status = Status::Error(Error::UndefinedLabelError(label));
return self.status;
}
}
}
_ => raise_arg_error(&mut self.status, opcode, arguments),
}
}
Opcode::JGT => {
match &arguments[..] {
[Argument::Label(label)] => {
if self.memory.comparitor.is_greater() {
if let Some(&label_ptr) = self.labels.get(label) {
self.program_counter = label_ptr;
} else {
let label: &'static str =
Box::leak(Box::new(label.value.to_string()));
self.status = Status::Error(Error::UndefinedLabelError(label));
return self.status;
}
}
}
_ => raise_arg_error(&mut self.status, opcode, arguments),
}
}
Opcode::JEQ => {
match &arguments[..] {
[Argument::Label(label)] => {
if self.memory.comparitor.is_equal() {
if let Some(&label_ptr) = self.labels.get(label) {
self.program_counter = label_ptr;
} else {
let label: &'static str =
Box::leak(Box::new(label.value.to_string()));
self.status = Status::Error(Error::UndefinedLabelError(label));
return self.status;
}
}
}
_ => raise_arg_error(&mut self.status, opcode, arguments),
}
}
Opcode::JNE => {
match &arguments[..] {
[Argument::Label(label)] => {
if self.memory.comparitor.is_not_equal() {
if let Some(&label_ptr) = self.labels.get(label) {
self.program_counter = label_ptr;
} else {
let label: &'static str =
Box::leak(Box::new(label.value.to_string()));
self.status = Status::Error(Error::UndefinedLabelError(label));
return self.status;
}
}
}
_ => raise_arg_error(&mut self.status, opcode, arguments),
}
}
Opcode::NOP => {}
Opcode::HLT => self.program_counter = self.statements.len(),
},
Statement::LabelDefinition(_) => {}
}
if self.program_counter >= self.statements.len() {
self.status = Status::Finished;
}
self.status
}
}