Skip to content

Commit

Permalink
add: Jump instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
supragya committed Jul 8, 2024
1 parent c5c1d56 commit 57d14ce
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 24 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ following instructions are chosen to be implemented:
- [ ] **DIV**: `DIV r1 r2` Divides registers `r1` and `r2` such that `r1 = r1 / r2`.
- [ ] **SHL**: `BSL r1 r2` BitShifts `r1` by `r2` to the left.
- [ ] **SHR**: `SHR r1 r2` BitShift analog towards the right.
- [ ] **JZ**: `JZ r1 0x10` Jump to `0x10` if value in `r1` is zero.
- [ ] **JNZ**: `JNZ r1 0x10` Jump to `0x10` if value in `r1` is not zero.
- [ ] **LB**: `LB r1 0x10` Loads a single byte at `0x10` into register `r1`.
- [ ] **SB**: `SB r1 0x10` Stores a single byte in register `r1` to memory location `0x10`.

Expand Down
108 changes: 87 additions & 21 deletions src/preflight_simulator.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
//! This simulator parses the program and executes it, formulating
//! an `ExecutionTrace` containing all the information of the preflight
//! simulation.
//!
//! This file does not involve itself with any S(N/T)ARK systems

use std::collections::HashMap;

use anyhow::{
Expand Down Expand Up @@ -77,25 +71,22 @@ impl SimulationRow {
&self,
prog: &Program,
) -> Result<Self> {
// Remember, we have no jump instructions in our VM ISA
// Hence, this following is safe. Otherwise, more complicated
// logic needs to be implemented.
let program_counter = self.program_counter + 1;
// This is mutable precisely because jump instructions can change it
// in weird ways. This is good default for many other operations though
let mut program_counter = self.program_counter + 1;
let clock = self.clock + 1;

let instruction = prog
.code
.get(&program_counter)
.cloned()
.context("instruction not found")?;

let is_halted = instruction == Instruction::Halt;
let mut registers = self.registers;

let mut memory_snapshot = self
.memory_snapshot
.clone();

println!(
"[Exec] clk: {}, pc: {}, inst: {:?}",
self.clock, self.program_counter, self.instruction
);

match self.instruction {
Instruction::Add(a, b) => {
registers[usize::from(a)] = registers[usize::from(a)]
Expand All @@ -121,6 +112,16 @@ impl SimulationRow {
registers[usize::from(reg)] = registers[usize::from(reg)]
.wrapping_shr(registers[usize::from(amount)].into());
}
Instruction::Jz(reg, instloc) => {
if registers[usize::from(reg)] == 0 {
program_counter = instloc.0
}
}
Instruction::Jnz(reg, instloc) => {
if registers[usize::from(reg)] != 0 {
program_counter = instloc.0
}
}
Instruction::Lb(reg, memloc) => {
registers[usize::from(reg)] = self
.memory_snapshot
Expand All @@ -138,6 +139,14 @@ impl SimulationRow {
}
};

let instruction = prog
.code
.get(&program_counter)
.cloned()
.context("instruction not found")?;

let is_halted = instruction == Instruction::Halt;

Ok(Self {
instruction,
clock,
Expand Down Expand Up @@ -171,16 +180,18 @@ pub struct PreflightSimulation {
}

impl PreflightSimulation {
/// Maximum number of CPU cycles allowed
const MAX_CPU_CYCLES_ALLOWED: usize = 1_000;

/// Entry point to simulate a program and generate a `PreflightSimulation`
/// to be used to generate tables
pub fn simulate(prog: &Program) -> Result<Self> {
const MAX_CPU_CYCLES_ALLOWED: usize = 1_000;

let mut trace_rows = Vec::with_capacity(MAX_CPU_CYCLES_ALLOWED / 4);
let mut trace_rows =
Vec::with_capacity(Self::MAX_CPU_CYCLES_ALLOWED / 4);
let first_row = SimulationRow::generate_first_row(prog)?;
trace_rows.push(first_row);

while trace_rows.len() < MAX_CPU_CYCLES_ALLOWED
while trace_rows.len() <= Self::MAX_CPU_CYCLES_ALLOWED
&& !trace_rows[trace_rows.len() - 1].is_halted
{
let current_row =
Expand All @@ -205,6 +216,7 @@ mod tests {

use crate::vm_specs::{
Instruction,
InstructionLocation,
MemoryLocation,
Program,
Register,
Expand Down Expand Up @@ -253,4 +265,58 @@ mod tests {
expected.1
);
}

#[test]
/// Tests whether execution stops on reaching `MAX_CPU_CYCLES_ALLOWED`
fn test_max_cpu_cycles() {
let instructions = vec![
Instruction::Jz(Register::R0, InstructionLocation(0x00)),
Instruction::Halt,
];

let code = instructions
.into_iter()
.enumerate()
.map(|(idx, inst)| (idx as u8, inst))
.collect::<HashMap<u8, Instruction>>();

let program = Program {
entry_point: 0,
code,
..Default::default()
};

let simulation = PreflightSimulation::simulate(&program);
assert!(simulation.is_err());
}

#[test]
/// Tests whether execution halts
fn test_haltable() {
let instructions = vec![
Instruction::Lb(Register::R0, MemoryLocation(0x40)),
Instruction::Lb(Register::R1, MemoryLocation(0x41)),
Instruction::Sub(Register::R0, Register::R1),
Instruction::Jnz(Register::R0, InstructionLocation(0x02)),
Instruction::Halt,
];

let code = instructions
.into_iter()
.enumerate()
.map(|(idx, inst)| (idx as u8, inst))
.collect::<HashMap<u8, Instruction>>();

let memory_init: HashMap<u8, u8> =
HashMap::from_iter(vec![(0x40, 0x05), (0x41, 0x01)]);

let program = Program {
entry_point: 0,
code,
memory_init,
};

let simulation = PreflightSimulation::simulate(&program);
assert!(simulation.is_ok());
}
}
15 changes: 12 additions & 3 deletions src/vm_specs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ pub const REGISTER_COUNT: usize = std::mem::variant_count::<Register>();
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct MemoryLocation(pub u8);

/// All instruction locations in this VM are addresses via u8.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct InstructionLocation(pub u8);

#[derive(Clone, Debug, Default, PartialEq)]
pub enum Instruction {
Add(Register, Register),
Expand All @@ -32,6 +36,8 @@ pub enum Instruction {
Div(Register, Register),
Shl(Register, Register),
Shr(Register, Register),
Jz(Register, InstructionLocation),
Jnz(Register, InstructionLocation),
Lb(Register, MemoryLocation),
Sb(Register, MemoryLocation),
#[default]
Expand All @@ -41,6 +47,7 @@ pub enum Instruction {
impl Instruction {
/// Not the best of the implementations. But written it like this
/// for demonstration purposes
/// Prime candidate for Proc Macros :)
pub fn get_opcode(&self) -> u8 {
match self {
Instruction::Add(_, _) => 0,
Expand All @@ -49,9 +56,11 @@ impl Instruction {
Instruction::Div(_, _) => 3,
Instruction::Shl(_, _) => 4,
Instruction::Shr(_, _) => 5,
Instruction::Lb(_, _) => 6,
Instruction::Sb(_, _) => 7,
Instruction::Halt => 8,
Instruction::Jz(_, _) => 6,
Instruction::Jnz(_, _) => 7,
Instruction::Lb(_, _) => 8,
Instruction::Sb(_, _) => 9,
Instruction::Halt => 10,
}
}
}
Expand Down

0 comments on commit 57d14ce

Please sign in to comment.