use std::io::{empty, sink, Empty, Error as IOError, Sink};
use std::path::Path;
pub mod instruction;
pub mod iostream_handler;
#[cfg(all(target_os = "windows", not(feature = "disable-crlf-compat-windows")))]
pub(crate) mod crlf_helper;
pub use instruction::*;
pub use iostream_handler::IOStreamHandler;
use std::fmt::Debug;
pub const KBSR: usize = 0xFE00;
pub const KBDR: usize = 0xFE02;
pub const DSR: usize = 0xFE04;
pub const DDR: usize = 0xFE06;
pub const MCR: usize = 0xFFFE;
const DEFAULT_MEM: [u16; 1 << 16] = {
let mut mem = [0; 1 << 16];
mem[DSR] = 0b1000_0000_0000_0000;
mem[MCR] = 0b1000_0000_0000_0000;
mem
};
const OPERATING_SYSTEM: &[u8] = include_bytes!("../static/lc3os.obj");
pub trait InstructionHandler
where
Self: Sized,
{
type Context;
type Err;
fn create_vm(initial_context: Self::Context) -> VM<Self>;
fn process_instruction(
vm_state: &mut VMState,
context: &mut Self::Context,
instruction: Instruction,
) -> Result<(), Self::Err>;
}
impl InstructionHandler for () {
type Context = (Empty, Sink);
type Err = IOError;
fn create_vm(_: Self::Context) -> VM<Self> {
let mut vm = VM::<()> {
state: VMState::default(),
context: (empty(), sink()),
};
vm.load_u8(OPERATING_SYSTEM);
vm
}
fn process_instruction(
vm_state: &mut VMState,
context: &mut Self::Context,
instruction: Instruction,
) -> Result<(), Self::Err> {
IOStreamHandler::process_instruction(vm_state, context, instruction)
}
}
#[derive(Clone)]
pub struct VM<H>
where
H: InstructionHandler,
{
pub state: VMState,
pub context: H::Context,
}
impl<H> VM<H>
where
H: InstructionHandler,
H::Err: Debug,
{
pub fn new(initial_context: H::Context) -> VM<H> {
H::create_vm(initial_context)
}
pub fn load_u8(&mut self, program: &[u8]) {
let mut chunks = program.chunks(2);
let header = chunks.next().unwrap();
let orig = (((u16::from(header[0])) << 8) + u16::from(header[1])).into();
self.load_u16(
orig,
chunks
.map(|x| ((u16::from(x[0])) << 8) + u16::from(x[1]))
.collect::<Vec<_>>()
.as_ref(),
);
}
pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> std::io::Result<()> {
let program = std::fs::read(path)?;
self.load_u8(&program);
Ok(())
}
pub fn load_u16(&mut self, entry: usize, program: &[u16]) {
self.state.mem[entry..(entry + program.len())].copy_from_slice(program);
self.state.pc = entry as u16;
}
pub fn step(&mut self) -> Result<(), H::Err> {
self.state.fetch();
let instruction = Instruction::from_u16(self.state.ir);
H::process_instruction(&mut self.state, &mut self.context, instruction)
}
pub fn step_n(&mut self, n: usize) -> Result<(), H::Err> {
for _ in 0..n {
self.step()?;
}
Ok(())
}
pub fn run(&mut self) -> Result<usize, H::Err> {
let mut steps = 0;
while self.state.mem[MCR] >> 15 > 0 {
self.step()?;
steps += 1;
}
Ok(steps)
}
pub fn run_n(&mut self, n: usize) -> Result<usize, H::Err> {
let mut steps = 0;
while self.state.mem[MCR] >> 15 > 0 && steps < n {
#[cfg(feature = "register-trace")]
let pc = self.pc;
self.step()?;
#[cfg(feature = "register-trace")]
eprintln!(
"[DEBUG] PC=0x{:04X}, IR=0x{:04X}, POST_REG={:04X?}",
pc, self.state.ir, self.state.register
);
steps += 1;
}
Ok(steps)
}
}
impl Default for VM<()> {
fn default() -> Self {
Self {
state: Default::default(),
context: (empty(), sink()),
}
}
}
#[derive(Clone)]
pub struct VMState {
pub register: [i16; 8],
pub pc: u16,
pub ir: u16,
pub supervisor: bool,
pub priority: u8,
pub condition: Condition,
pub mem: [u16; 1 << 16],
}
impl VMState {
pub fn psr(&self) -> u16 {
((if self.supervisor { 0 } else { 1 << 15 })
+ ((u16::from(self.priority) & 0b111) << 8)
+ (if self.condition.n { 1 << 2 } else { 0 })
+ (if self.condition.z { 1 << 1 } else { 0 })
+ (if self.condition.p { 1 } else { 0 })) as u16
}
fn fetch(&mut self) {
self.ir = self.mem[self.pc as usize];
self.pc += 1;
}
fn update_condition(&mut self, reg: usize) {
self.condition.n = self.register[reg] < 0;
self.condition.z = self.register[reg] == 0;
self.condition.p = self.register[reg] > 0;
}
}
impl Default for VMState {
fn default() -> Self {
VMState {
register: [0; 8],
pc: 0x0000,
ir: 0x0000,
supervisor: false,
priority: 0,
condition: Default::default(),
mem: DEFAULT_MEM,
}
}
}
#[test]
fn test_update_condition() {
let mut vm = VM::default();
vm.state.register[0] = 0xFF33u16 as i16;
vm.state.update_condition(0);
assert!(vm.state.condition.n);
vm.state.register[2] = 0x0F33i16;
vm.state.update_condition(2);
assert!(vm.state.condition.p);
vm.state.register[1] = 0x0000;
vm.state.update_condition(1);
assert!(vm.state.condition.z);
}
#[test]
fn test_step() {
let mut vm = VM::<()>::default();
vm.load_u16(0x3000, &[0x1260]);
vm.step().unwrap();
assert!(vm.state.condition.z);
}
#[test]
fn test_step_n() {
let mut vm = VM::<()>::default();
vm.load_u16(0x3000, &[0x5020, 0x102E]);
vm.step_n(2).unwrap();
assert_eq!(vm.state.register[0], 14);
}
#[test]
fn test_run() {
use std::io::{empty, sink};
let mut vm = VM::<()>::new((empty(), sink()));
vm.load_u16(
0x3000,
&[
0x5020, 0x5260, 0x1023, 0x0403, 0x1262, 0x103F, 0x0FFC, 0xB201, 0xF025, 0x3100,
],
);
vm.run().unwrap();
assert_eq!(vm.state.mem[0x3100], 6);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_load() {
VM::<()>::new((empty(), sink()));
VM::default().load_u8(OPERATING_SYSTEM);
VM::default().load_u8(&[0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF]);
VM::default().load_u16(0x3000, &[0, 0, 0, 0]);
}
#[test]
#[should_panic]
fn test_load_panic() {
VM::default().load_u8(OPERATING_SYSTEM[1..].as_ref());
VM::default().load_u8(&[0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF]);
VM::default().load_u8(&[0xFF, 0xFF, 0xFF, 0xFE, 0xFF]);
VM::default().load_u16(0xFFFF, &[0, 0, 0, 0]);
}
}