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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
use super::RegVal;
use std::fmt;

////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////

/// A struct to representing an address value.
///
/// Addresses in rv32i are 32-bits wide. We usually write them out in hexadecimal.
///
/// `Addr`s are used in the interface of the [`Memory`](crate::memory::Memory) trait.
/// An `Addr` is used to name the location of a specific byte in the memory space.
///
/// An important operation for operating for working with `Addr`s is adding or
/// subtracting an offset. Several RISC-V instructions take a register together with
/// a small immediate value (for example, `lw`, load word, and `jalr`, jump-and-link return).
/// The value in the register becomes the base address, but the instruction then operates on
/// memory on a small distance away from the base address. And so, several integer types,
/// (including `u32`, `i32`, and [`RegVal`](crate::reg::RegVal) can be added to an `Addr`.
///
/// When using offsets, the order must be: `Addr + offset`.
///
/// It doesn't make sense to add an `Addr` to another `Addr`, so this operation is not provided.
///
/// `Addr`s may be converted to `RegVal`s and vice versa:
///
/// ```
/// let value = RegVal::from_u32(0x2000_0000);
/// let addr: Addr = value.into();
/// let new_value = (addr + 0x0a).into();
/// ```
/// `Addr`s can also be compared with `<`, `<=`, etc. This is useful for certain implementations of
/// [`Memory`](crate::memory::Memory) which would like to store `Addr`s in
/// [`BTreeMap`](https://doc.rust-lang.org/stable/std/collections/struct.BTreeMap.html)s.
///
/// While `Addr` (together with the [`Memory`](crate::memory::Memory) interface) give every byte
/// in memory a unique address, physical hardware often operates with memory on the level of words.
/// That is, it is common that memory might only operate on groups of four bytes. Because of this,
/// some operations will only work when the `Addr` has a proper alignment.
///
/// No restrictions on alignment are given in this version of `riscy`, but this is subject
/// (and likely) to change in future versions.
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct Addr(pub(crate) u32);

impl Addr {
    pub fn new(addr: u32) -> Addr {
        Addr(addr)
    }

    pub(crate) fn align_to_halfword(&self) -> Addr {
        let Addr(addr) = *self;
        Addr(addr & !0x00_00_00_01)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Coversion
////////////////////////////////////////////////////////////////////////////////

impl std::convert::From<Addr> for u32 {
    fn from(addr: Addr) -> u32 {
        addr.0
    }
}

impl std::convert::From<Addr> for usize {
    fn from(addr: Addr) -> usize {
        addr.0 as usize
    }
}

////////////////////////////////////////////////////////////////////////////////
// Display
////////////////////////////////////////////////////////////////////////////////

impl fmt::Display for Addr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Addr(addr) = self;
        let addr_hi = (addr & 0xffff_0000) >> 16;
        let addr_lo = addr & 0x0000_ffff;
        write!(f, "0x{:04x}{:04x}", addr_hi, addr_lo)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Add
////////////////////////////////////////////////////////////////////////////////

impl std::ops::Add<usize> for Addr {
    type Output = Addr;

    fn add(self, offset: usize) -> Self {
        let Addr(here) = self;
        let (there, _did_overflow) = here.overflowing_add(offset as u32);
        Addr(there)
    }
}

impl std::ops::Add<u32> for Addr {
    type Output = Addr;

    fn add(self, offset: u32) -> Self {
        let Addr(here) = self;
        let (there, _did_overflow) = here.overflowing_add(offset);
        Addr(there)
    }
}

impl std::ops::Add<i32> for Addr {
    type Output = Addr;

    fn add(self, offset: i32) -> Self {
        let Addr(here) = self;
        let (there, _did_overflow) = here.overflowing_add(offset as u32);
        Addr(there)
    }
}

impl std::ops::Add<RegVal> for Addr {
    type Output = Addr;

    fn add(self, offset: RegVal) -> Self {
        let Addr(here) = self;
        let RegVal(offset) = offset;
        let (there, _did_overflow) = here.overflowing_add(offset);
        Addr::new(there)
    }
}

////////////////////////////////////////////////////////////////////////////////
// AddAssign
////////////////////////////////////////////////////////////////////////////////

impl std::ops::AddAssign<usize> for Addr {
    fn add_assign(&mut self, offset: usize) {
        let Addr(here) = self;
        let (there, _did_overflow) = here.overflowing_add(offset as u32);
        *self = Addr(there)
    }
}

impl std::ops::AddAssign<u32> for Addr {
    fn add_assign(&mut self, offset: u32) {
        let Addr(here) = self;
        let (there, _did_overflow) = here.overflowing_add(offset);
        *self = Addr(there)
    }
}

impl std::ops::AddAssign<i32> for Addr {
    fn add_assign(&mut self, offset: i32) {
        let Addr(here) = self;
        let (there, _did_overflow) = here.overflowing_add(offset as u32);
        *self = Addr(there)
    }
}

impl std::ops::AddAssign<RegVal> for Addr {
    fn add_assign(&mut self, offset: RegVal) {
        let Addr(here) = self;
        let RegVal(offset) = offset;
        let (there, _did_overflow) = here.overflowing_add(offset);
        *self = Addr(there)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Order
////////////////////////////////////////////////////////////////////////////////

impl std::cmp::PartialOrd for Addr {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.0.partial_cmp(&other.0)
    }
}

impl std::cmp::Ord for Addr {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}