Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve disk IO speeds, optimize FAT Implementation memory allocation & speeds #2841

Merged
merged 15 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions source/Cosmos.Core/IOPort.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ public static class IOPort
[PlugMethod(PlugRequired = true)]
public static void Write8(int aPort, byte aData) => throw null;

/// <summary>
/// Write many bytes to port with 400ns waits between each word
/// Plugged.
/// </summary>
/// <param name="aPort">A port to write to.</param>
/// <param name="aData">The data.</param>
[PlugMethod(PlugRequired = true)]
public static void WriteMany8WithWait(int aPort, byte[] aData) => throw null;

/// <summary>
/// Write Word to port.
/// Plugged.
Expand Down Expand Up @@ -69,16 +78,9 @@ public static class IOPort
/// </summary>
/// <param name="aData">Output data array.</param>
/// <exception cref="System.OverflowException">Thrown if aData lenght is greater than Int32.MaxValue.</exception>
public static void Read8(int aPort, byte[] aData)
{
for (int i = 0; i < aData.Length / 2; i++)
{
var xValue = Read16(aPort);
aData[i * 2] = (byte)xValue;
aData[i * 2 + 1] = (byte)(xValue >> 8);
}
}

[PlugMethod(PlugRequired = true)]
public static void Read8(int aPort, byte[] aData) => throw null;

/// <summary>
/// Read Word from base port.
/// </summary>
Expand Down
63 changes: 62 additions & 1 deletion source/Cosmos.Core_Asm/IOPortImpl.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Security.Cryptography;
using Cosmos.Core;

using IL2CPU.API;
using IL2CPU.API.Attribs;

using XSharp;
Expand Down Expand Up @@ -31,6 +32,37 @@ public override void AssembleNew(Assembler aAssembler, object aMethodInfo)

#endregion

#region WriteMany8WithWait (many)

private class WriteMany8WithWaitAssembler : AssemblerMethod {
public override void AssembleNew(Assembler aAssembler, object aMethodInfo) {
// the port index is in EBP+8
// the reference to the byte array is in EBP+12
XS.Set(XSRegisters.EDX, XSRegisters.EBP, sourceDisplacement: 16); // EDX = Port (ebp+16)
XS.Set(XSRegisters.ECX, XSRegisters.EBP, sourceDisplacement: 12); // ECX = Pointer to array (ebp+12)

XS.Lea(XSRegisters.ESI, XSRegisters.ECX, sourceDisplacement: 16); // ESI = Data* (ecx+16)
XS.Set(XSRegisters.EBX, XSRegisters.ECX, sourceDisplacement: 8); // EBX = Length (ecx+8)

XS.Label(".loop");
XS.Set(XSRegisters.AX, XSRegisters.ESI, sourceIsIndirect: true); // ax = *esi
XS.WriteToPortDX(XSRegisters.AX);
XS.LiteralCode("out 0x80, al");
XS.LiteralCode("out 0x80, al");
XS.LiteralCode("out 0x80, al");
XS.LiteralCode("out 0x80, al"); // Wait 400 ns between each word
XS.Add(XSRegisters.ESI, 2); // esi++

XS.Sub(XSRegisters.EBX, 2); // ebx--
XS.Jump(XSharp.Assembler.x86.ConditionalTestEnum.NotZero, ".loop"); // if (ebx != 0) goto .loop
}
}

[PlugMethod(Assembler = typeof(WriteMany8WithWaitAssembler))]
public static void WriteMany8WithWait(ushort aPort, byte[] aData) => throw null;

#endregion

#region Write16

private class Write16Assembler : AssemblerMethod
Expand Down Expand Up @@ -85,6 +117,35 @@ public override void AssembleNew(Assembler aAssembler, object aMethodInfo)

#endregion

#region Read8 (many)

private class Read8AssemblerMany : AssemblerMethod
{
public override void AssembleNew(Assembler aAssembler, object aMethodInfo)
{
// the port index is in EBP+16
// the reference to the byte array is in EBP+12
XS.Set(XSRegisters.EDX, XSRegisters.EBP, sourceDisplacement: 16); // EDX = Port (ebp+16)
XS.Set(XSRegisters.ECX, XSRegisters.EBP, sourceDisplacement: 12); // ECX = Pointer to array (ebp+12)

XS.Lea(XSRegisters.ESI, XSRegisters.ECX, sourceDisplacement: 16); // ESI = Data* (ecx+16)
XS.Set(XSRegisters.EBX, XSRegisters.ECX, sourceDisplacement: 8); // EBX = Length (ecx+8)

XS.Label(".loop");
XS.ReadFromPortDX(XSRegisters.AX);
XS.Set(XSRegisters.ESI, XSRegisters.AX, destinationIsIndirect: true); // *esi = ax
XS.Add(XSRegisters.ESI, 2); // esi++

XS.Sub(XSRegisters.EBX, 2); // ebx--
XS.Jump(XSharp.Assembler.x86.ConditionalTestEnum.NotZero, ".loop"); // if (ebx != 0) goto .loop
}
}

[PlugMethod(Assembler = typeof(Read8AssemblerMany))]
public static void Read8(ushort aPort, byte[] aData) => throw null;

#endregion

#region Read16

private class Read16Assembler : AssemblerMethod
Expand Down
15 changes: 3 additions & 12 deletions source/Cosmos.HAL2/BlockDevice/ATA_PIO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ public override void ReadBlock(ulong aBlockNo, ulong aBlockCount, ref byte[] aDa
SelectSector(aBlockNo, aBlockCount);
SendCmd(LBA48Bit ? Cmd.ReadPioExt : Cmd.ReadPio);
IOPort.Read8(IO.Data, aData);
}
}

/// <summary>
/// Writes the specific block of data using the starting block,
Expand All @@ -420,18 +420,9 @@ public override void WriteBlock(ulong aBlockNo, ulong aBlockCount, ref byte[] aD
SelectSector(aBlockNo, aBlockCount);
SendCmd(LBA48Bit ? Cmd.WritePioExt : Cmd.WritePio);

ushort xValue;

for (long i = 0; i < aData.Length / 2; i++)
{
xValue = (ushort)((aData[i * 2 + 1] << 8) | aData[i * 2]);
IOPort.Write16(IO.Data, xValue);
Wait();
// There must be a tiny delay between each OUTSW output word. A jmp $+2 size of delay.
// But that delay is cpu specific? so how long of a delay?
}
IOPort.WriteMany8WithWait(IO.Data, aData);

SendCmd(Cmd.CacheFlush);
SendCmd(Cmd.CacheFlush);
}

/// <summary>
Expand Down
54 changes: 27 additions & 27 deletions source/Cosmos.System2/FileSystem/FAT/FatFileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ internal class Fat

private readonly ulong mFatSector;

/// <summary>
/// A reused buffer for <see cref="GetFatEntry(uint, out uint)"/>s read operations to save on allocations.
/// </summary>
private byte[] _getFatEntryReadBuffer;

/// <summary>
/// Initializes a new instance of the <see cref="Fat"/> class.
/// </summary>
Expand All @@ -43,6 +48,7 @@ public Fat(FatFileSystem aFileSystem, ulong aFatSector)

mFileSystem = aFileSystem;
mFatSector = aFatSector;
_getFatEntryReadBuffer = mFileSystem.NewBlockArray();
}

/// <summary>
Expand Down Expand Up @@ -133,9 +139,11 @@ public uint[] GetFatChain(uint aFirstEntry, long aDataSize = 0)
if (xEntriesRequired > xReturn.Length)
{
long xNewClusters = xEntriesRequired - xReturn.Length;
uint prevFoundEntry = 0;
for (int i = 0; i < xNewClusters; i++)
{
xCurrentEntry = GetNextUnallocatedFatEntry();
xCurrentEntry = GetNextUnallocatedFatEntry(prevFoundEntry);
prevFoundEntry = xCurrentEntry;
mFileSystem.Write(xCurrentEntry, new byte[mFileSystem.BytesPerCluster]);
uint xLastFatEntry = xReturn[xReturn.Length - 1];
SetFatEntry(xLastFatEntry, xCurrentEntry);
Expand Down Expand Up @@ -175,12 +183,12 @@ public uint[] GetFatChain(uint aFirstEntry, long aDataSize = 0)
/// <exception cref="ArgumentNullException">Thrown on fatal error.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown on fatal error.</exception>
/// <exception cref="NotSupportedException">Thrown when FAT type is unknown.</exception>
public uint GetNextUnallocatedFatEntry()
public uint GetNextUnallocatedFatEntry(uint startOffset = 0)
{
Global.Debugger.SendInternal("-- Fat.GetNextUnallocatedFatEntry --");

uint xTotalEntries = mFileSystem.FatSectorCount * mFileSystem.BytesPerSector / GetFatEntrySizeInBytes();
for (uint i = mFileSystem.RootCluster + 1; i < xTotalEntries; i++)
for (uint i = mFileSystem.RootCluster + 1 + startOffset; i < xTotalEntries; i++)
{
GetFatEntry(i, out uint xEntryValue);
if (xEntryValue == 0) // check if fat entry is free
Expand Down Expand Up @@ -271,8 +279,8 @@ public void ClearAllFat()
Global.Debugger.SendInternal($"RootCluster is {mFileSystem.RootCluster}");
Global.Debugger.SendInternal("Clearing all Fat Table");

byte[] xFatTableFirstSector;
ReadFatSector(0, out xFatTableFirstSector);
byte[] xFatTableFirstSector = mFileSystem.NewBlockArray();
ReadFatSector(0, ref xFatTableFirstSector);

/* Change 3rd entry (RootDirectory) to be EOC */
SetValueInFat(2, FatEntryEofValue(), xFatTableFirstSector);
Expand Down Expand Up @@ -311,10 +319,8 @@ public void ClearAllFat()
/// <param name="aData">Output data byte.</param>
/// <exception cref="OverflowException">Thrown when data lenght is greater then Int32.MaxValue.</exception>
/// <exception cref="Exception">Thrown when data size invalid.</exception>
private void ReadFatSector(ulong aSector, out byte[] aData)
{
private void ReadFatSector(ulong aSector, ref byte[] aData) {
Global.Debugger.SendInternal("-- FatFileSystem.ReadFatSector --");
aData = mFileSystem.NewBlockArray();
ulong xSector = mFatSector + aSector;
Global.Debugger.SendInternal("xSector =" + xSector);
mFileSystem.Device.ReadBlock(xSector, mFileSystem.SectorsPerCluster, ref aData);
Expand Down Expand Up @@ -364,15 +370,15 @@ internal void GetFatEntry(uint aEntryNumber, out uint aValue)
ulong xSector = xEntryOffset / mFileSystem.BytesPerSector;
Global.Debugger.SendInternal("xSector = " + xSector);

ReadFatSector(xSector, out byte[] xData);
ReadFatSector(xSector, ref _getFatEntryReadBuffer);

switch (mFileSystem.mFatType)
{
case FatTypeEnum.Fat12:
// We now access the FAT entry as a WORD just as we do for FAT16, but if the cluster number is
// EVEN, we only want the low 12-bits of the 16-bits we fetch. If the cluster number is ODD
// we want the high 12-bits of the 16-bits we fetch.
uint xResult = BitConverter.ToUInt16(xData, (int)xEntryOffset);
uint xResult = BitConverter.ToUInt16(_getFatEntryReadBuffer, (int)xEntryOffset);
if ((aEntryNumber & 0x01) == 0)
{
aValue = xResult & 0x0FFF; // Even
Expand All @@ -384,12 +390,12 @@ internal void GetFatEntry(uint aEntryNumber, out uint aValue)
break;

case FatTypeEnum.Fat16:
aValue = BitConverter.ToUInt16(xData, (int)xEntryOffset);
aValue = BitConverter.ToUInt16(_getFatEntryReadBuffer, (int)xEntryOffset);
break;

case FatTypeEnum.Fat32:
int localOffset = (int)(xEntryOffset % mFileSystem.BytesPerSector);
aValue = BitConverter.ToUInt32(xData, localOffset) & 0x0FFFFFFF;
aValue = BitConverter.ToUInt32(_getFatEntryReadBuffer, localOffset) & 0x0FFFFFFF;
break;

default:
Expand Down Expand Up @@ -420,8 +426,8 @@ internal void SetFatEntry(ulong aEntryNumber, ulong aValue)
ulong xSector = xEntryOffset / mFileSystem.BytesPerSector;
int localOffset = (int)(xEntryOffset % mFileSystem.BytesPerSector);

byte[] xData;
ReadFatSector(xSector, out xData);
byte[] xData = mFileSystem.NewBlockArray();
ReadFatSector(xSector, ref xData);

switch (mFileSystem.mFatType)
{
Expand All @@ -442,6 +448,7 @@ internal void SetFatEntry(ulong aEntryNumber, ulong aValue)
}

WriteFatSector(xSector, xData);
GCImplementation.Free(xData);
Global.Debugger.SendInternal("Returning from --- Fat.SetFatEntry ---");
}

Expand All @@ -468,8 +475,8 @@ internal void SetFatEntry(ulong aEntryNumber, byte[] aData, uint aOffset, uint a
ulong xSector = xEntryOffset / mFileSystem.BytesPerSector;
ulong xSectorOffset = xSector * mFileSystem.BytesPerSector - xEntryOffset;

byte[] xData;
ReadFatSector(xSectorOffset, out xData);
byte[] xData = mFileSystem.NewBlockArray();
ReadFatSector(xSectorOffset, ref xData);

switch (mFileSystem.mFatType)
{
Expand Down Expand Up @@ -893,22 +900,20 @@ internal byte[] NewBlockArray()
/// <param name="aData">A data array to write the output to.</param>
/// <exception cref="OverflowException">Thrown when data lenght is greater then Int32.MaxValue.</exception>
/// <exception cref="Exception">Thrown when data size invalid.</exception>
internal void Read(long aCluster, out byte[] aData)
internal void Read(long aCluster, ref byte[] aData)
{
Global.Debugger.SendInternal("-- FatFileSystem.Read --");
Global.Debugger.SendInternal($"aCluster = {aCluster}");

if (mFatType == FatTypeEnum.Fat32)
{
aData = NewBlockArray();
long xSector = DataSector + (aCluster - RootCluster) * SectorsPerCluster;
Global.Debugger.SendInternal($"xSector = {xSector}");
Device.ReadBlock((ulong)xSector, SectorsPerCluster, ref aData);
}
else
{
Global.Debugger.SendInternal("aCluster: " + aCluster);
aData = Device.NewBlockArray(1);
Device.ReadBlock((ulong)aCluster, RootSectorCount, ref aData);
}
Global.Debugger.SendInternal($"aData.Length = {aData.Length}");
Expand Down Expand Up @@ -957,21 +962,16 @@ internal void Write(long aCluster, byte[] aData, long aSize = 0, long aOffset =
{
aSize = BytesPerCluster;
}

byte[] xData;
Read(aCluster, out xData);


Array.Copy(aData, 0, xData, aOffset, aSize);


if (mFatType == FatTypeEnum.Fat32)
{
long xSector = DataSector + (aCluster - RootCluster) * SectorsPerCluster;
Device.WriteBlock((ulong)xSector, SectorsPerCluster, ref xData);
Device.WriteBlock((ulong)xSector, SectorsPerCluster, ref aData);
}
else
{
Device.WriteBlock((ulong)aCluster, RootSectorCount, ref xData);
Device.WriteBlock((ulong)aCluster, RootSectorCount, ref aData);
}
}

Expand Down
10 changes: 7 additions & 3 deletions source/Cosmos.System2/FileSystem/FAT/FatStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

using System;
using System.IO;

using Cosmos.Core;
using Cosmos.System.FileSystem.FAT.Listing;

namespace Cosmos.System.FileSystem.FAT
Expand Down Expand Up @@ -280,12 +280,13 @@ public override int Read(byte[] aBuffer, int aOffset, int aCount)
}

long xClusterSize = mFS.BytesPerCluster;
byte[] xCluster = mFS.NewBlockArray();

while (xCount > 0)
{
long xClusterIdx = mPosition / xClusterSize;
long xPosInCluster = mPosition % xClusterSize;
mFS.Read(mFatTable[(int)xClusterIdx], out byte[] xCluster);
mFS.Read(mFatTable[(int)xClusterIdx], ref xCluster);
long xReadSize;
if (xPosInCluster + xCount > xClusterSize)
{
Expand Down Expand Up @@ -394,6 +395,7 @@ public override void Write(byte[] aBuffer, int aOffset, int aCount)
SetLength(xTotalLength);
}

byte[] xCluster = mFS.NewBlockArray();
while (xCount > 0)
{
long xWriteSize;
Expand All @@ -408,7 +410,7 @@ public override void Write(byte[] aBuffer, int aOffset, int aCount)
xWriteSize = xCount;
}

mFS.Read(mFatTable[xClusterIdx], out byte[] xCluster);
mFS.Read(mFatTable[xClusterIdx], ref xCluster);
Array.Copy(aBuffer, aOffset, xCluster, (int)xPosInCluster, (int)xWriteSize);
mFS.Write(mFatTable[xClusterIdx], xCluster);

Expand All @@ -424,6 +426,8 @@ public override void Write(byte[] aBuffer, int aOffset, int aCount)
aOffset += (int)xWriteSize;
mPosition += xWriteSize;
}

GCImplementation.Free(xCluster);
}
}
}
Loading