Skip to main content

lemurs_8080/chip/execution/
mod.rs

1use crate::prelude::*;
2use core::num::NonZeroU8;
3use super::access::{*, Register::*, Byte::*, Double::*, Internal::*, Word::*};
4
5pub mod opcode;
6use opcode::{Op, Op::*};
7
8#[cfg(feature="open")]
9pub(super) type OpOutcome = Result<Option<NonZeroU8>, String>;
10#[cfg(not(feature="open"))]
11pub(super) type OpOutcome = Option<NonZeroU8>;
12
13impl<H: Harness + ?Sized, C: BorrowMut<H>> Machine<H, C> {
14    fn from_pc(&self) -> impl Iterator<Item=u8> + '_ {
15        let mut start = self.chip.pc;
16        core::iter::from_fn(move || {let val = self.read(start).0; start += 1; Some(Wrapping(val))})
17    }
18
19    #[doc(hidden)]
20    #[cfg(feature="open")]
21	pub fn execute(&mut self) -> OpOutcome {
22		if !self.chip.active { return Ok(NonZeroU8::new(1)) };
23        let (op, len) = Op::extract(self.from_pc())
24            .map_err(|e| panic!("Couldn't extract opcode from {e:X} at {:#06X}", self.chip.pc)).unwrap();
25        self.chip.pc += len as raw::u16;
26        let outcome = {
27        	let (chip, bus) = self.split_mut();
28        	op.execute_on(chip, bus)
29        };
30        if outcome.is_err() {
31            self.chip.active = false;
32        };
33		let (chip, bus) = self.split_mut();
34        if let Some(action) = bus.did_execute(chip, op)? {
35            action.execute_on(chip, bus).unwrap();
36            if action == Halt { return Ok(None); }
37        }
38        outcome
39	}
40
41    /// The `execute` method is the heart and soul of emulation; it retrieves, decodes, and executes
42    /// one operation from the Harness address space, and updates the CPU's internal state accordingly.
43    ///
44    /// The method returns an optional non-zero 8-bit number indicating the number of CPU cycles
45    /// consumed, which you can use for timing control, or no value if the chip could not proceed.
46    ///
47    /// When the crate is compiled with the `"open"` feature, this method instead returns a `Result`,
48    /// which contains the same optional u8 as the regular form in its `Ok` option or a `String`
49    /// describing the failure in the `Err` option.
50    ///
51    /// For details of the chip operation and instruction set, see the 8080 Programmer's Manual.
52    #[cfg(any(not(feature="open"), doc))]
53	pub fn execute(&mut self) -> OpOutcome {
54		if !self.chip.active { return NonZeroU8::new(1) };
55        let (op, len) = Op::extract(self.from_pc())
56            .map_err(|e| panic!("Couldn't extract opcode from {e:X?}")).unwrap();
57        self.chip.pc += len as raw::u16;
58        let elapsed = {
59        	let (chip, board) = self.split_mut();
60        	op.execute_on(chip, board)
61        };
62        if elapsed.is_none() { self.chip.active = false; }
63        elapsed
64	}
65
66    /// This method submits an interrupt request containing any operation that can be contained
67    /// in one byte. If the core's interrupts flag is reset, no action will be taken and the
68    /// method will return `Ok(false)`. If the flag is set and the operation fits into a single
69    /// byte (a technical requirement of the original chip), it will reset the interrupts flag
70    /// (disabling interrupts until further notice; interrupt vectors should be written to set
71    /// the flag before returning) and execute the supplied instruction, then return `Ok(true)`.
72    ///
73    /// If the operation cannot fit into a single byte, the operation will return a
74    /// `Err(NotUsable(_))` value containing the submitted operation and take no further action.
75    pub fn interrupt(&mut self, op: Op) -> Result<bool, opcode::Error> {
76        if op.len() == 1 {
77            Ok(self.chip.interrupts && {
78                self.chip.active = true;
79                self.chip.interrupts = false;
80                let _ = op.execute_on(&mut self.chip, self.board.borrow_mut());
81                true
82            })
83        } else {
84            Err(opcode::Error::NotUsable(op))
85        }
86    }
87
88    /// This method is a convenience shorthand for `interrupt` that assumes the desired
89    /// operation is a RST action, saving the address of the next instruction of the stack
90    /// and jumping to one of the addresses 0x00, 0x80, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40 or 0x48.
91    pub fn reset_to(&mut self, index: usize) -> Result<bool, opcode::OutOfRange> {
92        match index {
93            0..=7 => Ok(self.interrupt(Reset{vector: index as raw::u8}).ok().unwrap()),
94            _ => Err(opcode::OutOfRange)
95        }
96    }
97}
98
99fn subtract(base: u8, by: u8) -> (u8, bool, bool) {
100    let value = (!by) + Wrapping(1);
101    let aux = base ^ value;
102    let (value, carry) = base.0.overflowing_add(value.0);
103    (Wrapping(value), by.0 != 0 && !carry, (value ^ aux.0) & 0x10 != 0)
104}
105
106macro_rules! byte {
107    {$chip:expr, $from:ident, $bus:expr, $onboard: expr, $external: expr} => {
108        match $chip.resolve($from) {
109            Single(register) => ($chip[register], $onboard),
110            Byte::RAM(address) => ($bus.read(address), $external),
111            _ => unreachable!()
112        }
113    };
114}
115
116impl Op {
117    #[cfg_attr(debug_assertions, allow(unreachable_patterns))]
118    fn execute_on<H: Harness + ?Sized>(self, chip: &mut State, mut bus: impl DerefMut<Target = H>) -> OpOutcome {
119        let cycles = match self {
120            Add { from, carry } => {
121                let (value, time) = byte!{chip, from, bus, 4, 7};
122                AddTo{value, carry}.execute_on(chip, bus)?;
123                time
124            }
125            AddTo { value, carry } => {
126                let carry_in = chip.c && carry;
127                let accumulator = &mut chip[A];
128                let aux = *accumulator ^ value;
129                let (value, carry) = accumulator.0.overflowing_add(value.0.wrapping_add(carry_in as raw::u8));
130                let value = Wrapping(value);
131                *accumulator = value;
132                *chip.update_flags() = carry;
133                chip.a = (value ^ aux).0 & 0x10 != 0;
134                7
135            }
136            And{from} => {
137                let (value, time) = byte!{chip, from, bus, 4, 7};
138                AndWith{value}.execute_on(chip, bus)?;
139                time
140            }
141            AndWith { value } => {
142                chip[A] &= value;
143                *chip.update_flags() = false;
144                7
145            }
146            Call{sub} => {
147                bus.write_word(chip.push(), chip.pc);
148                chip.pc = sub;
149                17
150            }
151            CallIf(test, sub) => if test.approves(chip) {
152                Call{sub}.execute_on(chip, bus)?;
153                17
154            } else {
155                11
156            }
157            CarryFlag(set) => {
158                chip.c = set || !chip.c;
159                4
160            }
161            Compare{from} => {
162                let (value, time) = byte!{chip, from, bus, 4, 7};
163                CompareWith { value }.execute_on(chip, bus)?;
164                time
165            }
166            CompareWith{value} => {
167                let (value, carry, aux) = subtract(chip[A], value);
168                *chip.update_flags_for(value) = carry;
169                chip.a = aux;
170                7
171            }
172            ComplementAccumulator => {
173                chip[A] = !chip[A];
174                4
175            }
176            DecimalAddAdjust => {
177                let aux = if chip[A].0  & 0x0F > 0x09 {
178                    chip[A] += 0x06;
179                    true
180                } else {
181                    if chip.a { chip[A] = chip[A] + Wrapping(6); }
182                    false
183                };
184                let carry = if chip[A] >> 4 > Wrapping(0x09) {
185                    chip[A] += 0x06 << 4;
186                    true
187                } else {
188                    if chip.c { chip[A] += 0x06 << 4; }
189                    false
190                };
191                *chip.update_flags() = carry;
192                chip.a = aux;
193                4
194            }
195            DecrementByte { register } => {
196                let (value, time) = match chip.resolve(register) {
197                    Single(reg) => { chip[reg] -= 1; (chip[reg], 5)}
198                    Byte::RAM(address) => {
199                        let value = bus.read(address) - Wrapping(1);
200                        bus.write(address, value);
201                        (value, 10)
202                    }
203                    _ => unreachable!()
204                };
205                *chip.update_flags_for(value) = false;
206                chip.a = (value ^ (value + Wrapping(1))).0 & 0x10 != 0;
207                time
208            }
209            DecrementWord{register} => {
210                chip[register] -= 1;
211                5
212            }
213            DoubleAdd { register } => {
214                let (value, carry) = chip[HL].0.overflowing_add(chip[register].0);
215                (chip[HL], chip.c) = (Wrapping(value), carry);
216                10
217            }
218            ExchangeDoubleWithHilo => {
219                (chip[DE], chip[HL]) = (chip[HL], chip[DE]);
220                5
221            }
222            ExchangeTopWithHilo => {
223                let out = chip[HL];
224                chip[HL] = bus.read_word(chip.sp);
225                bus.write_word(chip.sp, out);
226                18
227            }
228            ExclusiveOr { from } => {
229                let (value, time) = byte!(chip, from, bus, 4, 7);
230                ExclusiveOrWith{value}.execute_on(chip, bus)?;
231                time
232            }
233            ExclusiveOrWith { value } => {
234                chip[A] ^= value;
235                *chip.update_flags() = false;
236                7
237            }
238            Halt => {
239                chip.active = false;
240                7
241            }
242            In(port) => {
243                chip[A] = bus.input(port);
244                10
245            }
246            IncrementByte { register } => {
247                let (value, time) = match chip.resolve(register) {
248                    Single(reg) => { chip[reg] += 1; (chip[reg], 5)}
249                    Byte::RAM(address) => {
250                        let value = bus.read(address) + Wrapping(1);
251                        bus.write(address, value);
252                        (value, 10)
253                    }
254                    _ => unreachable!()
255                };
256                *chip.update_flags_for(value) = false;
257                chip.a = (value ^ (value - Wrapping(1))).0 & 0x10 != 0;
258                time
259            }
260            IncrementWord { register } => {
261                chip[register] += 1;
262                5
263            }
264            Interrupts(active) => {
265                chip.interrupts = active;
266                4
267            }
268            Jump{to} => {
269                chip.pc = to;
270                10
271            }
272            JumpIf(test, addr) => {
273                if test.approves(chip) { chip.pc = addr; }
274                10
275            }
276            LoadAccumulator{address} => {
277                chip[A] = bus.read(address);
278                13
279            }
280            LoadAccumulatorIndirect { register } => {
281                chip[A] = bus.read(chip[register]);
282                7
283            }
284            LoadExtendedWith { to, value } => {
285                chip[to] = value;
286                10
287            }
288            LoadHilo{address} => {
289                chip[HL] = bus.read_word(address);
290                16
291            }
292            Move{to, from} => {
293                let (to, from) = (chip.resolve(to), chip.resolve(from));
294                match (to, from) {
295                    (Single(to), Single(from)) => {
296                        chip[to] = chip[from];
297                        5
298                    }
299                    (Byte::RAM(address), Single(from)) => {
300                        bus.write(address, chip[from]);
301                        7
302                    }
303                    (Single(to), Byte::RAM(address)) => {
304                        chip[to] = bus.read(address);
305                        7
306                    }
307                    _ => unreachable!()
308                }
309            }
310            MoveData { value, to } => {
311                match chip.resolve(to) {
312                    Single(register) => { chip[register] = value; 7 },
313                    Byte::RAM(address) => { bus.write(address, value); 10},
314                    _ => unreachable!()
315                }
316            }
317            Or{from} => {
318                let (value, time) = byte!{chip, from, bus, 4, 7};
319                OrWith{value}.execute_on(chip, bus)?;
320                time
321            }
322            OrWith{value} => {
323                chip[A] |= value;
324                *chip.update_flags() = false;
325                7
326            }
327            Out(port) => {
328                bus.output(port, chip[A]);
329                10
330            }
331            Pop(target) => {
332                match target {
333                    OnBoard(internal) => chip[internal] = bus.read_word(chip.pop()),
334                    ProgramStatus => {
335                        let [accumulator, status] = bus.read_word(chip.pop()).0.to_le_bytes();
336                        chip[A] = Wrapping(accumulator);
337                        chip.extract_flags(status);
338                    }
339                    _ => unreachable!()
340                };
341                10
342            }
343            ProgramCounterFromHilo => {
344                chip[ProgramCounter] = chip[HL];
345                5
346            }
347            Push (source) => {
348                let source = match source {
349                    OnBoard(internal) => chip[internal],
350                    ProgramStatus => chip.status(),
351                    _ => unreachable!()
352                };
353                bus.write_word(chip.push(), source);
354                11
355            }
356            Reset{vector} => {
357                bus.write_word(chip.push(), chip.pc);
358                chip.pc = Wrapping(vector as raw::u16 * 8);
359                11
360            }
361            Return => {
362                chip.pc = bus.read_word(chip.pop());
363                10
364            }
365            ReturnIf(test) => {
366                if test.approves(chip) {
367                    Return.execute_on(chip, bus)?;
368                    11
369                } else {
370                    5
371                }
372            }
373            RotateAccumulatorLeft => {
374                let bits = chip[A].0 as raw::u16 | if chip.c { 0x8000 } else { 0x0000 };
375                let [bits, carry] = bits.rotate_left(1).to_le_bytes();
376                chip.c = carry != 0;
377                chip[A] = Wrapping(bits);
378                4
379            }
380            RotateAccumulatorRight => {
381                let bits = chip[A].0 as raw::u16 | if chip.c { 0x0100 } else { 0x0000 };
382                let [bits, carry] = bits.rotate_right(1).to_le_bytes();
383                chip.c = carry != 0;
384                chip[A] = Wrapping(bits);
385                4
386            }
387            RotateLeftCarrying => {
388                let accumulator = chip[A].0;
389                chip.c = accumulator & 0x80 != 0;
390                chip[A] = Wrapping(accumulator.rotate_left(1));
391                4
392            }
393            RotateRightCarrying => {
394                let accumulator = chip[A].0;
395                chip.c = accumulator & 0x01 != 0;
396                chip[A] = Wrapping(accumulator.rotate_right(1));
397                4
398            }
399            StackPointerFromHilo => {
400                chip[StackPointer] = chip[HL];
401                5
402            }
403            StoreAccumulator { address } => {
404                bus.write(address, chip[A]);
405                13
406            }
407            StoreAccumulatorIndirect { register } => {
408                bus.write(chip[register], chip[A]);
409                7
410            }
411            StoreHilo{ address } => {
412                bus.write_word(address, chip[HL]);
413                16
414            }
415            Subtract { from, carry } => {
416                let (value, time) = byte!{chip, from, bus, 4, 7};
417                SubtractBy{value, carry}.execute_on(chip, bus)?;
418                time
419            }
420            SubtractBy{ value, carry } => {
421                let (value, carry, aux) = subtract(chip[A], value + Wrapping((chip.c && carry) as raw::u8));
422                chip[A] = value;
423                *chip.update_flags() = carry;
424                chip.a = aux;
425                7
426            }
427            NOP(n) => n,
428            #[cfg(debug_assertions)]
429            _ => unimplemented!("Op {self:?} not implemented yet")
430        };
431        let cycles = NonZeroU8::new(cycles);
432        #[cfg(feature="open")]
433        let cycles = Ok(cycles);
434        cycles
435    }
436}
437
438#[cfg(test)]
439mod tests;