monistode_emulator/
stack.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use num_traits::{WrappingAdd, WrappingSub};

use super::memory::{DoublablePrecision, TwoByteMemory};
use std::{
    marker::PhantomData,
    ops::{Index, IndexMut},
};

// All of this file's magic fuckery is just for you to be able to do this:
// TwoByteStack::new(&mut self.memory, &mut self.registers.sp, true).push(0)

pub trait Stack<T> {
    fn push(&mut self, value: T);
    fn pop(&mut self) -> T;
    fn peek(&self) -> T;
    fn peek_down_by(&self, n: u8) -> T;
}

pub struct TwoByteStack<'a, T: 'a, U, V>
where
    T: IndexMut<V>,
    U: Into<V>,
    T::Output: DoublablePrecision + Sized,
{
    data: TwoByteMemory<'a, T, U, V>,
    pointer: &'a mut U,
    downward: bool,
    index: PhantomData<V>,
}

impl<'a, T, U, V> TwoByteStack<'a, T, U, V>
where
    T: IndexMut<V>,
    U: Into<V> + From<u8> + WrappingAdd<Output = U> + WrappingSub<Output = U> + Copy,
    T::Output: DoublablePrecision + Sized + Copy,
{
    #[inline]
    pub fn new(data: &'a mut T, pointer: &'a mut U, downward: bool) -> Self {
        TwoByteStack {
            data: TwoByteMemory::new(data),
            pointer,
            downward,
            index: PhantomData,
        }
    }
}

impl<'a, T: 'a, U, V> Stack<<<T as Index<V>>::Output as DoublablePrecision>::DoublePrecision>
    for TwoByteStack<'a, T, U, V>
where
    T: IndexMut<V>,
    U: Into<V> + From<u8> + WrappingAdd<Output = U> + WrappingSub<Output = U> + Copy,
    T::Output: DoublablePrecision + Sized + Copy,
{
    #[inline]
    fn push(&mut self, value: <T::Output as DoublablePrecision>::DoublePrecision) {
        if self.downward {
            *self.pointer = (*self.pointer).wrapping_sub(&U::from(2));
        } else {
            *self.pointer = (*self.pointer).wrapping_add(&U::from(2));
        }
        self.data.write(*self.pointer, value);
    }

    #[inline]
    fn pop(&mut self) -> <T::Output as DoublablePrecision>::DoublePrecision {
        let result = self.data.read(*self.pointer);
        if self.downward {
            *self.pointer = (*self.pointer).wrapping_add(&U::from(2));
        } else {
            *self.pointer = (*self.pointer).wrapping_sub(&U::from(2));
        }
        result
    }

    #[inline]
    fn peek(&self) -> <T::Output as DoublablePrecision>::DoublePrecision {
        self.data.read(*self.pointer)
    }

    #[inline]
    fn peek_down_by(&self, offset: u8) -> <T::Output as DoublablePrecision>::DoublePrecision {
        let mut pointer = *self.pointer;
        if self.downward {
            pointer = pointer.wrapping_add(&U::from(offset));
        } else {
            pointer = pointer.wrapping_sub(&U::from(offset));
        }
        self.data.read(pointer)
    }
}

macro_rules! two_byte_stack {
    // two_byte_stack!(memory_stack[registers.sp: u16] -> u8, basd on data_memory);
    // two_byte_stack!(memory_stack[registers.sp: u16] -> u8, based on data_memory, growing downward);
    ($name:ident[$register:ident: $indextype:ty] -> $itemtype:ty, based on $memory:ident) => {
        #[inline]
        pub fn $name(&mut self) -> TwoByteStack<'_, Memory<$itemtype>, $indextype, usize> {
            TwoByteStack::new(&mut self.$memory, &mut self.registers.$register, false)
        }
    };
    ($name:ident[$register:ident: $indextype:ty] -> $itemtype:ty, based on $memory:ident, growing downward) => {
        #[inline]
        pub fn $name(&mut self) -> TwoByteStack<'_, Memory<$itemtype>, $indextype, usize> {
            TwoByteStack::new(&mut self.$memory, &mut self.registers.$register, true)
        }
    };
}

pub(crate) use two_byte_stack;