Start working on a primitive mmap implementation. It's good enough for VirtualBoyee to boot, but undoubtedly has many bugs.
This commit is contained in:
parent
7d84946daa
commit
855ff7deca
|
@ -96,6 +96,9 @@ namespace BizHawk.Client.ApiHawk
|
||||||
case "WSWAN":
|
case "WSWAN":
|
||||||
return CoreSystem.WonderSwan;
|
return CoreSystem.WonderSwan;
|
||||||
|
|
||||||
|
case "VB":
|
||||||
|
return 0; // like I give a shit
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IndexOutOfRangeException(string.Format("{0} is missing in convert list", value));
|
throw new IndexOutOfRangeException(string.Format("{0} is missing in convert list", value));
|
||||||
}
|
}
|
||||||
|
|
|
@ -331,6 +331,10 @@ namespace BizHawk.Emulation.Common
|
||||||
case ".DO":
|
case ".DO":
|
||||||
game.System = "AppleII";
|
game.System = "AppleII";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ".VB":
|
||||||
|
game.System = "VB";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
game.Name = Path.GetFileNameWithoutExtension(fileName)?.Replace('_', ' ');
|
game.Name = Path.GetFileNameWithoutExtension(fileName)?.Replace('_', ' ');
|
||||||
|
|
|
@ -1279,6 +1279,7 @@
|
||||||
<Compile Include="Libretro\LibretroCore_Description.cs" />
|
<Compile Include="Libretro\LibretroCore_Description.cs" />
|
||||||
<Compile Include="Libretro\LibretroCore_InputCallbacks.cs" />
|
<Compile Include="Libretro\LibretroCore_InputCallbacks.cs" />
|
||||||
<Compile Include="Waterbox\Heap.cs" />
|
<Compile Include="Waterbox\Heap.cs" />
|
||||||
|
<Compile Include="Waterbox\MapHeap.cs" />
|
||||||
<Compile Include="Waterbox\MemoryBlock.cs" />
|
<Compile Include="Waterbox\MemoryBlock.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Sound\CDAudio.cs" />
|
<Compile Include="Sound\CDAudio.cs" />
|
||||||
|
|
|
@ -25,10 +25,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
||||||
{
|
{
|
||||||
Path = comm.CoreFileProvider.DllPath(),
|
Path = comm.CoreFileProvider.DllPath(),
|
||||||
Filename = "vb.wbx",
|
Filename = "vb.wbx",
|
||||||
NormalHeapSizeKB = 1024,
|
NormalHeapSizeKB = 4 * 1024,
|
||||||
SealedHeapSizeKB = 12 * 1024,
|
SealedHeapSizeKB = 12 * 1024,
|
||||||
InvisibleHeapSizeKB = 6 * 1024,
|
InvisibleHeapSizeKB = 6 * 1024,
|
||||||
SpecialHeapSizeKB = 64
|
SpecialHeapSizeKB = 64,
|
||||||
|
MmapHeapSizeKB = 16 * 1024
|
||||||
});
|
});
|
||||||
|
|
||||||
_boyee = BizInvoker.GetInvoker<LibVirtualBoyee>(_exe, _exe);
|
_boyee = BizInvoker.GetInvoker<LibVirtualBoyee>(_exe, _exe);
|
||||||
|
@ -112,22 +113,22 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
|
||||||
|
|
||||||
#region IVideoProvider
|
#region IVideoProvider
|
||||||
|
|
||||||
private int[] _videoBuffer = new int[0];
|
private int[] _videoBuffer = new int[256 * 192];
|
||||||
|
|
||||||
public int[] GetVideoBuffer()
|
public int[] GetVideoBuffer()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _videoBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int VirtualWidth { get; private set; }
|
public int VirtualWidth { get; private set; } = 256;
|
||||||
public int VirtualHeight { get; private set; }
|
public int VirtualHeight { get; private set; } = 192;
|
||||||
|
|
||||||
public int BufferWidth { get; private set; }
|
public int BufferWidth { get; private set; } = 256;
|
||||||
public int BufferHeight { get; private set; }
|
public int BufferHeight { get; private set; } = 192;
|
||||||
|
|
||||||
public int VsyncNumerator { get; private set; }
|
public int VsyncNumerator { get; private set; } = 60;
|
||||||
|
|
||||||
public int VsyncDenominator { get; private set; }
|
public int VsyncDenominator { get; private set; } = 1;
|
||||||
|
|
||||||
public int BackgroundColor => unchecked((int)0xff000000);
|
public int BackgroundColor => unchecked((int)0xff000000);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,303 @@
|
||||||
|
using BizHawk.Emulation.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// a heap that supports basic alloc, free, and realloc calls
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class MapHeap : IBinaryStateable, IDisposable
|
||||||
|
{
|
||||||
|
public MemoryBlock Memory { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// name, used in identifying errors
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
public byte[] XorHash { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// get a page index within the block
|
||||||
|
/// </summary>
|
||||||
|
private int GetPage(ulong addr)
|
||||||
|
{
|
||||||
|
return (int)((addr - Memory.Start) >> WaterboxUtils.PageShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// get a start address for a page index within the block
|
||||||
|
/// </summary>
|
||||||
|
private ulong GetStartAddr(int page)
|
||||||
|
{
|
||||||
|
return ((ulong)page << WaterboxUtils.PageShift) + Memory.Start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Bin
|
||||||
|
{
|
||||||
|
public int StartPage;
|
||||||
|
public int PageCount;
|
||||||
|
public MemoryBlock.Protection Protection;
|
||||||
|
public bool Free
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (byte)Protection == 255;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Protection = value ? (MemoryBlock.Protection)255 : MemoryBlock.Protection.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bin Next;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// split this bin, keeping only numPages pages
|
||||||
|
/// </summary>
|
||||||
|
public bool Cleave(int numPages)
|
||||||
|
{
|
||||||
|
int nextPages = PageCount - numPages;
|
||||||
|
if (nextPages > 0)
|
||||||
|
{
|
||||||
|
Next = new Bin
|
||||||
|
{
|
||||||
|
StartPage = StartPage + numPages,
|
||||||
|
PageCount = nextPages,
|
||||||
|
Next = Next
|
||||||
|
};
|
||||||
|
PageCount = numPages;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bin _root;
|
||||||
|
|
||||||
|
public MapHeap(ulong start, ulong size, string name)
|
||||||
|
{
|
||||||
|
size = WaterboxUtils.AlignUp(size);
|
||||||
|
Memory = new MemoryBlock(start, size);
|
||||||
|
Name = name;
|
||||||
|
Console.WriteLine("Created mapheap `{1}` at {0:x16}:{2:x16}", start, name, start + size);
|
||||||
|
|
||||||
|
_root = new Bin
|
||||||
|
{
|
||||||
|
StartPage = 0,
|
||||||
|
PageCount = (int)(size >> WaterboxUtils.PageShift),
|
||||||
|
Free = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// gets the bin that contains a page
|
||||||
|
/// </summary>
|
||||||
|
private Bin GetBinForStartPage(int page)
|
||||||
|
{
|
||||||
|
Bin curr = _root;
|
||||||
|
while (curr.StartPage + curr.PageCount <= page)
|
||||||
|
curr = curr.Next;
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// gets the bin that contains the page before the passed page, returning null if
|
||||||
|
/// any bin along the way is Free
|
||||||
|
/// </summary>
|
||||||
|
private Bin GetBinForEndPageEnsureAllocated(int page, Bin start)
|
||||||
|
{
|
||||||
|
Bin curr = start;
|
||||||
|
while (curr != null && curr.StartPage + curr.PageCount < page)
|
||||||
|
{
|
||||||
|
if (curr.Free)
|
||||||
|
return null;
|
||||||
|
curr = curr.Next;
|
||||||
|
}
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong Map(ulong size, MemoryBlock.Protection prot)
|
||||||
|
{
|
||||||
|
int numPages = WaterboxUtils.PagesNeeded(size);
|
||||||
|
Bin best = null;
|
||||||
|
Bin curr = _root;
|
||||||
|
|
||||||
|
// find smallest potential bin
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (curr.Free && curr.PageCount >= numPages)
|
||||||
|
{
|
||||||
|
if (best == null || curr.PageCount < best.PageCount)
|
||||||
|
{
|
||||||
|
best = curr;
|
||||||
|
if (curr.PageCount == numPages)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curr = curr.Next;
|
||||||
|
} while (curr != null);
|
||||||
|
|
||||||
|
if (best == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (best.Cleave(numPages))
|
||||||
|
best.Next.Free = true;
|
||||||
|
best.Protection = prot;
|
||||||
|
|
||||||
|
var ret = GetStartAddr(best.StartPage);
|
||||||
|
Memory.Protect(ret, ((ulong)numPages) << WaterboxUtils.PageShift, prot);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong Remap(ulong start, ulong oldSize, ulong newSize, bool canMove)
|
||||||
|
{
|
||||||
|
if (start < Memory.Start || start + oldSize > Memory.End)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var oldStartPage = GetPage(start);
|
||||||
|
var oldStartBin = GetBinForStartPage(oldStartPage);
|
||||||
|
if (oldSize == 0 && canMove)
|
||||||
|
return Map(newSize, oldStartBin.Protection);
|
||||||
|
|
||||||
|
var oldNumPages = WaterboxUtils.PagesNeeded(oldSize);
|
||||||
|
var oldEndPage = oldStartPage + oldNumPages;
|
||||||
|
// first, check if the requested area is actually mapped
|
||||||
|
var oldEndBin = GetBinForEndPageEnsureAllocated(oldEndPage, oldStartBin);
|
||||||
|
if (oldEndBin == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var newNumPages = WaterboxUtils.PagesNeeded(newSize);
|
||||||
|
var newEndPage = oldStartPage + newNumPages;
|
||||||
|
if (newEndPage > oldEndPage)
|
||||||
|
{
|
||||||
|
// increase size
|
||||||
|
// the only way this will work in place is if all of the remaining space is free
|
||||||
|
Bin nextBin;
|
||||||
|
if (oldEndBin.StartPage + oldEndBin.PageCount == oldEndPage // if end bin is too bag, space after that is used by something else
|
||||||
|
&& (nextBin = oldEndBin.Next) != null // can't go off the edge
|
||||||
|
&& nextBin.Free
|
||||||
|
&& nextBin.StartPage + nextBin.PageCount >= newEndPage)
|
||||||
|
{
|
||||||
|
nextBin.Protection = oldStartBin.Protection;
|
||||||
|
if (nextBin.Cleave(newEndPage - nextBin.StartPage))
|
||||||
|
nextBin.Next.Free = true;
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
// could not increase in place, so move
|
||||||
|
if (!canMove)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// if there's some free space right before `start`, and some right after, but not enough
|
||||||
|
// to extend in place, it's possible that a realloc would succeed reusing the same space,
|
||||||
|
// but would fail anywhere else due to heavy memory pressure.
|
||||||
|
|
||||||
|
// that would be a much more complicated algorithm; we'd need to compute a new allocation
|
||||||
|
// as if this one had been freed, but still be able to preserve this if that allocation
|
||||||
|
// still failed. instead, we ignore this case.
|
||||||
|
var ret = Map(newSize, oldStartBin.Protection);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
// move data
|
||||||
|
// NB: oldSize > 0
|
||||||
|
Memory.Protect(start, oldSize, MemoryBlock.Protection.R);
|
||||||
|
var ss = Memory.GetStream(start, oldSize, false);
|
||||||
|
Memory.Protect(ret, oldSize, MemoryBlock.Protection.RW);
|
||||||
|
var ds = Memory.GetStream(ret, oldSize, true);
|
||||||
|
ss.CopyTo(ds);
|
||||||
|
Memory.Protect(ret, oldSize, oldStartBin.Protection);
|
||||||
|
UnmapPagesInternal(oldStartPage, oldNumPages, oldStartBin);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (newEndPage < oldEndPage)
|
||||||
|
{
|
||||||
|
// shrink in place
|
||||||
|
var s = GetBinForStartPage(newEndPage);
|
||||||
|
UnmapPagesInternal(newEndPage, oldEndPage - newEndPage, s);
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no change
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unmap(ulong start, ulong size)
|
||||||
|
{
|
||||||
|
if (start < Memory.Start || start + size > Memory.End)
|
||||||
|
return false;
|
||||||
|
if (size == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var startPage = GetPage(start);
|
||||||
|
var numPages = WaterboxUtils.PagesNeeded(size);
|
||||||
|
var endPage = startPage + numPages;
|
||||||
|
// check to see if the requested area is actually mapped
|
||||||
|
var startBin = GetBinForStartPage(startPage);
|
||||||
|
if (GetBinForEndPageEnsureAllocated(endPage, startBin) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
UnmapPagesInternal(startPage, numPages, startBin);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// frees some pages. assumes they are all allocated
|
||||||
|
/// </summary>
|
||||||
|
private void UnmapPagesInternal(int startPage, int numPages, Bin startBin)
|
||||||
|
{
|
||||||
|
// from the various paths we took to get here, we must be unmapping at least one page
|
||||||
|
|
||||||
|
var endPage = startPage + numPages;
|
||||||
|
Bin freeBin = startBin;
|
||||||
|
if (!freeBin.Free && freeBin.StartPage != startPage)
|
||||||
|
{
|
||||||
|
freeBin.Cleave(startPage - freeBin.StartPage);
|
||||||
|
freeBin = freeBin.Next;
|
||||||
|
freeBin.Free = true;
|
||||||
|
}
|
||||||
|
MemoryBlock.Protection lastEaten = MemoryBlock.Protection.None;
|
||||||
|
while (freeBin.StartPage + freeBin.PageCount < endPage)
|
||||||
|
{
|
||||||
|
freeBin.PageCount += freeBin.Next.PageCount;
|
||||||
|
lastEaten = freeBin.Next.Protection;
|
||||||
|
freeBin.Next = freeBin.Next.Next;
|
||||||
|
}
|
||||||
|
if (freeBin.Cleave(freeBin.StartPage + freeBin.PageCount - endPage))
|
||||||
|
{
|
||||||
|
freeBin.Next.Protection = lastEaten;
|
||||||
|
}
|
||||||
|
Memory.Protect(GetStartAddr(startPage), ((ulong)numPages) << WaterboxUtils.PageShift, MemoryBlock.Protection.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Memory != null)
|
||||||
|
{
|
||||||
|
Memory.Dispose();
|
||||||
|
Memory = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveStateBinary(BinaryWriter writer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadStateBinary(BinaryReader reader)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,96 +7,8 @@ using System.IO;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Waterbox
|
namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
// C# is annoying: arithmetic operators for native ints are not exposed.
|
|
||||||
// So we store them as long/ulong instead in many places, and use these helpers
|
|
||||||
// to convert to IntPtr when needed
|
|
||||||
|
|
||||||
public static class Z
|
|
||||||
{
|
|
||||||
public static IntPtr US(ulong l)
|
|
||||||
{
|
|
||||||
if (IntPtr.Size == 8)
|
|
||||||
return (IntPtr)(long)l;
|
|
||||||
else
|
|
||||||
return (IntPtr)(int)l;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UIntPtr UU(ulong l)
|
|
||||||
{
|
|
||||||
if (UIntPtr.Size == 8)
|
|
||||||
return (UIntPtr)l;
|
|
||||||
else
|
|
||||||
return (UIntPtr)(uint)l;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr SS(long l)
|
|
||||||
{
|
|
||||||
if (IntPtr.Size == 8)
|
|
||||||
return (IntPtr)l;
|
|
||||||
else
|
|
||||||
return (IntPtr)(int)l;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UIntPtr SU(long l)
|
|
||||||
{
|
|
||||||
if (UIntPtr.Size == 8)
|
|
||||||
return (UIntPtr)(ulong)l;
|
|
||||||
else
|
|
||||||
return (UIntPtr)(uint)l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class MemoryBlock : IDisposable
|
public sealed class MemoryBlock : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// system page size
|
|
||||||
/// </summary>
|
|
||||||
public static int PageSize { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// bitshift corresponding to PageSize
|
|
||||||
/// </summary>
|
|
||||||
private static readonly int PageShift;
|
|
||||||
/// <summary>
|
|
||||||
/// bitmask corresponding to PageSize
|
|
||||||
/// </summary>
|
|
||||||
private static readonly ulong PageMask;
|
|
||||||
|
|
||||||
static MemoryBlock()
|
|
||||||
{
|
|
||||||
int p = PageSize = Environment.SystemPageSize;
|
|
||||||
while (p != 1)
|
|
||||||
{
|
|
||||||
p >>= 1;
|
|
||||||
PageShift++;
|
|
||||||
}
|
|
||||||
PageMask = (ulong)(PageSize - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// true if addr is aligned
|
|
||||||
/// </summary>
|
|
||||||
private static bool Aligned(ulong addr)
|
|
||||||
{
|
|
||||||
return (addr & PageMask) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// align address down to previous page boundary
|
|
||||||
/// </summary>
|
|
||||||
private static ulong AlignDown(ulong addr)
|
|
||||||
{
|
|
||||||
return addr & ~PageMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// align address up to next page boundary
|
|
||||||
/// </summary>
|
|
||||||
private static ulong AlignUp(ulong addr)
|
|
||||||
{
|
|
||||||
return ((addr - 1) | PageMask) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// starting address of the memory block
|
/// starting address of the memory block
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -140,7 +52,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
if (addr < Start || addr >= End)
|
if (addr < Start || addr >= End)
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
return (int)((addr - Start) >> PageShift);
|
return (int)((addr - Start) >> WaterboxUtils.PageShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -148,7 +60,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ulong GetStartAddr(int page)
|
private ulong GetStartAddr(int page)
|
||||||
{
|
{
|
||||||
return ((ulong)page << PageShift) + Start;
|
return ((ulong)page << WaterboxUtils.PageShift) + Start;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -167,11 +79,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// <param name="size"></param>
|
/// <param name="size"></param>
|
||||||
public MemoryBlock(ulong start, ulong size)
|
public MemoryBlock(ulong start, ulong size)
|
||||||
{
|
{
|
||||||
if (!Aligned(start))
|
if (!WaterboxUtils.Aligned(start))
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
size = AlignUp(size);
|
size = WaterboxUtils.AlignUp(size);
|
||||||
|
|
||||||
_handle = Kernel32.CreateFileMapping(Kernel32.INVALID_HANDLE_VALUE, IntPtr.Zero,
|
_handle = Kernel32.CreateFileMapping(Kernel32.INVALID_HANDLE_VALUE, IntPtr.Zero,
|
||||||
Kernel32.FileMapProtection.PageExecuteReadWrite | Kernel32.FileMapProtection.SectionCommit, (uint)(size >> 32), (uint)size, null);
|
Kernel32.FileMapProtection.PageExecuteReadWrite | Kernel32.FileMapProtection.SectionCommit, (uint)(size >> 32), (uint)size, null);
|
||||||
|
@ -325,10 +237,13 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
if (Active) // it's legal to Protect() if we're not active; the information is just saved for the next activation
|
if (Active) // it's legal to Protect() if we're not active; the information is just saved for the next activation
|
||||||
{
|
{
|
||||||
// TODO: if using another OS's memory protection calls, they must give the same non-aligned behavior
|
var computedStart = WaterboxUtils.AlignDown(start);
|
||||||
// as VirtualProtect, or this must be changed
|
var computedEnd = WaterboxUtils.AlignUp(start + length);
|
||||||
|
var computedLength = computedEnd - computedStart;
|
||||||
|
|
||||||
Kernel32.MemoryProtection old;
|
Kernel32.MemoryProtection old;
|
||||||
if (!Kernel32.VirtualProtect(Z.UU(start), Z.UU(length), p, out old))
|
if (!Kernel32.VirtualProtect(Z.UU(computedStart),
|
||||||
|
Z.UU(computedLength), p, out old))
|
||||||
throw new InvalidOperationException("VirtualProtect() returned FALSE!");
|
throw new InvalidOperationException("VirtualProtect() returned FALSE!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,23 +26,33 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// how large the normal heap should be. it services sbrk calls
|
/// how large the normal heap should be. it services sbrk calls
|
||||||
|
/// can be 0, but sbrk calls will crash.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint NormalHeapSizeKB { get; set; }
|
public uint NormalHeapSizeKB { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// how large the sealed heap should be. it services special allocations that become readonly after init
|
/// how large the sealed heap should be. it services special allocations that become readonly after init
|
||||||
|
/// Must be > 0 and at least large enough to store argv and envp
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint SealedHeapSizeKB { get; set; }
|
public uint SealedHeapSizeKB { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// how large the invisible heap should be. it services special allocations which are not savestated
|
/// how large the invisible heap should be. it services special allocations which are not savestated
|
||||||
|
/// Must be > 0 and at least large enough for the internal vtables
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint InvisibleHeapSizeKB { get; set; }
|
public uint InvisibleHeapSizeKB { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// how large the special heap should be. it is savestated, and contains ??
|
/// how large the special heap should be. it is savestated, and contains ??
|
||||||
|
/// Must be > 0 and at least large enough for the internal pthread structure
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint SpecialHeapSizeKB { get; set; }
|
public uint SpecialHeapSizeKB { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// how large the mmap heap should be. it is savestated.
|
||||||
|
/// can be 0, but mmap calls will crash.
|
||||||
|
/// </summary>
|
||||||
|
public uint MmapHeapSizeKB { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -385,6 +395,57 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
time.NanoSeconds = 0;
|
time.NanoSeconds = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n9")]
|
||||||
|
public IntPtr MMap(IntPtr address, UIntPtr size, int prot, int flags, int fd, IntPtr offs)
|
||||||
|
{
|
||||||
|
if (address != IntPtr.Zero)
|
||||||
|
return Z.SS(-1);
|
||||||
|
MemoryBlock.Protection mprot;
|
||||||
|
switch (prot)
|
||||||
|
{
|
||||||
|
case 0: mprot = MemoryBlock.Protection.None; break;
|
||||||
|
default:
|
||||||
|
case 6: // W^X
|
||||||
|
case 7: // W^X
|
||||||
|
case 4: // exec only????
|
||||||
|
case 2: return Z.SS(-1); // write only????
|
||||||
|
case 3: mprot = MemoryBlock.Protection.RW; break;
|
||||||
|
case 1: mprot = MemoryBlock.Protection.R; break;
|
||||||
|
case 5: mprot = MemoryBlock.Protection.RX; break;
|
||||||
|
}
|
||||||
|
if ((flags & 0x20) == 0)
|
||||||
|
{
|
||||||
|
// MAP_ANONYMOUS is required
|
||||||
|
return Z.SS(-1);
|
||||||
|
}
|
||||||
|
if ((flags & 0xf00) != 0)
|
||||||
|
{
|
||||||
|
// various unsupported flags
|
||||||
|
return Z.SS(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = _parent._mmapheap.Map((ulong)size, mprot);
|
||||||
|
return ret == 0 ? Z.SS(-1) : Z.US(ret);
|
||||||
|
}
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n25")]
|
||||||
|
public IntPtr MRemap(UIntPtr oldAddress, UIntPtr oldSize,
|
||||||
|
UIntPtr newSize, int flags)
|
||||||
|
{
|
||||||
|
if ((flags & 2) != 0)
|
||||||
|
{
|
||||||
|
// don't support MREMAP_FIXED
|
||||||
|
return Z.SS(-1);
|
||||||
|
}
|
||||||
|
var ret = _parent._mmapheap.Remap((ulong)oldAddress, (ulong)oldSize, (ulong)newSize,
|
||||||
|
(flags & 1) != 0);
|
||||||
|
return ret == 0 ? Z.SS(-1) : Z.US(ret);
|
||||||
|
}
|
||||||
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "n11")]
|
||||||
|
public int MUnmap(UIntPtr address, UIntPtr size)
|
||||||
|
{
|
||||||
|
return _parent._mmapheap.Unmap((ulong)address, (ulong)size) ? 0 : -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -511,6 +572,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Heap _specheap;
|
private Heap _specheap;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// memory map emulation
|
||||||
|
/// </summary>
|
||||||
|
private MapHeap _mmapheap;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// all loaded PE files
|
/// all loaded PE files
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -547,6 +613,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
private readonly long _createstamp = WaterboxUtils.Timestamp();
|
private readonly long _createstamp = WaterboxUtils.Timestamp();
|
||||||
|
|
||||||
private Heap CreateHeapHelper(uint sizeKB, string name, bool saveStated)
|
private Heap CreateHeapHelper(uint sizeKB, string name, bool saveStated)
|
||||||
|
{
|
||||||
|
if (sizeKB != 0)
|
||||||
{
|
{
|
||||||
var heap = new Heap(_nextStart, sizeKB * 1024, name);
|
var heap = new Heap(_nextStart, sizeKB * 1024, name);
|
||||||
heap.Memory.Activate();
|
heap.Memory.Activate();
|
||||||
|
@ -558,6 +626,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
_heaps.Add(heap);
|
_heaps.Add(heap);
|
||||||
return heap;
|
return heap;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
private delegate void LibcEntryRoutineD(IntPtr appMain, IntPtr psxInit, int options);
|
private delegate void LibcEntryRoutineD(IntPtr appMain, IntPtr psxInit, int options);
|
||||||
|
@ -610,6 +683,16 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
_invisibleheap = CreateHeapHelper(opt.InvisibleHeapSizeKB, "invisible-heap", false);
|
_invisibleheap = CreateHeapHelper(opt.InvisibleHeapSizeKB, "invisible-heap", false);
|
||||||
_specheap = CreateHeapHelper(opt.SpecialHeapSizeKB, "special-heap", true);
|
_specheap = CreateHeapHelper(opt.SpecialHeapSizeKB, "special-heap", true);
|
||||||
|
|
||||||
|
if (opt.MmapHeapSizeKB != 0)
|
||||||
|
{
|
||||||
|
_mmapheap = new MapHeap(_nextStart, opt.MmapHeapSizeKB * 1024, "mmap-heap");
|
||||||
|
_mmapheap.Memory.Activate();
|
||||||
|
ComputeNextStart(opt.MmapHeapSizeKB * 1024);
|
||||||
|
AddMemoryBlock(_mmapheap.Memory);
|
||||||
|
_savestateComponents.Add(_mmapheap);
|
||||||
|
_disposeList.Add(_mmapheap);
|
||||||
|
}
|
||||||
|
|
||||||
_syscalls.Init();
|
_syscalls.Init();
|
||||||
|
|
||||||
//System.Diagnostics.Debugger.Break();
|
//System.Diagnostics.Debugger.Break();
|
||||||
|
@ -729,6 +812,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
_sealedheap = null;
|
_sealedheap = null;
|
||||||
_invisibleheap = null;
|
_invisibleheap = null;
|
||||||
_specheap = null;
|
_specheap = null;
|
||||||
|
_mmapheap = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,11 +294,12 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
s.Size);
|
s.Size);
|
||||||
}
|
}
|
||||||
Console.WriteLine("GDB Symbol Load:");
|
Console.WriteLine("GDB Symbol Load:");
|
||||||
Console.WriteLine("add-sym {0} {1} -s .data {2} -s .bss {3}",
|
var symload = $"add-sym {ModuleName} {_sectionsByName[".text"].Start}";
|
||||||
ModuleName,
|
if (_sectionsByName.ContainsKey(".data"))
|
||||||
_sectionsByName[".text"].Start,
|
symload += $" -s .data {_sectionsByName[".data"].Start}";
|
||||||
_sectionsByName[".data"].Start,
|
if (_sectionsByName.ContainsKey(".bss"))
|
||||||
_sectionsByName[".bss"].Start);
|
symload += $" -s .bss {_sectionsByName[".bss"].Start}";
|
||||||
|
Console.WriteLine(symload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntPtr Resolve(string entryPoint)
|
public IntPtr Resolve(string entryPoint)
|
||||||
|
|
|
@ -56,5 +56,101 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
return DateTime.UtcNow.Ticks;
|
return DateTime.UtcNow.Ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// system page size
|
||||||
|
/// </summary>
|
||||||
|
public static int PageSize { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// bitshift corresponding to PageSize
|
||||||
|
/// </summary>
|
||||||
|
public static int PageShift { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// bitmask corresponding to PageSize
|
||||||
|
/// </summary>
|
||||||
|
public static ulong PageMask { get; private set; }
|
||||||
|
|
||||||
|
static WaterboxUtils()
|
||||||
|
{
|
||||||
|
int p = PageSize = Environment.SystemPageSize;
|
||||||
|
while (p != 1)
|
||||||
|
{
|
||||||
|
p >>= 1;
|
||||||
|
PageShift++;
|
||||||
|
}
|
||||||
|
PageMask = (ulong)(PageSize - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// true if addr is aligned
|
||||||
|
/// </summary>
|
||||||
|
public static bool Aligned(ulong addr)
|
||||||
|
{
|
||||||
|
return (addr & PageMask) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// align address down to previous page boundary
|
||||||
|
/// </summary>
|
||||||
|
public static ulong AlignDown(ulong addr)
|
||||||
|
{
|
||||||
|
return addr & ~PageMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// align address up to next page boundary
|
||||||
|
/// </summary>
|
||||||
|
public static ulong AlignUp(ulong addr)
|
||||||
|
{
|
||||||
|
return ((addr - 1) | PageMask) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// return the minimum number of pages needed to hold size
|
||||||
|
/// </summary>
|
||||||
|
public static int PagesNeeded(ulong size)
|
||||||
|
{
|
||||||
|
return (int)((size + PageMask) >> PageShift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// C# is annoying: arithmetic operators for native ints are not exposed.
|
||||||
|
// So we store them as long/ulong instead in many places, and use these helpers
|
||||||
|
// to convert to IntPtr when needed
|
||||||
|
|
||||||
|
public static class Z
|
||||||
|
{
|
||||||
|
public static IntPtr US(ulong l)
|
||||||
|
{
|
||||||
|
if (IntPtr.Size == 8)
|
||||||
|
return (IntPtr)(long)l;
|
||||||
|
else
|
||||||
|
return (IntPtr)(int)l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UIntPtr UU(ulong l)
|
||||||
|
{
|
||||||
|
if (UIntPtr.Size == 8)
|
||||||
|
return (UIntPtr)l;
|
||||||
|
else
|
||||||
|
return (UIntPtr)(uint)l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntPtr SS(long l)
|
||||||
|
{
|
||||||
|
if (IntPtr.Size == 8)
|
||||||
|
return (IntPtr)l;
|
||||||
|
else
|
||||||
|
return (IntPtr)(int)l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UIntPtr SU(long l)
|
||||||
|
{
|
||||||
|
if (UIntPtr.Size == 8)
|
||||||
|
return (UIntPtr)(ulong)l;
|
||||||
|
else
|
||||||
|
return (UIntPtr)(uint)l;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ CCFLAGS:= -I. -I../emulibc \
|
||||||
-Wall -Werror=pointer-to-int-cast -Werror=int-to-pointer-cast -Werror=implicit-function-declaration \
|
-Wall -Werror=pointer-to-int-cast -Werror=int-to-pointer-cast -Werror=implicit-function-declaration \
|
||||||
-std=c++0x -fomit-frame-pointer -fvisibility=hidden -fno-exceptions -fno-rtti \
|
-std=c++0x -fomit-frame-pointer -fvisibility=hidden -fno-exceptions -fno-rtti \
|
||||||
-DLSB_FIRST \
|
-DLSB_FIRST \
|
||||||
-O0
|
-O0 -g
|
||||||
|
|
||||||
TARGET = vb.wbx
|
TARGET = vb.wbx
|
||||||
|
|
||||||
|
@ -29,7 +29,8 @@ $(TARGET).in: $(OBJS)
|
||||||
@$(CC) -o $@ $(LDFLAGS) $(CCFLAGS) $(OBJS) ../emulibc/libemuhost.so
|
@$(CC) -o $@ $(LDFLAGS) $(CCFLAGS) $(OBJS) ../emulibc/libemuhost.so
|
||||||
|
|
||||||
$(TARGET): $(TARGET).in
|
$(TARGET): $(TARGET).in
|
||||||
strip $< -o $@ -R /4 -R /14 -R /29 -R /41 -R /55 -R /67 -R /78 -R /89 -R /104
|
# strip $< -o $@ -R /4 -R /14 -R /29 -R /41 -R /55 -R /67 -R /78 -R /89 -R /104
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJ_DIR)
|
rm -rf $(OBJ_DIR)
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue