Skip to content

Commit

Permalink
🎉 implement assembly and assembler for kvm
Browse files Browse the repository at this point in the history
  • Loading branch information
KPMGE committed Sep 18, 2024
1 parent 27e865b commit 0e17bb0
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 85 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[workspace]
resolver = "2"
members = [ "ksm", "kvm","lang"]
1 change: 1 addition & 0 deletions ksm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2021"

[dependencies]
kvm = { path = "../kvm" }
11 changes: 11 additions & 0 deletions ksm/examples/fibonacci.ksm
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
push 0
push 1
dup 1
dup 1
add
dup 1
push 8
eq
jmpif 10
jmp 2
halt
27 changes: 25 additions & 2 deletions ksm/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
fn main() {
println!("Hello, world!");
use kvm::Instruction;
use std::env;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = env::args().collect();

if args.len() < 3 {
panic!("Usage: ksm <input.ksm> <output.kvm>");
}

let input_file = &args[1];
let output_file = &args[2];

let prog_asm = std::fs::read_to_string(input_file)?;
let prog: Vec<Instruction> = prog_asm
.lines()
.map(|line| line.trim().try_into().unwrap())
.collect();

println!("prog: {:?}", prog);

kvm::save_program_to_file(prog, output_file);

Ok(())
}
131 changes: 131 additions & 0 deletions kvm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use std::{fmt::Display, fs::File, io::Write};

#[derive(Debug, Clone)]
pub enum Instruction {
Halt,
Add,
Sub,
Div,
Mul,
Eq,
Push(i32),
Jmp(u32),
JmpIf(u32),
Dup(u32),
}

// TODO: find a better place for this function
pub fn save_program_to_file(program: Vec<Instruction>, file_path: &str) {
let mut file = File::create(file_path).unwrap();
let prog_bin: Vec<u8> = program.iter().flat_map(|inst| inst.as_bytes()).collect();
file.write_all(prog_bin.as_ref()).unwrap();
}

impl TryFrom<&str> for Instruction {
// TODO: add better error
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"halt" => Ok(Instruction::Halt),
"add" => Ok(Instruction::Add),
"sub" => Ok(Instruction::Sub),
"div" => Ok(Instruction::Div),
"mul" => Ok(Instruction::Mul),
"eq" => Ok(Instruction::Eq),
_ if value.starts_with("push") => {
let n = value
.split_whitespace()
.nth(1)
.ok_or_else(|| "could not get argument for push".to_string())?
.parse::<i32>()
.map_err(|e| format!("{:?}", e))?;

Ok(Instruction::Push(n))
}
_ if value.starts_with("jmp") => {
let n = value
.split_whitespace()
.nth(1)
.ok_or_else(|| "could not get argument for jmp".to_string())?
.parse::<u32>()
.map_err(|e| format!("{:?}", e))?;

Ok(Instruction::Jmp(n))
}
_ if value.starts_with("jmpif") => {
let n = value
.split_whitespace()
.nth(1)
.ok_or_else(|| "could not get argument for jmpif".to_string())?
.parse::<u32>()
.map_err(|e| format!("{:?}", e))?;

Ok(Instruction::JmpIf(n))
}
_ if value.starts_with("dup") => {
let n = value
.split_whitespace()
.nth(1)
.ok_or_else(|| "could not get argument for dup".to_string())?
.parse::<u32>()
.map_err(|e| format!("{:?}", e))?;

Ok(Instruction::Dup(n))
}
_ => {
panic!("Invalid instruction: {}", value);
}
}
}
}

impl Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Instruction::Halt => "halt".to_string(),
Instruction::Add => "add".to_string(),
Instruction::Sub => "sub".to_string(),
Instruction::Div => "div".to_string(),
Instruction::Mul => "mul".to_string(),
Instruction::Eq => "eq".to_string(),
Instruction::Push(n) => format!("push {}", n),
Instruction::Jmp(addr) => format!("jmp {}", addr),
Instruction::JmpIf(addr) => format!("jmpif {}", addr),
Instruction::Dup(addr) => format!("dup {}", addr),
};
write!(f, "{}", s)
}
}

impl Instruction {
pub fn as_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();

match self {
Instruction::Halt => bytes.push(0x0),
Instruction::Add => bytes.push(0x1),
Instruction::Sub => bytes.push(0x2),
Instruction::Div => bytes.push(0x3),
Instruction::Mul => bytes.push(0x4),
Instruction::Eq => bytes.push(0x5),
Instruction::Push(n) => {
bytes.push(0x6);
bytes.extend(n.to_le_bytes());
}
Instruction::Jmp(n) => {
bytes.push(0x7);
bytes.extend(n.to_le_bytes());
}
Instruction::JmpIf(n) => {
bytes.push(0x8);
bytes.extend(n.to_le_bytes());
}
Instruction::Dup(n) => {
bytes.push(0x9);
bytes.extend(n.to_le_bytes());
}
};

bytes
}
}
96 changes: 13 additions & 83 deletions kvm/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,7 @@
use std::{
error::Error,
fs::File,
io::{Read, Write},
};
use kvm::Instruction;
use std::{error::Error, fs::File, io::Read};
use thiserror::Error;

#[derive(Debug, Clone)]
enum Instruction {
Halt,
Add,
Sub,
Div,
Mul,
Eq,
Push(i32),
Jmp(u32),
JmpIf(u32),
Dup(u32),
}

impl Instruction {
fn as_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();

match self {
Instruction::Halt => bytes.push(0x0),
Instruction::Add => bytes.push(0x1),
Instruction::Sub => bytes.push(0x2),
Instruction::Div => bytes.push(0x3),
Instruction::Mul => bytes.push(0x4),
Instruction::Eq => bytes.push(0x5),
Instruction::Push(n) => {
bytes.push(0x6);
bytes.extend(n.to_le_bytes());
}
Instruction::Jmp(n) => {
bytes.push(0x7);
bytes.extend(n.to_le_bytes());
}
Instruction::JmpIf(n) => {
bytes.push(0x8);
bytes.extend(n.to_le_bytes());
}
Instruction::Dup(n) => {
bytes.push(0x9);
bytes.extend(n.to_le_bytes());
}
};

bytes
}
}

const STACK_CAPACITY: usize = 1024;

#[derive(Debug, Error)]
Expand Down Expand Up @@ -211,7 +161,7 @@ impl Kvm {
buffer[i + 3],
buffer[i + 4],
]);
// skip 8 bytes
// skip 4 bytes
i += 4;

Instruction::Jmp(addr)
Expand All @@ -223,7 +173,7 @@ impl Kvm {
buffer[i + 3],
buffer[i + 4],
]);
// skip 8 bytes
// skip 4 bytes
i += 4;

Instruction::JmpIf(addr)
Expand All @@ -235,7 +185,7 @@ impl Kvm {
buffer[i + 3],
buffer[i + 4],
]);
// skip 8 bytes
// skip 4 bytes
i += 4;
Instruction::Dup(addr)
}
Expand All @@ -249,16 +199,6 @@ impl Kvm {
self.load_program_from_vec(instructions);
}

fn save_program_to_file(&self, file_path: &str) {
let mut file = File::create(file_path).unwrap();
let prog_bin: Vec<u8> = self
.program
.iter()
.flat_map(|inst| inst.as_bytes())
.collect();
file.write_all(prog_bin.as_ref()).unwrap();
}

fn dump_stack(&self) {
println!("Stack: ");

Expand All @@ -276,28 +216,18 @@ impl Kvm {
}

fn main() -> Result<(), Box<dyn Error>> {
let mut vm = Kvm::new();
let args: Vec<String> = std::env::args().collect();

let prog = vec![
Instruction::Push(0),
Instruction::Push(1),
Instruction::Dup(1),
Instruction::Dup(1),
Instruction::Add,
Instruction::Dup(1),
Instruction::Push(8),
Instruction::Eq,
Instruction::JmpIf(10),
Instruction::Jmp(2),
Instruction::Halt,
];
if args.len() < 2 {
panic!("Usage: kvm <input.kvm>");
}

let mut vm = Kvm::new();

vm.load_program_from_vec(prog);
vm.save_program_to_file("test.kvm");
vm.load_program_from_file("test.kvm");
vm.load_program_from_file(&args[1]);
vm.dump_program();
vm.execute_program()?;
println!("-----");
println!("--------");
vm.dump_stack();

Ok(())
Expand Down

0 comments on commit 0e17bb0

Please sign in to comment.