add emitting, start commenting
This commit is contained in:
parent
18e3721a08
commit
c8bf7fd8c9
11
README.md
11
README.md
|
@ -1,2 +1,13 @@
|
|||
# oxvm
|
||||
|
||||
A virtual machine written in Rust
|
||||
|
||||
## binary format
|
||||
|
||||
```Rust
|
||||
const MAGIC_NUMBER: u16 = 0xBADFAD;
|
||||
struct Executable {
|
||||
magic_number: u16
|
||||
load_addr: u32
|
||||
}
|
||||
```
|
||||
|
|
86
src/asm.rs
86
src/asm.rs
|
@ -1,12 +1,12 @@
|
|||
use crate::opcodes::Opcodes;
|
||||
use std::u32;
|
||||
use std::{collections::HashMap, u32};
|
||||
// Registers are u8
|
||||
// Memory addresses are u32
|
||||
#[derive(Debug)]
|
||||
enum Instruction {
|
||||
Jmp(JmpInstr),
|
||||
Goto(LabelJmpInstr),
|
||||
Math(MathInstr),
|
||||
Goto(GotoInstr),
|
||||
Op(OpInstr),
|
||||
Label(LabelInstr),
|
||||
}
|
||||
#[derive(Debug)]
|
||||
|
@ -15,12 +15,12 @@ struct JmpInstr {
|
|||
target: u32,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct LabelJmpInstr {
|
||||
struct GotoInstr {
|
||||
op_type: Opcodes,
|
||||
target: LabelInstr,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct MathInstr {
|
||||
struct OpInstr {
|
||||
op_type: Opcodes,
|
||||
target: u8,
|
||||
src1: u8,
|
||||
|
@ -67,7 +67,7 @@ impl Assembler {
|
|||
match tokens.next() {
|
||||
Some(token) => match token {
|
||||
".start" => {
|
||||
let straddr = tokens.next().expect("No address for .start!");
|
||||
let straddr = tokens.next().expect("FATAL: No address for .start!");
|
||||
let addr = u32::from_str_radix(
|
||||
match straddr.strip_prefix("0x") {
|
||||
Some(stripped) => stripped,
|
||||
|
@ -91,21 +91,19 @@ impl Assembler {
|
|||
}
|
||||
fn code(&mut self, line: &str) {
|
||||
let mut op = line.splitn(2, ' ');
|
||||
let op_type = op
|
||||
.next()
|
||||
.expect("Empty lines should already be handled. THIS IS A BUG.");
|
||||
let op_type = op.next().unwrap();
|
||||
match op_type {
|
||||
"goto" => {
|
||||
let label = op.next().expect("No address for goto!");
|
||||
self.instructions.push(Instruction::Goto(LabelJmpInstr {
|
||||
op_type: Opcodes::Goto,
|
||||
let label = op.next().expect("FATAL: No label for goto!");
|
||||
self.instructions.push(Instruction::Goto(GotoInstr {
|
||||
op_type: Opcodes::Jmp,
|
||||
target: LabelInstr {
|
||||
name: String::from(label),
|
||||
},
|
||||
}))
|
||||
}
|
||||
"jmp" => {
|
||||
let straddr = op.next().expect("No address for jmp!");
|
||||
let straddr = op.next().expect("FATAL: No address for jmp!");
|
||||
let addr = u32::from_str_radix(
|
||||
match straddr.strip_prefix("0x") {
|
||||
Some(stripped) => stripped,
|
||||
|
@ -113,9 +111,9 @@ impl Assembler {
|
|||
},
|
||||
16,
|
||||
)
|
||||
.expect("Error parsing address!");
|
||||
.expect("FATAL: Error parsing address for jmp!");
|
||||
self.instructions.push(Instruction::Jmp(JmpInstr {
|
||||
op_type: Opcodes::Goto,
|
||||
op_type: Opcodes::Jmp,
|
||||
target: addr,
|
||||
}));
|
||||
}
|
||||
|
@ -123,4 +121,62 @@ impl Assembler {
|
|||
_ => println!("Unknown instruction: {}", op_type),
|
||||
}
|
||||
}
|
||||
pub fn emit(self) -> Vec<u8> {
|
||||
let mut instructions: Vec<Vec<u8>> = vec![];
|
||||
let mut labels_srcs: HashMap<usize, String> = HashMap::new();
|
||||
let mut labels_targets: HashMap<String, usize> = HashMap::new();
|
||||
for instr in self.instructions {
|
||||
match instr {
|
||||
Instruction::Label(label_instr) => {
|
||||
instructions.push(vec![]);
|
||||
labels_targets.insert(label_instr.name, instructions.len());
|
||||
}
|
||||
Instruction::Op(op_instr) => instructions.push(vec![
|
||||
op_instr.op_type as u8,
|
||||
op_instr.target,
|
||||
op_instr.src1,
|
||||
op_instr.src2,
|
||||
]),
|
||||
Instruction::Jmp(jmp_instr) => {
|
||||
let mut code = vec![jmp_instr.op_type as u8];
|
||||
code.append(&mut jmp_instr.target.to_le_bytes().to_vec());
|
||||
instructions.push(code);
|
||||
}
|
||||
Instruction::Goto(goto_instr) => {
|
||||
// push placeholder
|
||||
instructions.push(vec![goto_instr.op_type as u8, 255, 255, 255, 255]);
|
||||
// add index of goto to table
|
||||
labels_srcs.insert(instructions.len() - 1, goto_instr.target.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
// a bit ugly, but makes rust happy
|
||||
let mut mutations = Vec::new();
|
||||
// resolve gotos
|
||||
for (index, mut instr) in instructions.iter().enumerate() {
|
||||
// check for placeholder
|
||||
if instr == &vec![Opcodes::Jmp as u8, 255, 255, 255, 255] {
|
||||
let label_name = labels_srcs.get(&index);
|
||||
match label_name {
|
||||
Some(label) => {
|
||||
let label_target =
|
||||
labels_targets.get(label).expect("FATAL: Unknown label!");
|
||||
let mut code = vec![Opcodes::Jmp as u8];
|
||||
let target_addr: u32 = instructions[..*label_target]
|
||||
.iter()
|
||||
.map(|v| v.len())
|
||||
.sum::<usize>() as u32;
|
||||
code.append(&mut target_addr.to_le_bytes().to_vec());
|
||||
mutations.push((index, code));
|
||||
} // if there is no jump target
|
||||
None => println!("Why would you jump to 0xFFFFFFFF?"),
|
||||
}
|
||||
}
|
||||
}
|
||||
for (index, code) in mutations {
|
||||
instructions[index] = code;
|
||||
}
|
||||
println!("{:#?}", instructions);
|
||||
instructions.into_iter().flatten().collect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ fn main() {
|
|||
let mut asmb = Assembler::new();
|
||||
asmb.assemble(read_to_string(filename).expect("Failed to read file!"));
|
||||
println!("{:#?}", asmb);
|
||||
let code = asmb.emit();
|
||||
println!("{:#?}", code);
|
||||
}
|
||||
None => println!("No file specified"),
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#[derive(Debug)]
|
||||
pub enum Opcodes {
|
||||
Goto,
|
||||
Jmp,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue