added support for indirect reference
This commit is contained in:
parent
664d89a436
commit
d98334244e
31
ISA.md
31
ISA.md
|
@ -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.
|
||||
|
|
|
@ -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(());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
|
|
12
src/index.ts
12
src/index.ts
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue