lc3/
iostream_handler.rs

1use crate::{Instruction, InstructionHandler, VMState, DDR, DSR, KBDR, KBSR, OPERATING_SYSTEM, VM};
2use std::io::{Error as IOError, Read, Result as IOResult, Write};
3use std::marker::PhantomData;
4
5#[cfg(all(target_os = "windows", not(feature = "disable-crlf-compat-windows")))]
6use crate::crlf_helper::CrlfToLf;
7
8/// A standard instruction handler with Read/Write streams for input/output.
9pub struct IOStreamHandler<R: Read, W: Write> {
10    phantom_r: PhantomData<R>,
11    phantom_w: PhantomData<W>,
12}
13
14impl<R: Read, W: Write> InstructionHandler for IOStreamHandler<R, W> {
15    type Context = (R, W);
16    type Err = IOError;
17
18    fn create_vm(initial_context: Self::Context) -> VM<Self> {
19        let mut vm = VM {
20            state: Default::default(),
21            context: initial_context,
22        };
23        vm.load_u8(OPERATING_SYSTEM);
24        vm
25    }
26
27    fn process_instruction(
28        vm_state: &mut VMState,
29        context: &mut Self::Context,
30        instruction: Instruction,
31    ) -> IOResult<()> {
32        macro_rules! reg {
33            ($num:expr) => {
34                vm_state.register[$num as usize]
35            };
36        }
37
38        macro_rules! zero_if_eq {
39            ($addr:expr, $data:expr, $state:expr) => {
40                if $addr == $data {
41                    vm_state.mem[$state] = 0;
42                    /*
43                    println!(
44                        "{}, {}, {}, 0x{:04X}",
45                        stringify!($addr),
46                        stringify!($data),
47                        stringify!($state),
48                        vm_state.mem[$data]
49                    );
50                    */
51                }
52            };
53        }
54
55        let input = &mut context.0;
56
57        #[cfg(all(target_os = "windows", not(feature = "disable-crlf-compat-windows")))]
58        let input = CrlfToLf(input); // Wrap input to replace CRLF to LF
59
60        let mut in_stream = input.bytes();
61
62        macro_rules! handle_input {
63            ($addr:expr) => {
64                if $addr == KBSR {
65                    if let Some(result) = in_stream.next() {
66                        vm_state.mem[KBSR] |= 0b1000_0000_0000_0000;
67                        vm_state.mem[KBDR] = u16::from(result.unwrap());
68                    }
69                }
70            };
71        }
72
73        macro_rules! handle_output {
74            ($addr:expr) => {
75                if $addr == DDR {
76                    context.1.write_all(&[vm_state.mem[DDR] as u8])?;
77                    vm_state.mem[DSR] |= 0b1000_0000_0000_0000;
78                }
79            };
80        }
81
82        #[cfg(feature = "instruction-trace")]
83        eprintln!("[DEBUG] {:?}", instruction);
84
85        match instruction {
86            Instruction::ADD { dst, src1, src2 } => {
87                reg!(dst) = reg!(src1).wrapping_add(reg!(src2));
88                vm_state.update_condition(dst as usize);
89            }
90            Instruction::ADDi { dst, src, immd } => {
91                reg!(dst) = reg!(src).wrapping_add(immd);
92                vm_state.update_condition(dst as usize);
93            }
94            Instruction::AND { dst, src1, src2 } => {
95                reg!(dst) = reg!(src1) & reg!(src2);
96                vm_state.update_condition(dst as usize);
97            }
98            Instruction::ANDi { dst, src, immd } => {
99                reg!(dst) = reg!(src) & immd;
100                vm_state.update_condition(dst as usize);
101            }
102            Instruction::BR { cond, offset } => {
103                if cond.satisfies(&vm_state.condition) {
104                    vm_state.pc = vm_state.pc.wrapping_add(offset as u16);
105                }
106            }
107            Instruction::JMP { base } => {
108                vm_state.pc = reg!(base) as u16;
109            }
110            Instruction::JSR { offset } => {
111                reg!(7) = vm_state.pc as i16;
112                vm_state.pc = vm_state.pc.wrapping_add(offset as u16);
113            }
114            Instruction::JSRR { base } => {
115                reg!(7) = vm_state.pc as i16;
116                vm_state.pc = reg!(base) as u16;
117            }
118            Instruction::LD { dst, offset } => {
119                let addr = (vm_state.pc.wrapping_add(offset as u16)) as usize;
120                handle_input!(addr);
121
122                reg!(dst) = vm_state.mem[addr] as i16;
123                vm_state.update_condition(dst as usize);
124                zero_if_eq!(addr, KBDR, KBSR);
125            }
126            Instruction::LDI { dst, offset } => {
127                let addr =
128                    vm_state.mem[(vm_state.pc.wrapping_add(offset as u16)) as usize] as usize;
129                handle_input!(addr);
130
131                reg!(dst) = vm_state.mem[addr] as i16;
132                vm_state.update_condition(dst as usize);
133                zero_if_eq!(addr, KBDR, KBSR);
134            }
135            Instruction::LDR { dst, base, offset } => {
136                let addr = (reg!(base) as u16).wrapping_add(offset as u16) as usize;
137                handle_input!(addr);
138
139                reg!(dst) = vm_state.mem[addr] as i16;
140                vm_state.update_condition(dst as usize);
141                zero_if_eq!(addr, KBDR, KBSR);
142            }
143            Instruction::LEA { dst, offset } => {
144                reg!(dst) = (vm_state.pc as i16).wrapping_add(offset);
145                vm_state.update_condition(dst as usize);
146            }
147            Instruction::NOT { dst, src } => {
148                reg!(dst) = !reg!(src);
149                vm_state.update_condition(dst as usize);
150            }
151            Instruction::RTI => {
152                context.1.write_fmt(format_args!(
153                    "*** lc3-rs notification ***
154lc3-rs does not support interrupts, but RTI instruction is being executed from address x{:04X}.
155Please check your program if this is not intended.
156*** End lc3-rs notification ***",
157                    vm_state.pc - 1
158                ))?;
159            }
160            Instruction::ST { src, offset } => {
161                let addr = (vm_state.pc.wrapping_add(offset as u16)) as usize;
162                vm_state.mem[addr] = reg!(src) as u16;
163                zero_if_eq!(addr, DDR, DSR);
164
165                handle_output!(addr);
166            }
167            Instruction::STI { src, offset } => {
168                let addr =
169                    vm_state.mem[(vm_state.pc.wrapping_add(offset as u16)) as usize] as usize;
170                vm_state.mem[addr] = reg!(src) as u16;
171                zero_if_eq!(addr, DDR, DSR);
172
173                handle_output!(addr);
174            }
175            Instruction::STR { src, base, offset } => {
176                let addr = (reg!(base) as u16).wrapping_add(offset as u16) as usize;
177                vm_state.mem[addr] = reg!(src) as u16;
178                zero_if_eq!(addr, DDR, DSR);
179
180                handle_output!(addr);
181            }
182            Instruction::RESERVED => {
183                context.1.write_fmt(format_args!(
184                    "*** lc3-rs notification ***
185A RESERVED instruction(0b1101) is being executed from address x{:04X}.
186Please check your program if this is not intended.
187*** End lc3-rs notification ***",
188                    vm_state.pc - 1
189                ))?;
190            }
191            Instruction::TRAP { vect } => {
192                reg!(7) = vm_state.pc as i16;
193                vm_state.pc = vm_state.mem[(vect as u16) as usize];
194            }
195        }
196        Ok(())
197    }
198}