1use std::io::{empty, sink, Empty, Error as IOError, Sink};
2use std::path::Path;
3
4pub mod instruction;
5pub mod iostream_handler;
6
7#[cfg(all(target_os = "windows", not(feature = "disable-crlf-compat-windows")))]
8pub(crate) mod crlf_helper;
9
10pub use instruction::*;
11pub use iostream_handler::IOStreamHandler;
12use std::fmt::Debug;
13
14pub const KBSR: usize = 0xFE00;
16pub const KBDR: usize = 0xFE02;
17pub const DSR: usize = 0xFE04;
18pub const DDR: usize = 0xFE06;
19pub const MCR: usize = 0xFFFE;
20
21const DEFAULT_MEM: [u16; 1 << 16] = {
22 let mut mem = [0; 1 << 16];
23 mem[DSR] = 0b1000_0000_0000_0000;
24 mem[MCR] = 0b1000_0000_0000_0000;
25 mem
26};
27
28const OPERATING_SYSTEM: &[u8] = include_bytes!("../static/lc3os.obj");
39
40pub trait InstructionHandler
42where
43 Self: Sized,
44{
45 type Context;
47 type Err;
50
51 fn create_vm(initial_context: Self::Context) -> VM<Self>;
52
53 fn process_instruction(
54 vm_state: &mut VMState,
55 context: &mut Self::Context,
56 instruction: Instruction,
57 ) -> Result<(), Self::Err>;
58}
59
60pub struct TrivialHandler;
62
63impl InstructionHandler for TrivialHandler {
64 type Context = (Empty, Sink);
65 type Err = IOError;
66
67 fn create_vm(_: Self::Context) -> VM<Self> {
68 let mut vm = VM {
69 state: VMState::default(),
70 context: (empty(), sink()),
71 };
72 vm.load_u8(OPERATING_SYSTEM);
73 vm
74 }
75
76 fn process_instruction(
77 vm_state: &mut VMState,
78 context: &mut Self::Context,
79 instruction: Instruction,
80 ) -> Result<(), Self::Err> {
81 IOStreamHandler::process_instruction(vm_state, context, instruction)
82 }
83}
84
85#[derive(Clone)]
94pub struct VM<H=TrivialHandler>
95where
96 H: InstructionHandler,
97{
98 pub state: VMState,
99 pub context: H::Context,
101}
102
103impl<H> VM<H>
104where
105 H: InstructionHandler,
106 H::Err: Debug,
107{
108 pub fn new(initial_context: H::Context) -> VM<H> {
110 H::create_vm(initial_context)
111 }
112
113 pub fn load_u8(&mut self, program: &[u8]) {
117 let mut chunks = program.chunks(2);
118 let header = chunks.next().unwrap();
119 let orig = (((u16::from(header[0])) << 8) + u16::from(header[1])).into();
120 self.load_u16(
121 orig,
122 chunks
123 .map(|x| ((u16::from(x[0])) << 8) + u16::from(x[1]))
124 .collect::<Vec<_>>()
125 .as_ref(),
126 );
127 }
128
129 pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> std::io::Result<()> {
133 let program = std::fs::read(path)?;
134 self.load_u8(&program);
135 Ok(())
136 }
137
138 pub fn load_u16(&mut self, entry: usize, program: &[u16]) {
145 self.state.mem[entry..(entry + program.len())].copy_from_slice(program);
146 self.state.pc = entry as u16;
147 }
148
149 pub fn step(&mut self) -> Result<(), H::Err> {
152 self.state.fetch();
153 let instruction = Instruction::from_u16(self.state.ir);
154 H::process_instruction(&mut self.state, &mut self.context, instruction)
155 }
156
157 pub fn step_n(&mut self, n: usize) -> Result<(), H::Err> {
160 for _ in 0..n {
161 self.step()?;
162 }
163 Ok(())
164 }
165
166 pub fn run(&mut self) -> Result<usize, H::Err> {
171 let mut steps = 0;
172 while self.state.mem[MCR] >> 15 > 0 {
173 self.step()?;
174 steps += 1;
175 }
176 Ok(steps)
177 }
178
179 pub fn run_n(&mut self, n: usize) -> Result<usize, H::Err> {
185 let mut steps = 0;
186 while self.state.mem[MCR] >> 15 > 0 && steps < n {
187 #[cfg(feature = "register-trace")]
188 let pc = self.pc;
189
190 self.step()?;
191
192 #[cfg(feature = "register-trace")]
193 eprintln!(
194 "[DEBUG] PC=0x{:04X}, IR=0x{:04X}, POST_REG={:04X?}",
195 pc, self.state.ir, self.state.register
196 );
197
198 steps += 1;
199 }
200 Ok(steps)
201 }
202}
203
204impl Default for VM<TrivialHandler> {
205 fn default() -> Self {
206 Self {
207 state: Default::default(),
208 context: (empty(), sink()),
209 }
210 }
211}
212
213#[derive(Clone)]
215pub struct VMState {
216 pub register: [i16; 8],
218 pub pc: u16,
220 pub ir: u16,
222 pub supervisor: bool,
224 pub priority: u8,
226 pub condition: Condition,
228 pub mem: [u16; 1 << 16],
230}
231
232impl VMState {
233 pub fn psr(&self) -> u16 {
235 ((if self.supervisor { 0 } else { 1 << 15 })
236 + ((u16::from(self.priority) & 0b111) << 8)
237 + (if self.condition.n { 1 << 2 } else { 0 })
238 + (if self.condition.z { 1 << 1 } else { 0 })
239 + (if self.condition.p { 1 } else { 0 })) as u16
240 }
241
242 fn fetch(&mut self) {
244 self.ir = self.mem[self.pc as usize];
245 self.pc += 1;
246 }
247
248 fn update_condition(&mut self, reg: usize) {
251 self.condition.n = self.register[reg] < 0;
252 self.condition.z = self.register[reg] == 0;
253 self.condition.p = self.register[reg] > 0;
254 }
255}
256
257impl Default for VMState {
258 fn default() -> Self {
260 VMState {
261 register: [0; 8],
262 pc: 0x0000,
263 ir: 0x0000,
264 supervisor: false,
265 priority: 0,
266 condition: Default::default(),
267 mem: DEFAULT_MEM,
268 }
269 }
270}
271
272#[test]
273fn test_update_condition() {
274 let mut vm = VM::default();
275
276 vm.state.register[0] = 0xFF33u16 as i16;
277 vm.state.update_condition(0);
278 assert!(vm.state.condition.n);
279
280 vm.state.register[2] = 0x0F33i16;
281 vm.state.update_condition(2);
282 assert!(vm.state.condition.p);
283
284 vm.state.register[1] = 0x0000;
285 vm.state.update_condition(1);
286 assert!(vm.state.condition.z);
287}
288
289#[test]
290fn test_step() {
291 let mut vm = VM::default();
292 vm.load_u16(0x3000, &[0x1260]);
294 vm.step().unwrap();
295 assert!(vm.state.condition.z);
296}
297
298#[test]
299fn test_step_n() {
300 let mut vm = VM::default();
301 vm.load_u16(0x3000, &[0x5020, 0x102E]);
304 vm.step_n(2).unwrap();
305 assert_eq!(vm.state.register[0], 14);
306}
307
308#[test]
309fn test_run() {
310 let mut vm = VM::default();
311 vm.load_u16(
322 0x3000,
323 &[
324 0x5020, 0x5260, 0x1023, 0x0403, 0x1262, 0x103F, 0x0FFC, 0xB201, 0xF025, 0x3100,
325 ],
326 );
327 vm.run().unwrap();
328 assert_eq!(vm.state.mem[0x3100], 6);
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334
335 #[test]
336 fn test_load() {
337 VM::default();
338 VM::default().load_u8(OPERATING_SYSTEM);
339 VM::default().load_u8(&[0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF]);
340 VM::default().load_u16(0x3000, &[0, 0, 0, 0]);
341 }
342
343 #[test]
344 #[should_panic]
345 fn test_load_panic() {
346 VM::default().load_u8(OPERATING_SYSTEM[1..].as_ref());
347 VM::default().load_u8(&[0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF]);
348 VM::default().load_u8(&[0xFF, 0xFF, 0xFF, 0xFE, 0xFF]);
349 VM::default().load_u16(0xFFFF, &[0, 0, 0, 0]);
350 }
351}