@@ -18,9 +18,12 @@ public abstract class WaterboxMemoryDomain : MemoryDomain
1818
1919 public static WaterboxMemoryDomain Create ( MemoryArea m , WaterboxHost monitor )
2020 {
21- return m . Flags . HasFlag ( MemoryDomainFlags . FunctionHook )
22- ? new WaterboxMemoryDomainFunc ( m , monitor )
23- : new WaterboxMemoryDomainPointer ( m , monitor ) ;
21+ if ( m . Flags . HasFlag ( MemoryDomainFlags . FunctionHook ) )
22+ return new WaterboxMemoryDomainFunc ( m , monitor ) ;
23+ else if ( m . Flags . HasFlag ( MemoryDomainFlags . SizedFunctionHooks ) )
24+ return new WaterboxMemoryDomainSizedFuncs ( m , monitor ) ;
25+ else
26+ return new WaterboxMemoryDomainPointer ( m , monitor ) ;
2427 }
2528
2629 protected WaterboxMemoryDomain ( MemoryArea m , IMonitor monitor )
@@ -149,6 +152,33 @@ public static MemoryDomainAccessStub Create(IntPtr p, WaterboxHost host)
149152 new StubResolver ( p ) , host , CallingConventionAdapters . MakeWaterboxDepartureOnly ( host ) ) ;
150153 }
151154 }
155+ public abstract class MemoryDomainSizedAccessStub
156+ {
157+ [ BizImport ( CallingConvention . Cdecl ) ]
158+ public abstract void Access ( IntPtr buffer , long address , long count ) ;
159+
160+ private class StubResolver : IImportResolver
161+ {
162+ private readonly IntPtr _p ;
163+
164+ public StubResolver ( IntPtr p )
165+ {
166+ _p = p ;
167+ }
168+
169+ public IntPtr GetProcAddrOrThrow ( string entryPoint ) => _p ;
170+ public IntPtr GetProcAddrOrZero ( string entryPoint ) => _p ;
171+ }
172+
173+ public static MemoryDomainSizedAccessStub Create ( IntPtr p , WaterboxHost host )
174+ {
175+ return BizInvoker . GetInvoker < MemoryDomainSizedAccessStub > (
176+ new StubResolver ( p ) ,
177+ host ,
178+ CallingConventionAdapters . MakeWaterboxDepartureOnly ( host ) ) ;
179+ }
180+ }
181+
152182
153183 public unsafe class WaterboxMemoryDomainFunc : WaterboxMemoryDomain
154184 {
@@ -202,7 +232,7 @@ public override void BulkPeekByte(Range<long> addresses, byte[] values)
202232
203233 if ( start < ( ulong ) Size && ( start + count ) <= ( ulong ) Size )
204234 {
205- fixed( byte * p = values )
235+ fixed ( byte * p = values )
206236 _access . Access ( ( IntPtr ) p , ( long ) start , ( long ) count , false ) ;
207237 }
208238 else
@@ -211,4 +241,145 @@ public override void BulkPeekByte(Range<long> addresses, byte[] values)
211241 }
212242 }
213243 }
244+
245+ /// <summary>
246+ /// A memory domain for things like a system bus, where the size of the read/write may affect the result.
247+ /// Endianness is ignored; we are writing numbers not multi-byte representations of numbers.
248+ /// </summary>
249+ public unsafe class WaterboxMemoryDomainSizedFuncs : WaterboxMemoryDomain
250+ {
251+ private readonly MemoryDomainSizedAccessStub _read8 = null ;
252+ private readonly MemoryDomainSizedAccessStub _write8 = null ;
253+ private readonly MemoryDomainSizedAccessStub _read16 = null ;
254+ private readonly MemoryDomainSizedAccessStub _write16 = null ;
255+ private readonly MemoryDomainSizedAccessStub _read32 = null ;
256+ private readonly MemoryDomainSizedAccessStub _write32 = null ;
257+
258+ private string AddressRangeError => string . Format ( "Address must be in the range [0, 0x{0:x}]" , Size - 1 ) ;
259+
260+ internal WaterboxMemoryDomainSizedFuncs ( MemoryArea m , WaterboxHost monitor )
261+ : base ( m , monitor )
262+ {
263+ if ( ! m . Flags . HasFlag ( MemoryDomainFlags . SizedFunctionHooks ) )
264+ throw new InvalidOperationException ( ) ;
265+
266+ using ( monitor . EnterExit ( ) )
267+ {
268+ IntPtr * functionPointers = ( IntPtr * ) m . Data ;
269+ for ( int i = 0 ; i < 6 ; i ++ )
270+ {
271+ if ( functionPointers [ i ] == IntPtr . Zero ) throw new Exception ( "All access functions must have implementations." ) ;
272+ }
273+ _read8 = MemoryDomainSizedAccessStub . Create ( functionPointers [ 0 ] , monitor ) ;
274+ _write8 = MemoryDomainSizedAccessStub . Create ( functionPointers [ 1 ] , monitor ) ;
275+
276+ _read16 = MemoryDomainSizedAccessStub . Create ( functionPointers [ 2 ] , monitor ) ;
277+ _write16 = MemoryDomainSizedAccessStub . Create ( functionPointers [ 3 ] , monitor ) ;
278+
279+ _read32 = MemoryDomainSizedAccessStub . Create ( functionPointers [ 4 ] , monitor ) ;
280+ _write32 = MemoryDomainSizedAccessStub . Create ( functionPointers [ 5 ] , monitor ) ;
281+ }
282+ }
283+
284+ public override byte PeekByte ( long addr )
285+ {
286+ if ( ( ulong ) addr >= ( ulong ) Size || addr < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addr ) , message : AddressRangeError ) ;
287+
288+ byte ret = 0 ;
289+ _read8 . Access ( ( IntPtr ) ( & ret ) , addr , 1 ) ;
290+ return ret ;
291+ }
292+
293+ public override void PokeByte ( long addr , byte val )
294+ {
295+ if ( ( ulong ) addr >= ( ulong ) Size || addr < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addr ) , message : AddressRangeError ) ;
296+
297+ _write8 . Access ( ( IntPtr ) ( & val ) , addr , 1 ) ;
298+ }
299+
300+ public override ushort PeekUshort ( long addr , bool bigEndian )
301+ {
302+ if ( ( ulong ) addr + 1 >= ( ulong ) Size || addr < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addr ) , message : AddressRangeError ) ;
303+
304+ ushort ret = 0 ;
305+ _read16 . Access ( ( IntPtr ) ( & ret ) , addr , 1 ) ;
306+ return ret ;
307+ }
308+
309+ public override void PokeUshort ( long addr , ushort val , bool bigEndian )
310+ {
311+ if ( ( ulong ) addr + 1 >= ( ulong ) Size || addr < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addr ) , message : AddressRangeError ) ;
312+
313+ _write16 . Access ( ( IntPtr ) ( & val ) , addr , 1 ) ;
314+ }
315+
316+ public override uint PeekUint ( long addr , bool bigEndian )
317+ {
318+ if ( ( ulong ) addr + 3 >= ( ulong ) Size || addr < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addr ) , message : AddressRangeError ) ;
319+
320+ uint ret = 0 ;
321+ _read32 . Access ( ( IntPtr ) ( & ret ) , addr , 1 ) ;
322+ return ret ;
323+ }
324+
325+ public override void PokeUint ( long addr , uint val , bool bigEndian )
326+ {
327+ if ( ( ulong ) addr + 3 >= ( ulong ) Size || addr < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addr ) , message : AddressRangeError ) ;
328+
329+ _write32 . Access ( ( IntPtr ) ( & val ) , addr , 1 ) ;
330+ }
331+
332+ public override void BulkPeekByte ( Range < long > addresses , byte [ ] values )
333+ {
334+ if ( ( ulong ) addresses . Start + addresses . Count ( ) > ( ulong ) Size || addresses . Start < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addresses ) , message : AddressRangeError ) ;
335+ if ( addresses . Count ( ) > ( uint ) values . Length ) throw new ArgumentException ( message : $ "Length of { nameof ( values ) } must be at least { nameof ( addresses ) } count.", nameof ( values ) ) ;
336+
337+ fixed ( byte * p = values )
338+ _read8 . Access ( ( IntPtr ) p , addresses . Start , ( long ) addresses . Count ( ) ) ;
339+ }
340+
341+ public void BulkPokeByte ( long addr , Span < byte > values )
342+ {
343+ if ( ( ulong ) addr + ( ulong ) values . Length > ( ulong ) Size || addr < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addr ) , message : AddressRangeError ) ;
344+
345+ fixed ( byte * p = values )
346+ _write8 . Access ( ( IntPtr ) p , addr , values . Length ) ;
347+ }
348+
349+ public override void BulkPeekUshort ( Range < long > addresses , bool bigEndian , ushort [ ] values )
350+ {
351+ if ( ( ulong ) addresses . Start + addresses . Count ( ) > ( ulong ) Size || addresses . Start < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addresses ) , message : AddressRangeError ) ;
352+ if ( addresses . Count ( ) > ( uint ) values . Length * sizeof ( ushort ) ) throw new ArgumentException ( message : $ "Length of { nameof ( values ) } must be at least { nameof ( addresses ) } count.", nameof ( values ) ) ;
353+ if ( ( addresses . Count ( ) & 1 ) != 0 ) throw new ArgumentOutOfRangeException ( nameof ( addresses ) , "Requested byte count must be divisible by 2." ) ; // TODO: The caller should be specifying the count of ushorts, not the count of bytes!
354+
355+ fixed ( ushort * p = values )
356+ _read16 . Access ( ( IntPtr ) p , addresses . Start , ( long ) addresses . Count ( ) / sizeof ( ushort ) ) ;
357+ }
358+
359+ public void BulkPokeUshort ( long addr , Span < ushort > values )
360+ {
361+ if ( ( ulong ) addr + ( ulong ) values . Length * sizeof ( ushort ) > ( ulong ) Size || addr < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addr ) , message : AddressRangeError ) ;
362+
363+ fixed ( ushort * p = values )
364+ _write16 . Access ( ( IntPtr ) p , addr , values . Length ) ;
365+ }
366+
367+ public override void BulkPeekUint ( Range < long > addresses , bool bigEndian , uint [ ] values )
368+ {
369+ if ( ( ulong ) addresses . Start + addresses . Count ( ) > ( ulong ) Size || addresses . Start < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addresses ) , message : AddressRangeError ) ;
370+ if ( addresses . Count ( ) > ( uint ) values . Length * sizeof ( uint ) ) throw new ArgumentException ( message : $ "Length of { nameof ( values ) } must be at least { nameof ( addresses ) } count.", nameof ( values ) ) ;
371+ if ( ( addresses . Count ( ) & 3 ) != 0 ) throw new ArgumentOutOfRangeException ( nameof ( addresses ) , "Requested byte count must be divisible by 4." ) ; // TODO: The caller should be specifying the count of uints, not the count of bytes!
372+
373+ fixed ( uint * p = values )
374+ _read32 . Access ( ( IntPtr ) p , addresses . Start , ( long ) addresses . Count ( ) / sizeof ( uint ) ) ;
375+ }
376+
377+ public void BulkPokeUint ( long addr , Span < uint > values )
378+ {
379+ if ( ( ulong ) addr + ( ulong ) values . Length * sizeof ( uint ) > ( ulong ) Size || addr < 0 ) throw new ArgumentOutOfRangeException ( nameof ( addr ) , message : AddressRangeError ) ;
380+
381+ fixed ( uint * p = values )
382+ _write32 . Access ( ( IntPtr ) p , addr , values . Length ) ;
383+ }
384+ }
214385}
0 commit comments