Compare commits

...

10 Commits

Author SHA1 Message Date
vik
c47cb3ba43 wrestling with crates and imports 2025-01-01 20:38:25 -06:00
vik
3b3421dc1b implement get opcode from instr 2025-01-01 19:42:52 -06:00
vik
0531db1641 fetch execute loop 2025-01-01 17:48:41 -06:00
vik
017350d6e7 happy new year! implementing reading to mem from file 2025-01-01 15:25:59 -06:00
vik
39cdd9550f opcodes + cleanup 2024-12-31 22:45:42 -06:00
vik
41514990b5 basic vm struct 2024-12-31 22:40:45 -06:00
vik
c6ebceccac registers 2024-12-31 22:21:19 -06:00
vik
707a17e0bc made functions and stuff for memory 2024-12-31 21:51:27 -06:00
vik
0f9980425c stripped everything down - starting from scratch. reading obj file works 2024-12-31 21:39:26 -06:00
vik
df6caea574 adding memory read 2024-12-30 17:50:21 -06:00
6 changed files with 244 additions and 44 deletions

BIN
helloworld.obj Normal file

Binary file not shown.

72
src/isa.rs Normal file
View File

@ -0,0 +1,72 @@
#![allow(dead_code)]
#![allow(unused_variables)]
use crate::vm::VM;
enum Opcode {
BR = 0,
ADD,
LD,
ST,
JSR,
AND,
LDR,
STR,
RTI,
NOT,
LDI,
STI,
JMP,
RES,
LEA,
TRAP,
NOOP,
}
fn get_opcode(instr: u16) -> Opcode {
match instr >> 12 {
0 => Opcode::BR,
1 => Opcode::ADD,
2 => Opcode::LD,
3 => Opcode::ST,
4 => Opcode::JSR,
5 => Opcode::AND,
6 => Opcode::LDR,
7 => Opcode::STR,
8 => Opcode::RTI,
9 => Opcode::NOT,
10 => Opcode::LDI,
11 => Opcode::STI,
12 => Opcode::JMP,
13 => Opcode::RES,
14 => Opcode::LEA,
15 => Opcode::TRAP,
_ => Opcode::NOOP,
}
}
pub fn execute_opcode(vm: &mut VM) {
let instr = vm.memory.get(vm.registers.pc);
let opcode = get_opcode(instr);
match opcode {
Opcode::ADD => noop(instr, vm),
Opcode::AND => noop(instr, vm),
Opcode::NOT => noop(instr, vm),
Opcode::BR => noop(instr, vm),
Opcode::JMP => noop(instr, vm),
Opcode::JSR => noop(instr, vm),
Opcode::LD => noop(instr, vm),
Opcode::LDI => noop(instr, vm),
Opcode::LDR => noop(instr, vm),
Opcode::LEA => noop(instr, vm),
Opcode::ST => noop(instr, vm),
Opcode::STI => noop(instr, vm),
Opcode::STR => noop(instr, vm),
Opcode::TRAP => noop(instr, vm),
_ => {}
}
}
fn noop(instr: u16, vm: &mut VM) {}

3
src/lib.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod virtmem;
pub mod isa;
pub mod vm;

View File

@ -1,47 +1,12 @@
#[allow(dead_code)] #![allow(dead_code)]
enum Registers { #![allow(unused_variables)]
R0 = 0, mod vm;
R1,
R2,
R3,
R4,
R5,
R6,
R7,
PC,
COND,
COUNT
}
#[allow(dead_code)]
enum Opcodes {
// https://www.cs.utexas.edu/~fussell/courses/cs310h/lectures/Lecture_10-310h.pdf
ADD = 0, // add
AND, // and
NOT, // not (bitwise)
LD, // load (pc relative)
LDI, // load indirect
LDR, // load base + offset
LEA, // load immediate
ST, // store pc relative
STR, // store base + offset
STI, // store indirect
BR, // branch
JSR, // jump register
JMP, // jump
RTI, // return from interrupt (unused)
TRAP, // trap
}
#[allow(dead_code)]
enum ConditionFlags {
POS = 1 << 0,
ZERO = 1 << 1,
NEG = 1 << 2,
}
fn main() { fn main() {
let _memory:[u16; 1 << 16]; let lc3 = vm::VM::new();
let _registers:[u16; Registers::COUNT as usize]; // let mut lc3 = VM::new();
// let args: Vec<String> = env::args().collect();
// let path = args.get(1).expect("a file must be specified");
// lc3.memory.read(path.to_string());
// lc3.execute();
} }

48
src/virtmem.rs Normal file
View File

@ -0,0 +1,48 @@
use std::{fs, io::{self, Read}};
pub const MEM_SIZE: usize = u16::MAX as usize;
pub struct Memory {
pub data: [u16; MEM_SIZE],
}
impl Memory {
pub fn new() -> Memory {
Memory {
data: [0; MEM_SIZE],
}
}
pub fn set(&mut self, addr: u16, value: u16) {
self.data[addr as usize] = value
}
pub fn get(&mut self, addr: u16) -> u16 {
self.data[addr as usize]
}
pub fn read(&mut self, path: String) {
// don't need byteorder crate!!
let file = fs::File::open(path).expect("could not open file");
let mut reader = io::BufReader::new(file);
let mut buffer: [u8;2] = [0, 2];
let _ = reader.read_exact(&mut buffer).expect("could not read file");
let base_addr = u16::from_be_bytes(buffer);
let mut addr = base_addr;
loop {
match reader.read_exact(&mut buffer) {
Ok(()) => {
let instruction = u16::from_be_bytes(buffer);
self.set(addr, instruction as u16);
addr += 1;
}
Err(e) => {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
break;
} else {
panic!("could not read instruction {:?}", e);
}
}
}
}
}
}

112
src/vm.rs Normal file
View File

@ -0,0 +1,112 @@
#![allow(dead_code)]
#![allow(unused)]
use crate::virtmem::Memory;
const PC_START: u16 = 0x300;
pub struct Registers {
pub r0: u16,
pub r1: u16,
pub r2: u16,
pub r3: u16,
pub r4: u16,
pub r5: u16,
pub r6: u16,
pub r7: u16,
pub pc: u16,
pub cond: u16,
}
enum ConditionFlags {
// condition flags can only be set as nzp with a 1 in each position
POS = 1 << 0,
ZERO = 1 << 1,
NEG = 1 << 2,
}
impl Registers {
fn new() -> Registers {
Registers {
r0: 0,
r1: 0,
r2: 0,
r3: 0,
r4: 0,
r5: 0,
r6: 0,
r7: 0,
pc: PC_START,
cond: 0,
}
}
// s/o dogeystamp for this cool register return logic
// most other vms just updated it with a &mut self
fn return_register(&mut self, num: u16) -> &mut u16 {
match num {
0 => &mut self.r0,
1 => &mut self.r1,
2 => &mut self.r2,
3 => &mut self.r3,
4 => &mut self.r4,
5 => &mut self.r5,
6 => &mut self.r6,
7 => &mut self.r7,
8 => &mut self.pc,
9 => &mut self.cond,
_ => panic!("not a register!"),
}
}
fn get_register(&mut self, num: u16) -> u16 {
*self.return_register(num)
}
fn set_registers(&mut self, num : u16, value: u16) {
*self.return_register(num) = value;
}
fn update_cond(&mut self, num : u16) {
if *self.return_register(num) > 0 {
self.cond = ConditionFlags::POS as u16;
} else if *self.return_register(num) == 0 {
self.cond = ConditionFlags::ZERO as u16;
} else {
self.cond = ConditionFlags::NEG as u16
}
}
fn update_reg_and_cond(&mut self, num : u16, value: u16) {
self.set_registers(num, value);
self.update_cond(num);
}
}
pub struct VM {
pub memory: Memory,
pub registers: Registers,
}
impl VM {
pub fn new() -> VM {
VM{
memory: Memory::new(),
registers: Registers::new(),
}
}
}
// pub fn execute(&mut self) {
// while self.registers.pc < 0x3010 {
// let instruction = self.memory.get(self.registers.pc);
// match instruction >> 12 {
// _ => todo!(),
// }
// self.registers.pc += 1;
// }
// }
// }