monistode_emulator/
stack.rs

1use num_traits::{WrappingAdd, WrappingSub};
2
3use super::memory::{DoublablePrecision, TwoByteMemory};
4use std::{
5    marker::PhantomData,
6    ops::{Index, IndexMut},
7};
8
9// All of this file's magic fuckery is just for you to be able to do this:
10// TwoByteStack::new(&mut self.memory, &mut self.registers.sp, true).push(0)
11
12pub trait Stack<T> {
13    fn push(&mut self, value: T);
14    fn pop(&mut self) -> T;
15    fn peek(&self) -> T;
16    fn peek_down_by(&self, n: u8) -> T;
17}
18
19pub struct TwoByteStack<'a, T: 'a, U, V>
20where
21    T: IndexMut<V>,
22    U: Into<V>,
23    T::Output: DoublablePrecision + Sized,
24{
25    data: TwoByteMemory<'a, T, U, V>,
26    pointer: &'a mut U,
27    downward: bool,
28    index: PhantomData<V>,
29}
30
31impl<'a, T, U, V> TwoByteStack<'a, T, U, V>
32where
33    T: IndexMut<V>,
34    U: Into<V> + From<u8> + WrappingAdd<Output = U> + WrappingSub<Output = U> + Copy,
35    T::Output: DoublablePrecision + Sized + Copy,
36{
37    #[inline]
38    pub fn new(data: &'a mut T, pointer: &'a mut U, downward: bool) -> Self {
39        TwoByteStack {
40            data: TwoByteMemory::new(data),
41            pointer,
42            downward,
43            index: PhantomData,
44        }
45    }
46}
47
48impl<'a, T: 'a, U, V> Stack<<<T as Index<V>>::Output as DoublablePrecision>::DoublePrecision>
49    for TwoByteStack<'a, T, U, V>
50where
51    T: IndexMut<V>,
52    U: Into<V> + From<u8> + WrappingAdd<Output = U> + WrappingSub<Output = U> + Copy,
53    T::Output: DoublablePrecision + Sized + Copy,
54{
55    #[inline]
56    fn push(&mut self, value: <T::Output as DoublablePrecision>::DoublePrecision) {
57        if self.downward {
58            *self.pointer = (*self.pointer).wrapping_sub(&U::from(2));
59        } else {
60            *self.pointer = (*self.pointer).wrapping_add(&U::from(2));
61        }
62        self.data.write(*self.pointer, value);
63    }
64
65    #[inline]
66    fn pop(&mut self) -> <T::Output as DoublablePrecision>::DoublePrecision {
67        let result = self.data.read(*self.pointer);
68        if self.downward {
69            *self.pointer = (*self.pointer).wrapping_add(&U::from(2));
70        } else {
71            *self.pointer = (*self.pointer).wrapping_sub(&U::from(2));
72        }
73        result
74    }
75
76    #[inline]
77    fn peek(&self) -> <T::Output as DoublablePrecision>::DoublePrecision {
78        self.data.read(*self.pointer)
79    }
80
81    #[inline]
82    fn peek_down_by(&self, offset: u8) -> <T::Output as DoublablePrecision>::DoublePrecision {
83        let mut pointer = *self.pointer;
84        if self.downward {
85            pointer = pointer.wrapping_add(&U::from(offset));
86        } else {
87            pointer = pointer.wrapping_sub(&U::from(offset));
88        }
89        self.data.read(pointer)
90    }
91}
92
93macro_rules! two_byte_stack {
94    // two_byte_stack!(memory_stack[registers.sp: u16] -> u8, basd on data_memory);
95    // two_byte_stack!(memory_stack[registers.sp: u16] -> u8, based on data_memory, growing downward);
96    ($name:ident[$register:ident: $indextype:ty] -> $itemtype:ty, based on $memory:ident) => {
97        #[inline]
98        pub fn $name(&mut self) -> TwoByteStack<'_, Memory<$itemtype>, $indextype, usize> {
99            TwoByteStack::new(&mut self.$memory, &mut self.registers.$register, false)
100        }
101    };
102    ($name:ident[$register:ident: $indextype:ty] -> $itemtype:ty, based on $memory:ident, growing downward) => {
103        #[inline]
104        pub fn $name(&mut self) -> TwoByteStack<'_, Memory<$itemtype>, $indextype, usize> {
105            TwoByteStack::new(&mut self.$memory, &mut self.registers.$register, true)
106        }
107    };
108}
109
110pub(crate) use two_byte_stack;