1
+ using System . Buffers . Binary ;
1
2
using System . Collections . Generic ;
3
+ using System . Runtime . InteropServices ;
2
4
5
+ using BizHawk . Common ;
3
6
using BizHawk . Emulation . Common ;
4
7
5
8
namespace BizHawk . Emulation . Cores . Consoles . Nintendo . N3DS
@@ -28,6 +31,9 @@ private void InitMemoryDomains()
28
31
domains . Add ( _n3dsExRam ) ;
29
32
}
30
33
34
+ // extra domain for virtual memory (important for dealing with pointers!)
35
+ domains . Add ( new EncoreMMU ( _context ) ) ;
36
+
31
37
_memoryDomains = new MemoryDomainList ( domains ) ;
32
38
_serviceProvider . Register ( _memoryDomains ) ;
33
39
WireMemoryDomains ( ) ;
@@ -47,5 +53,223 @@ void WireDomain(LibEncore.MemoryRegion region, MemoryDomainIntPtr domain)
47
53
WireDomain ( LibEncore . MemoryRegion . DSP , _dspRam ) ;
48
54
WireDomain ( LibEncore . MemoryRegion . N3DS , _n3dsExRam ) ;
49
55
}
56
+
57
+ private class EncoreMMU : MemoryDomain
58
+ {
59
+ private const uint ENCORE_PAGE_SIZE = 0x1000 ;
60
+ private const uint ENCORE_PAGE_MASK = ENCORE_PAGE_SIZE - 1 ;
61
+
62
+ private readonly IntPtr _context ;
63
+
64
+ public EncoreMMU ( IntPtr context )
65
+ {
66
+ Name = "System Bus" ;
67
+ Size = 1L << 32 ;
68
+ WordSize = 4 ;
69
+ EndianType = Endian . Little ;
70
+ Writable = true ;
71
+ _context = context ;
72
+ }
73
+
74
+ private Span < byte > GetPage ( uint addr )
75
+ {
76
+ var pagePointer = _core . Encore_GetPagePointer ( _context , addr ) ;
77
+ return pagePointer == IntPtr . Zero ? [ ] : Util. UnsafeSpanFromPointer ( pagePointer , ( int ) ( ENCORE_PAGE_SIZE - ( addr & ENCORE_PAGE_MASK ) ) ) ;
78
+ }
79
+
80
+ public override byte PeekByte ( long addr )
81
+ {
82
+ var page = GetPage ( ( uint ) addr ) ;
83
+ return page . IsEmpty ? ( byte ) 0 : page [ 0 ] ;
84
+ }
85
+
86
+ public override ushort PeekUshort ( long addr , bool bigEndian )
87
+ {
88
+ // if we cross a page boundary, we need to read multiple pages
89
+ if ( ( addr & ENCORE_PAGE_MASK ) > ENCORE_PAGE_MASK - 1 )
90
+ {
91
+ return base . PeekUshort ( addr , bigEndian ) ;
92
+ }
93
+
94
+ var page = GetPage ( ( uint ) addr ) ;
95
+ if ( page . IsEmpty )
96
+ {
97
+ return 0 ;
98
+ }
99
+
100
+ return bigEndian
101
+ ? BinaryPrimitives . ReadUInt16BigEndian ( page )
102
+ : BinaryPrimitives . ReadUInt16LittleEndian ( page ) ;
103
+ }
104
+
105
+ public override uint PeekUint ( long addr , bool bigEndian )
106
+ {
107
+ // if we cross a page boundary, we need to read multiple pages
108
+ if ( ( addr & ENCORE_PAGE_MASK ) > ENCORE_PAGE_MASK - 3 )
109
+ {
110
+ return base . PeekUint ( addr , bigEndian ) ;
111
+ }
112
+
113
+ var page = GetPage ( ( uint ) addr ) ;
114
+ if ( page . IsEmpty )
115
+ {
116
+ return 0 ;
117
+ }
118
+
119
+ return bigEndian
120
+ ? BinaryPrimitives . ReadUInt32BigEndian ( page )
121
+ : BinaryPrimitives . ReadUInt32LittleEndian ( page ) ;
122
+ }
123
+
124
+ public override void PokeByte ( long addr , byte val )
125
+ {
126
+ var page = GetPage ( ( uint ) addr ) ;
127
+ if ( page . IsEmpty )
128
+ {
129
+ return ;
130
+ }
131
+
132
+ page [ 0 ] = val ;
133
+ }
134
+
135
+ public override void PokeUshort ( long addr , ushort val , bool bigEndian )
136
+ {
137
+ // if we cross a page boundary, we need to write to multiple pages
138
+ if ( ( addr & ENCORE_PAGE_MASK ) > ENCORE_PAGE_MASK - 1 )
139
+ {
140
+ base . PokeUshort ( addr , val , bigEndian ) ;
141
+ return ;
142
+ }
143
+
144
+ var page = GetPage ( ( uint ) addr ) ;
145
+ if ( page . IsEmpty )
146
+ {
147
+ return ;
148
+ }
149
+
150
+ if ( bigEndian )
151
+ {
152
+ BinaryPrimitives . WriteUInt16BigEndian ( page , val ) ;
153
+ }
154
+ else
155
+ {
156
+ BinaryPrimitives . WriteUInt16LittleEndian ( page , val ) ;
157
+ }
158
+ }
159
+
160
+ public override void PokeUint ( long addr , uint val , bool bigEndian )
161
+ {
162
+ // if we cross a page boundary, we need to write to multiple pages
163
+ if ( ( addr & ENCORE_PAGE_MASK ) > ENCORE_PAGE_MASK - 3 )
164
+ {
165
+ base . PokeUint ( addr , val , bigEndian ) ;
166
+ return ;
167
+ }
168
+
169
+ var page = GetPage ( ( uint ) addr ) ;
170
+ if ( page . IsEmpty )
171
+ {
172
+ return ;
173
+ }
174
+
175
+ if ( bigEndian )
176
+ {
177
+ BinaryPrimitives . WriteUInt32BigEndian ( page , val ) ;
178
+ }
179
+ else
180
+ {
181
+ BinaryPrimitives . WriteUInt32LittleEndian ( page , val ) ;
182
+ }
183
+ }
184
+
185
+ private void BulkPeekByte ( uint startAddr , Span < byte > values )
186
+ {
187
+ while ( ! values . IsEmpty )
188
+ {
189
+ var page = GetPage ( startAddr ) ;
190
+ var numBytes = Math . Min ( values . Length , ( int ) ( ENCORE_PAGE_SIZE - ( startAddr & ENCORE_PAGE_MASK ) ) ) ;
191
+ if ( page . IsEmpty )
192
+ {
193
+ values [ ..numBytes ] . Clear ( ) ;
194
+ }
195
+ else
196
+ {
197
+ page [ ..numBytes ] . CopyTo ( values ) ;
198
+ }
199
+
200
+ values = values [ numBytes ..] ;
201
+ startAddr += ( uint ) numBytes ;
202
+ }
203
+ }
204
+
205
+ public override void BulkPeekByte ( Range < long > addresses , byte [ ] values )
206
+ {
207
+ if ( addresses is null ) throw new ArgumentNullException ( paramName : nameof ( addresses ) ) ;
208
+ if ( values is null ) throw new ArgumentNullException ( paramName : nameof ( values ) ) ;
209
+
210
+ if ( ( long ) addresses . Count ( ) != values . Length )
211
+ {
212
+ throw new InvalidOperationException ( "Invalid length of values array" ) ;
213
+ }
214
+
215
+ BulkPeekByte ( ( uint ) addresses . Start , values ) ;
216
+ }
217
+
218
+ public override void BulkPeekUshort ( Range < long > addresses , bool bigEndian , ushort [ ] values )
219
+ {
220
+ if ( addresses is null ) throw new ArgumentNullException ( paramName : nameof ( addresses ) ) ;
221
+ if ( values is null ) throw new ArgumentNullException ( paramName : nameof ( values ) ) ;
222
+
223
+ var start = addresses . Start ;
224
+ var end = addresses . EndInclusive + 1 ;
225
+
226
+ if ( ( start & 1 ) != 0 || ( end & 1 ) != 0 )
227
+ throw new InvalidOperationException ( "The API contract doesn't define what to do for unaligned reads and writes!" ) ;
228
+
229
+ if ( values . LongLength * 2 != end - start )
230
+ {
231
+ // a longer array could be valid, but nothing needs that so don't support it for now
232
+ throw new InvalidOperationException ( "Invalid length of values array" ) ;
233
+ }
234
+
235
+ BulkPeekByte ( ( uint ) addresses . Start , MemoryMarshal . AsBytes ( values . AsSpan ( ) ) ) ;
236
+
237
+ if ( ! bigEndian )
238
+ {
239
+ for ( var i = 0 ; i < values . Length ; i ++ )
240
+ {
241
+ values [ i ] = BinaryPrimitives . ReverseEndianness ( values [ i ] ) ;
242
+ }
243
+ }
244
+ }
245
+
246
+ public override void BulkPeekUint ( Range < long > addresses , bool bigEndian , uint [ ] values )
247
+ {
248
+ if ( addresses is null ) throw new ArgumentNullException ( paramName : nameof ( addresses ) ) ;
249
+ if ( values is null ) throw new ArgumentNullException ( paramName : nameof ( values ) ) ;
250
+
251
+ var start = addresses . Start ;
252
+ var end = addresses . EndInclusive + 1 ;
253
+
254
+ if ( ( start & 3 ) != 0 || ( end & 3 ) != 0 )
255
+ throw new InvalidOperationException ( "The API contract doesn't define what to do for unaligned reads and writes!" ) ;
256
+
257
+ if ( values . LongLength * 4 != end - start )
258
+ {
259
+ // a longer array could be valid, but nothing needs that so don't support it for now
260
+ throw new InvalidOperationException ( "Invalid length of values array" ) ;
261
+ }
262
+
263
+ BulkPeekByte ( ( uint ) addresses . Start , MemoryMarshal . AsBytes ( values . AsSpan ( ) ) ) ;
264
+
265
+ if ( ! bigEndian )
266
+ {
267
+ for ( var i = 0 ; i < values . Length ; i ++ )
268
+ {
269
+ values [ i ] = BinaryPrimitives . ReverseEndianness ( values [ i ] ) ;
270
+ }
271
+ }
272
+ }
273
+ }
50
274
}
51
275
}
0 commit comments