Compare commits
10 Commits
e21c64cc08
...
c47cb3ba43
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c47cb3ba43 | ||
![]() |
3b3421dc1b | ||
![]() |
0531db1641 | ||
![]() |
017350d6e7 | ||
![]() |
39cdd9550f | ||
![]() |
41514990b5 | ||
![]() |
c6ebceccac | ||
![]() |
707a17e0bc | ||
![]() |
0f9980425c | ||
![]() |
df6caea574 |
BIN
helloworld.obj
Normal file
BIN
helloworld.obj
Normal file
Binary file not shown.
72
src/isa.rs
Normal file
72
src/isa.rs
Normal 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
3
src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod virtmem;
|
||||
pub mod isa;
|
||||
pub mod vm;
|
53
src/main.rs
53
src/main.rs
@ -1,47 +1,12 @@
|
||||
#[allow(dead_code)]
|
||||
enum Registers {
|
||||
R0 = 0,
|
||||
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,
|
||||
}
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
mod vm;
|
||||
|
||||
fn main() {
|
||||
let _memory:[u16; 1 << 16];
|
||||
let _registers:[u16; Registers::COUNT as usize];
|
||||
|
||||
let lc3 = vm::VM::new();
|
||||
// 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
48
src/virtmem.rs
Normal 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
112
src/vm.rs
Normal 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;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
Loading…
x
Reference in New Issue
Block a user