added support for indirect reference

This commit is contained in:
Raphael Jacobs 2022-11-02 11:00:06 +01:00
parent 664d89a436
commit d98334244e
5 changed files with 151 additions and 49 deletions

31
ISA.md
View File

@ -15,29 +15,36 @@ Jump operations are zero-indexed. Meaning, JUMP 0 will jump to the first instruc
## Instructions (and syntax)
#### `READ <register_number>`
Reads a single number from the input queue, and stores the said number in register number `<register_number>`.
All instructions that take a register as argument can also take a indirect reference as argument:
This means that instred of writing `READ n` to read register `n`, if the number `n` is stored in a register R we can write `READ *R`. This will read the value `n` of register `R`, and perform `READ n`.
#### `WRITE <register_number>`
Writes the number stored in register number `<register_number>` in the output queue.
#### `READ <register_number | *register_ref>`
Reads a single number from the input queue, and stores the said number in register number `<register_number>`, or in the register referenced by the register number `<register_ref>`
#### `LOAD <register_number>`
Writes the number stored in register number `<register_number>` in the accumulator (id est, register 0).
#### `WRITE <register_number | *register_ref>`
Writes the number stored in register number `<register_number>` or in the register referenced by the register number `<register_ref>` in the output queue.
#### `LOAD <register_number | *register_ref>`
Writes the number stored in register number `<register_number>` or in the register referenced by the register number `<register_ref>` in the accumulator (id est, register 0).
#### `LOAD =<constant>` (_load immediate_)
Writes `<constant>` directly in the accumulator.
#### `STORE <register_number>`
Writes the number currently in the accumulator in the register number `<register_number>`.
#### `STORE <register_number | *register_ref>`
Writes the number currently in the accumulator in the register number `<register_number>` or in the register referenced by the register number `<register_ref>`.
#### `ADD <register_number>`
Adds to the accumulator the number stored in register number `<register_number>`.
#### `ADD <register_number | *register_ref>`
Adds to the accumulator the number stored in register number `<register_number>` or or in the register referenced by the register number `<register_ref>`.
.
#### `ADD =<constant>` (_add immediate_)
Adds `<constant>` directly to the accumulator.
#### `SUB <register_number>`
Subtracts from the accumulator the number stored in register number `<register_number>`.
#### `SUB <register_number | *register_ref>`
Subtracts from the accumulator the number stored in register number `<register_number>` or in the register referenced by the register number `<register_ref>`.
.
#### `SUB =<constant>` (_sub immediate_)
Subtracts `<constant>` directly from the accumulator.

View File

@ -1,31 +1,44 @@
use std::collections::VecDeque;
use std::{collections::VecDeque};
/// The type we use to represent values stored in out CPU.
pub type Word = i32;
/// The type we use to adress registers.
pub type Register = usize;
// Operations on register can also support the dereference operator.
// Because of this, every instruction that takes a register has two
// variants: indirect and direct access
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegRef {
Direct(Register),
Indirect(Register),
}
/// Our whole instruction set. Consult ISA.md for an explaination.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Instruction {
READ(Register), // read n
WRITE(Register), // write n
LOAD(Register), // load =4
READ(RegRef), // read n
WRITE(RegRef), // write n
LOAD(RegRef), // load =4
LOADI(Word), // load 4 (load immediate)
STORE(Register), //
ADD(Register),
STORE(RegRef), //
ADD(RegRef),
ADDI(Word),
SUB(Register),
SUB(RegRef),
SUBI(Word),
JUMP(Word),
JZERO(Word),
JGTZ(Word),
JUMPR(Register),
JUMPR(RegRef),
HALT,
}
use Instruction::*;
type InstructionNumber = usize;
#[derive(Debug, Clone)]
@ -161,7 +174,7 @@ impl CPU {
/// Retrives the value of some register. If the register doesn't
/// exist, returns a BadRegister error.
fn readmemory(&mut self, addr: &Register) -> Result<Word, CpuError> {
fn readmemory(&self, addr: &Register) -> Result<Word, CpuError> {
if addr >= &self.regmemory.len() {
let n = self.readmemory(&self.pc.clone()).unwrap();
return Err(BadRegister(n as usize, *addr));
@ -181,6 +194,18 @@ impl CPU {
return Ok(());
}
fn deref(&self, addref: &RegRef) -> Result<Register, CpuError> {
match addref {
RegRef::Direct(r) => return Ok(*r),
RegRef::Indirect(addr) => {
let reg = self.readmemory(addr)?;
return Ok(reg as Register)
},
}
}
/// Executes a single statement.
pub fn execone(&mut self) -> Result<(), CpuError> {
let acc = &self.acc.clone(); // constant
@ -195,19 +220,22 @@ impl CPU {
// println!("{:?}, {}", *op, pc_val);
match *op {
READ(r) => {
READ(rgref) => {
let r = self.deref(&rgref)?;
self.acc_access_check(&r)?;
let k = self.getinput()?;
self.writememory(&r, k)?;
}
WRITE(r) => {
WRITE(rgref) => {
let r = self.deref(&rgref)?;
self.acc_access_check(&r)?;
let k = self.readmemory(&r)?;
self.writeoutput(&k);
}
LOAD(r) => {
LOAD(rgref) => {
let r = self.deref(&rgref)?;
self.acc_access_check(&r)?;
let k = self.readmemory(&r)?;
self.writememory(acc, k)?;
@ -217,13 +245,15 @@ impl CPU {
self.writememory(acc, w)?;
}
STORE(r) => {
STORE(rgref) => {
let r = self.deref(&rgref)?;
self.acc_access_check(&r)?;
let k = self.readmemory(acc)?;
self.writememory(&r, k)?;
}
ADD(r) => {
ADD(rgref) => {
let r = self.deref(&rgref)?;
self.acc_access_check(&r)?;
let k = &self.readmemory(&r)?;
let acc_current = self.readmemory(acc)?;
@ -235,7 +265,8 @@ impl CPU {
self.writememory(acc, acc_current + w)?;
}
SUB(r) => {
SUB(rgref) => {
let r = self.deref(&rgref)?;
self.acc_access_check(&r)?;
let k = &self.readmemory(&r)?;
let acc_current = self.readmemory(acc)?;
@ -272,8 +303,9 @@ impl CPU {
}
// NON DOCUMENTED!
JUMPR(w) => {
let u = self.readmemory(&w)?;
JUMPR(rgref) => {
let r = self.deref(&rgref)?;
let u = self.readmemory(&r)?;
self.jump_check(&u)?;
self.writememory(pc, u)?;
return Ok(());

View File

@ -2,9 +2,44 @@ use crate::parser::{parse_line, parse_program, ParseError};
use js_sys::{Int32Array, Array};
use wasm_bindgen::prelude::*;
use super::cpu::*;
use super::cpu::CpuError::*;
use Instruction::*;
use RegRef::*;
impl std::fmt::Display for RegRef {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
RegRef::Direct(k) => write!(f, "{}", k),
RegRef::Indirect(k) => write!(f, "*{}", k),
}
}
}
impl std::fmt::Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
READ(k) => write!(f, "READ {}", k),
WRITE(k) => write!(f, "WRITE {}", k),
LOAD(k) => write!(f, "LOAD {}", k),
LOADI(k) => write!(f, "LOAD ={}", k),
STORE(k) => write!(f, "STORE {}", k),
ADD(k) => write!(f, "ADD {}", k),
ADDI(k) => write!(f, "ADD ={}", k),
SUB(k) => write!(f, "SUB {}", k),
SUBI(k) => write!(f, "SUB ={}", k),
JUMP(k) => write!(f, "JUMP {}", k),
JZERO(k) => write!(f, "JZERO {}", k),
JGTZ(k) => write!(f, "JGTZ {}", k),
JUMPR(k) => write!(f, "JUMPR {}", k),
HALT => write!(f, "HALT"),
}
}
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = CPUEMU)]
@ -59,7 +94,7 @@ impl WrapCpu {
let (_, _, _, k) = self.cpu.exposestate();
let arr = Array::new_with_length(k.len() as u32);
for i in 0..arr.length() {
let s = JsValue::from_str(&format!("{:?}", k[i as usize]));
let s = JsValue::from_str(&format!("{}: {}", i, k[i as usize]));
arr.set(i, s);
}
return arr;

View File

@ -1,7 +1,8 @@
use crate::cpu::Instruction;
use crate::cpu::{Instruction, RegRef};
use regex::{Regex, RegexSet};
use std::collections::VecDeque;
use Instruction::*;
use RegRef::*;
// OOO WE PARSIN' WITH REGEX!!!
@ -81,19 +82,32 @@ pub fn parse_line(line: &str) -> Option<Instruction> {
// comping the example from https://docs.rs/regex/latest/regex/struct.RegexSet.html
// COULD BE BETTER!!!!!!!!!
// We have to parse differently in case of references..
let patternspairs = [
(FnWrap::new(|x| READ(x.parse().unwrap())), r"^READ\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| WRITE(x.parse().unwrap())), r"^WRITE\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| LOAD(x.parse().unwrap())), r"^LOAD\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| READ(Direct(x.parse().unwrap()))), r"^READ\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| READ(Indirect(x.parse().unwrap()))), r"^READ\s+\*(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| WRITE(Direct(x.parse().unwrap()))), r"^WRITE\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| WRITE(Indirect(x.parse().unwrap()))), r"^WRITE\s+\*(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| LOAD(Direct(x.parse().unwrap()))), r"^LOAD\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| LOAD(Indirect(x.parse().unwrap()))), r"^LOAD\s+\*(\d+)\s*(?:#.*)?$"),
(
FnWrap::new(|x| LOADI(x.parse().unwrap())),
r"^LOAD\s+=\s*([-+]?\d+)\s*(?:#.*)?$",
),
(FnWrap::new(|x| STORE(x.parse().unwrap())), r"^STORE\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| ADD(x.parse().unwrap())), r"^ADD\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| STORE(Direct(x.parse().unwrap()))), r"^STORE\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| STORE(Indirect(x.parse().unwrap()))), r"^STORE\s+\*(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| ADD(Direct(x.parse().unwrap()))), r"^ADD\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| ADD(Indirect(x.parse().unwrap()))), r"^ADD\s+\*(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| ADDI(x.parse().unwrap())), r"^ADD\s=\s*([-+]?\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| SUB(x.parse().unwrap())), r"^SUB\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| SUB(Direct(x.parse().unwrap()))), r"^SUB\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| SUB(Indirect(x.parse().unwrap()))), r"^SUB\s+\*(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| SUBI(x.parse().unwrap())), r"^SUB\s=\s*([-+]?\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| JUMP(x.parse().unwrap())), r"^JUMP\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| JZERO(x.parse().unwrap())), r"^JZERO\s+(\d+)\s*(?:#.*)?$"),
(FnWrap::new(|x| JGTZ(x.parse().unwrap())), r"^JGTZ\s+(\d+)\s*(?:#.*)?$"),
@ -131,20 +145,34 @@ pub fn parse_line(line: &str) -> Option<Instruction> {
// testing should go in /tests but i am LAZY!!
#[test]
fn single_line_test() {
assert_eq!(parse_line("ADD 3"), Some(ADD(3)));
assert_eq!(parse_line("READ 3"), Some(READ(Direct(3))));
assert_eq!(parse_line("READ *3"), Some(READ(Indirect(3))));
assert_eq!(parse_line("WRITE 3"), Some(WRITE(Direct(3))));
assert_eq!(parse_line("WRITE *3"), Some(WRITE(Indirect(3))));
assert_eq!(parse_line("LOAD 3"), Some(LOAD(Direct(3))));
assert_eq!(parse_line("LOAD *3"), Some(LOAD(Indirect(3))));
assert_eq!(parse_line("LOAD =3"), Some(LOADI(3)));
assert_eq!(parse_line("STORE 3"), Some(STORE(Direct(3))));
assert_eq!(parse_line("STORE *3"), Some(STORE(Indirect(3))));
assert_eq!(parse_line("ADD 3"), Some(ADD(Direct(3))));
assert_eq!(parse_line("ADD *3"), Some(ADD(Indirect(3))));
assert_eq!(parse_line("ADD =3"), Some(ADDI(3)));
assert_eq!(parse_line("SUB 3"), Some(SUB(Direct(3))));
assert_eq!(parse_line("SUB *3"), Some(SUB(Indirect(3))));
}
#[test]
fn program_test() {
let code = "# this is a comment. \nREAD 1 # this is another comment.\nLOAD 1\nJZERO 8\nSUB =-1\nREAD 1\nWRITE 1\nJUMP 3\nHALT";
let code = "# this is a comment. \nREAD 1 # this is another comment.\nLOAD 1\nJZERO 8\nSUB =-1\nREAD 1\nWRITE *1\nJUMP 3\nHALT";
let program = vec![
READ(1),
LOAD(1),
READ(Direct(1)),
LOAD(Direct(1)),
JZERO(8),
SUBI(-1),
READ(1),
WRITE(1),
READ(Direct(1)),
WRITE(Indirect(1)),
JUMP(3),
HALT,
];

View File

@ -28,15 +28,15 @@ function handleInput() {
function applyHighlights(text: string) {
let res = text
.replace(
/^(READ\s+\d+)(\s*(#.*)?)?$/gm,
/^(READ\s+\*?\d+)(\s*(#.*)?)?$/gm,
'<mark class="goodcode">$1</mark>$2'
)
.replace(
/^(WRITE\s+\d+)(\s*(#.*)?)?$/gm,
/^(WRITE\s+\*?\d+)(\s*(#.*)?)?$/gm,
'<mark class="goodcode">$1</mark>$2'
)
.replace(
/^(LOAD\s+\d+)(\s*(#.*)?)?$/gm,
/^(LOAD\s+\*?\d+)(\s*(#.*)?)?$/gm,
'<mark class="goodcode">$1</mark>$2'
)
.replace(
@ -44,11 +44,11 @@ function applyHighlights(text: string) {
'<mark class="goodcode">$1</mark>$2'
)
.replace(
/^(STORE\s+\d+)(\s*(#.*)?)?$/gm,
/^(STORE\s+\*?\d+)(\s*(#.*)?)?$/gm,
'<mark class="goodcode">$1</mark>$2'
)
.replace(
/^(ADD\s+\d+)(\s*(#.*)?)?$/gm,
/^(ADD\s+\*?\d+)(\s*(#.*)?)?$/gm,
'<mark class="goodcode">$1</mark>$2'
)
.replace(
@ -56,7 +56,7 @@ function applyHighlights(text: string) {
'<mark class="goodcode">$1</mark>$2'
)
.replace(
/^(SUB\s+\d+)(\s*(#.*)?)?$/gm,
/^(SUB\s+\*?\d+)(\s*(#.*)?)?$/gm,
'<mark class="goodcode">$1</mark>$2'
)
.replace(