Cosmos/source/Cosmos.Core.Plugs/MemoryOperations/MemoryOperationsImpl.cs
fanoI db5b713090 CGS is finally ready to be used! Let's merge it...
- To solve the slowness of the MemoryBlock Fill() method when the block has a big size I've created a new class for this MemoryOperations that for now contains only the method Fill() with a part implemented in ASM and with some overloads to semplify its usage from managed code. In future in this class will be added other methods to operate fast on memory for example Cmp() and Copy().
- Adding (not passing) test of the Color struct to the BoxingTest kernel
- Removed BoxingTest kernel inside BCL test: it was a duplicate of the BoxingTest kernel
- Restored correct TestKernelSets
- In the CGS Test kernel the last rectangle is now LimeGreen instead of another type of red (more clear that is working correctly)
- Added to X# the generation of the instruction Shuftps
- Added to X# the generation of the instruction MoveUPS
- Modified Guess Demo to wait for a key press to terminate (it closed suddenly in case the number was guessed)
- The VBE IOMemoryBlock has again size of 1920x1200 (the max usable)
- Added CGS Demo
- Code clean up
2017-03-05 20:32:55 +01:00

187 lines
6.4 KiB
C#

#define COSMOSDEBUG
using System;
using System.Runtime.CompilerServices;
using Cosmos.IL2CPU.Plugs;
//using Cosmos.IL2CPU.Plugs.Assemblers.MemoryOperations;
namespace Cosmos.Core.Plugs.MemoryOperations
{
[Plug(Target = typeof(Cosmos.Core.MemoryOperations))]
public unsafe class MemoryOperationsImpl
{
[PlugMethod(Assembler = typeof(MemoryOperationsFill16BlocksAsm))]
public static unsafe void Fill16Blocks(byte* dest, int value, int BlocksNum)
{
}
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 BlocksNum;
int ByteRemaining;
BlocksNum = Math.DivRem(size, 16, out ByteRemaining);
//Global.mDebugger.SendInternal("size " + size + " is composed of " + BlocksNum + " block of 16 bytes with " + ByteRemaining + " remainder");
for (int i = 0; i < ByteRemaining; i++)
*(dest + i) = (byte)value;
/* Let's call the assembler version now to do the 16 byte block copies */
Fill16Blocks(dest + ByteRemaining, value, BlocksNum);
/*
* 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);
}
}
}
}