mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
187 lines
6.4 KiB
C#
187 lines
6.4 KiB
C#
#define COSMOSDEBUG
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using Cosmos.IL2CPU.Plugs;
|
|
|
|
namespace Cosmos.Core.Plugs.MemoryOperations
|
|
{
|
|
[Plug(Target = typeof(Cosmos.Core.MemoryOperations))]
|
|
public unsafe class MemoryOperationsImpl
|
|
{
|
|
unsafe public static void Fill(byte* dest, int value, int size)
|
|
{
|
|
//Console.WriteLine("Filling array of size " + size + " with value 0x" + value.ToString("X"));
|
|
//Global.mDebugger.SendInternal("Filling array of size " + size + " with value " + value);
|
|
|
|
/* For very little sizes (until 15 bytes) we hand unroll the loop */
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
return;
|
|
|
|
case 1:
|
|
*dest = (byte)value;
|
|
return;
|
|
|
|
case 2:
|
|
*(short*)dest = (short)value;
|
|
return;
|
|
|
|
case 3:
|
|
*(short*)dest = (short)value;
|
|
*(dest + 2) = (byte)value;
|
|
return;
|
|
|
|
case 4:
|
|
*(int*)dest = value;
|
|
return;
|
|
|
|
case 5:
|
|
*(int*)dest = value;
|
|
*(dest + 4) = (byte)value;
|
|
return;
|
|
|
|
case 6:
|
|
*(int*)dest = value;
|
|
*(short*)(dest + 4) = (short)value;
|
|
return;
|
|
|
|
case 7:
|
|
*(int*)dest = value;
|
|
*(short*)(dest + 4) = (short)value;
|
|
*(dest + 6) = (byte)value;
|
|
return;
|
|
|
|
case 8:
|
|
*(int*)dest = value;
|
|
*(int*)(dest + 4) = value;
|
|
return;
|
|
|
|
case 9:
|
|
*(int*)dest = value;
|
|
*(int*)(dest + 4) = value;
|
|
*(dest + 8) = (byte)value;
|
|
return;
|
|
|
|
case 10:
|
|
*(int*)dest = value;
|
|
*(int*)(dest + 4) = value;
|
|
*(short*)(dest + 8) = (short)value;
|
|
return;
|
|
|
|
case 11:
|
|
*(int*)dest = value;
|
|
*(int*)(dest + 4) = value;
|
|
*(short*)(dest + 8) = (short)value;
|
|
*(dest + 10) = (byte)value;
|
|
return;
|
|
|
|
case 12:
|
|
*(int*)dest = value;
|
|
*(int*)(dest + 4) = value;
|
|
*(int*)(dest + 8) = value;
|
|
return;
|
|
|
|
case 13:
|
|
*(int*)dest = value;
|
|
*(int*)(dest + 4) = value;
|
|
*(int*)(dest + 8) = value;
|
|
*(dest + 12) = (byte)value;
|
|
return;
|
|
|
|
case 14:
|
|
*(int*)dest = value;
|
|
*(int*)(dest + 4) = value;
|
|
*(int*)(dest + 8) = value;
|
|
*(short*)(dest + 12) = (byte)value;
|
|
return;
|
|
|
|
case 15:
|
|
*(int*)dest = value;
|
|
*(int*)(dest + 4) = value;
|
|
*(int*)(dest + 8) = value;
|
|
*(short*)(dest + 12) = (short)value;
|
|
*(dest + 14) = (byte)value;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* OK size is >= 16 it does not make any sense to do it with primitive types as C#
|
|
* has not a Int128 type, the Int128 operations will be done in assembler but we can
|
|
* do yet in the Managed world the two things:
|
|
* 1. Check of how many blocks of 16 bytes size is composed
|
|
* 2. If there are reaming bytes (that is size is not a perfect multiple of size)
|
|
* we do the Fill() using a simple managed for() loop of bytes
|
|
*/
|
|
int xBlocksNum;
|
|
int xByteRemaining;
|
|
|
|
#if NETSTANDARD1_5
|
|
xBlocksNum = size / 16;
|
|
xByteRemaining = size % 16;
|
|
#else
|
|
xBlocksNum = Math.DivRem(size, 16, out xByteRemaining);
|
|
#endif
|
|
|
|
//Global.mDebugger.SendInternal("size " + size + " is composed of " + BlocksNum + " block of 16 bytes with " + ByteRemaining + " remainder");
|
|
|
|
for (int i = 0; i < xByteRemaining; i++)
|
|
{
|
|
*(dest + i) = (byte)value;
|
|
}
|
|
|
|
/* Let's call the assembler version now to do the 16 byte block copies */
|
|
Cosmos.Core.MemoryOperations.Fill16Blocks(dest + xByteRemaining, value, xBlocksNum);
|
|
|
|
/*
|
|
* If needed there is yet space of optimization here for example:
|
|
* - you can check if size is a multiple of 64 and if yes use an yet more faster Fill64Blocks
|
|
* - or if it is not so try to see if it is a multiple of 32
|
|
* at point probably it would be better to move a lot of the logic to assembler
|
|
*/
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
unsafe public static void Fill(int[] dest, int value)
|
|
{
|
|
fixed (int* destPtr = dest)
|
|
{
|
|
Fill((byte*)destPtr, value, dest.Length * 4);
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
unsafe public static void Fill(ushort[] dest, ushort value)
|
|
{
|
|
fixed (ushort* destPtr = dest)
|
|
{
|
|
/* Broadcast 'value' fill all the integer register (0x42 --> 0x42424242) */
|
|
int valueFiller = value * 0x10001;
|
|
Fill((byte*)destPtr, valueFiller, dest.Length * 2);
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
unsafe public static void Fill(short[] dest, short value)
|
|
{
|
|
fixed (short* destPtr = dest)
|
|
{
|
|
/* Broadcast 'value' fill all the integer register (0x42 --> 0x42424242) */
|
|
int valueFiller = (ushort)value * 0x10001;
|
|
Fill((byte*)destPtr, valueFiller, dest.Length * 2);
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
unsafe public static void Fill(byte[] dest, byte value)
|
|
{
|
|
fixed (byte* destPtr = dest)
|
|
{
|
|
/* Broadcast 'value' fill all the integer register (0x42 --> 0x42424242) */
|
|
int valueFiller = value * 0x1010101;
|
|
Fill(destPtr, valueFiller, dest.Length);
|
|
}
|
|
}
|
|
}
|
|
}
|