merge latest ares, hook up its new N64DD support, make ares use AxisContraint (see #3453), some other cleanups here
This commit is contained in:
parent
c23b063733
commit
9420c8b21c
Binary file not shown.
|
@ -828,6 +828,8 @@ namespace BizHawk.Client.Common
|
|||
|
||||
public static readonly IReadOnlyCollection<string> N64 = new[] { "z64", "v64", "n64" };
|
||||
|
||||
public static readonly IReadOnlyCollection<string> N64DD = new[] { "ndd" };
|
||||
|
||||
public static readonly IReadOnlyCollection<string> NDS = new[] { "nds" };
|
||||
|
||||
public static readonly IReadOnlyCollection<string> NES = new[] { "nes", "fds", "unf" };
|
||||
|
@ -870,6 +872,7 @@ namespace BizHawk.Client.Common
|
|||
.Concat(Lynx)
|
||||
.Concat(MSX)
|
||||
.Concat(N64)
|
||||
.Concat(N64DD)
|
||||
.Concat(NDS)
|
||||
.Concat(NES)
|
||||
.Concat(NGP)
|
||||
|
@ -898,6 +901,7 @@ namespace BizHawk.Client.Common
|
|||
new FilesystemFilter("PSX Executables (experimental)", Array.Empty<string>(), devBuildExtraExts: new[] { "exe" }),
|
||||
new FilesystemFilter("PSF Playstation Sound File", new[] { "psf", "minipsf" }),
|
||||
new FilesystemFilter("Nintendo 64", RomFileExtensions.N64),
|
||||
new FilesystemFilter("Nintendo 64 Disk Drive", RomFileExtensions.N64DD),
|
||||
new FilesystemFilter("Gameboy", RomFileExtensions.GB.Concat(new[] { "gbs" }).ToList(), addArchiveExts: true),
|
||||
new FilesystemFilter("Gameboy Advance", RomFileExtensions.GBA, addArchiveExts: true),
|
||||
new FilesystemFilter("Nintendo DS", RomFileExtensions.NDS),
|
||||
|
|
|
@ -7,6 +7,7 @@ using BizHawk.Common.PathExtensions;
|
|||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Arcades.MAME;
|
||||
using BizHawk.Emulation.Cores.Atari.Jaguar;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
|
||||
using BizHawk.Emulation.Cores.Consoles.Sega.gpgx;
|
||||
|
@ -286,6 +287,11 @@ namespace BizHawk.Client.Common
|
|||
movie.HeaderEntries.Add("IsJaguarCD", "1");
|
||||
}
|
||||
|
||||
if (emulator is Ares64 ares && ares.IsDD)
|
||||
{
|
||||
movie.HeaderEntries.Add("IsDD", "1");
|
||||
}
|
||||
|
||||
if (emulator is MAME mame)
|
||||
{
|
||||
movie.HeaderEntries.Add(HeaderKeys.VsyncAttoseconds, mame.VsyncAttoseconds.ToString());
|
||||
|
|
|
@ -263,6 +263,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
this.ColecoUseSGMMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.N64SubMenu = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.N64PluginSettingsMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.Ares64SubMenu = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.Ares64SettingsMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.N64ControllerSettingsMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.toolStripSeparator23 = new BizHawk.WinForms.Controls.ToolStripSeparatorEx();
|
||||
this.N64CircularAnalogRangeMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
|
@ -395,6 +397,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
this.SNESSubMenu,
|
||||
this.ColecoSubMenu,
|
||||
this.N64SubMenu,
|
||||
this.Ares64SubMenu,
|
||||
this.GBLSubMenu,
|
||||
this.AppleSubMenu,
|
||||
this.C64SubMenu,
|
||||
|
@ -1755,6 +1758,19 @@ namespace BizHawk.Client.EmuHawk
|
|||
this.N64ExpansionSlotMenuItem.Text = "&Use Expansion Slot";
|
||||
this.N64ExpansionSlotMenuItem.Click += new System.EventHandler(this.N64ExpansionSlotMenuItem_Click);
|
||||
//
|
||||
// Ares64SubMenu
|
||||
//
|
||||
this.Ares64SubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.Ares64SettingsMenuItem,
|
||||
this.N64CircularAnalogRangeMenuItem});
|
||||
this.Ares64SubMenu.Text = "N64";
|
||||
this.Ares64SubMenu.DropDownOpened += new System.EventHandler(this.Ares64SubMenu_DropDownOpened);
|
||||
//
|
||||
// Ares64SettingsMenuItem
|
||||
//
|
||||
this.Ares64SettingsMenuItem.Text = "Settings...";
|
||||
this.Ares64SettingsMenuItem.Click += new System.EventHandler(this.Ares64SettingsMenuItem_Click);
|
||||
//
|
||||
// GBLSubMenu
|
||||
//
|
||||
this.GBLSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
|
@ -2633,6 +2649,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
private BizHawk.WinForms.Controls.ToolStripSeparatorEx toolStripSeparator29;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx N64SubMenu;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx N64PluginSettingsMenuItem;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx Ares64SubMenu;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx Ares64SettingsMenuItem;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx ConfigContextMenuItem;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx RewindOptionsMenuItem;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx FirmwaresMenuItem;
|
||||
|
|
|
@ -1856,6 +1856,14 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
private void Ares64SubMenu_DropDownOpened(object sender, EventArgs e)
|
||||
{
|
||||
N64CircularAnalogRangeMenuItem.Checked = Config.N64UseCircularAnalogConstraint;
|
||||
}
|
||||
|
||||
private void Ares64SettingsMenuItem_Click(object sender, EventArgs e)
|
||||
=> OpenGenericCoreConfigFor<Ares64>(CoreNames.Ares64 + " Settings");
|
||||
|
||||
private DialogResult OpenGambatteLinkSettingsDialog(ISettingsAdapter settable)
|
||||
=> GBLPrefs.DoGBLPrefsDialog(Config, this, Game, MovieSession, settable);
|
||||
|
||||
|
@ -2736,7 +2744,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
items.Add(a7800HawkSubmenu);
|
||||
|
||||
// Ares64
|
||||
items.Add(CreateCoreSubmenu(VSystemCategory.Consoles, CoreNames.Ares64, CreateGenericCoreConfigItem<Ares64>(CoreNames.Ares64)));
|
||||
var ares64AnalogConstraintItem = CreateSettingsItem("Circular Analog Range", N64CircularAnalogRangeMenuItem_Click);
|
||||
var ares64Submenu = CreateCoreSubmenu(VSystemCategory.Consoles, CoreNames.Ares64, CreateGenericCoreConfigItem<Ares64>(CoreNames.Ares64));
|
||||
ares64Submenu.DropDownOpened += (_, _) => ares64AnalogConstraintItem.Checked = Config.N64UseCircularAnalogConstraint;
|
||||
items.Add(ares64Submenu);
|
||||
|
||||
// Atari2600Hawk
|
||||
items.Add(CreateCoreSubmenu(VSystemCategory.Consoles, CoreNames.Atari2600Hawk, CreateGenericCoreConfigItem<Atari2600>(CoreNames.Atari2600Hawk)));
|
||||
|
|
|
@ -27,6 +27,7 @@ using BizHawk.Emulation.Cores;
|
|||
using BizHawk.Emulation.Cores.Arcades.MAME;
|
||||
using BizHawk.Emulation.Cores.Calculators.TI83;
|
||||
using BizHawk.Emulation.Cores.Consoles.NEC.PCE;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
|
||||
using BizHawk.Emulation.Cores.Consoles.SNK;
|
||||
using BizHawk.Emulation.Cores.Nintendo.BSNES;
|
||||
|
@ -2000,6 +2001,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
PSXSubMenu.Visible = false;
|
||||
ColecoSubMenu.Visible = false;
|
||||
N64SubMenu.Visible = false;
|
||||
Ares64SubMenu.Visible = false;
|
||||
GBLSubMenu.Visible = false;
|
||||
AppleSubMenu.Visible = false;
|
||||
C64SubMenu.Visible = false;
|
||||
|
@ -2032,6 +2034,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
case VSystemID.Raw.N64 when Emulator is N64:
|
||||
N64SubMenu.Visible = true;
|
||||
break;
|
||||
case VSystemID.Raw.N64 when Emulator is Ares64:
|
||||
Ares64SubMenu.Visible = true;
|
||||
break;
|
||||
case VSystemID.Raw.NES:
|
||||
NESSubMenu.Visible = true;
|
||||
break;
|
||||
|
|
|
@ -89,6 +89,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
["ChannelF"] = "Channel F",
|
||||
["VEC"] = "Vectrex",
|
||||
["MSX"] = "MSX",
|
||||
["N64DD"] = "N64 Disk Drive",
|
||||
// ["PS2"] = "Sony PlayStation 2",
|
||||
};
|
||||
|
||||
|
|
|
@ -337,6 +337,7 @@ namespace BizHawk.Emulation.Common
|
|||
case ".Z64":
|
||||
case ".V64":
|
||||
case ".N64":
|
||||
case ".NDD":
|
||||
game.System = VSystemID.Raw.N64;
|
||||
break;
|
||||
|
||||
|
|
|
@ -460,6 +460,16 @@ namespace BizHawk.Emulation.Common
|
|||
var fxscsi = File("65482A23AC5C10A6095AEE1DB5824CCA54EAD6E5", 512 * 1024, "PCFX_fx-scsi.rom", "PCFX SCSI ROM");
|
||||
Option("PCFX", "SCSIROM", in fxscsi);
|
||||
|
||||
Firmware("N64DD", "IPL JPN", "N64DD Japan IPL");
|
||||
var ddv10 = File("58670C0063793A8F3BE957D71D937B618829BA9E", 4 * 1024 * 1024, "64DD_IPL_v10_JPN.bin", "N64DD JPN IPL v1.0 (Beta)");
|
||||
var ddv11 = File("B3E26DBB4E945F78C918FABC5B9E60FCF262C47B", 4 * 1024 * 1024, "64DD_IPL_v11_JPN.bin", "N64DD JPN IPL v1.1 (Beta)");
|
||||
var ddv12 = File("BF861922DCB78C316360E3E742F4F70FF63C9BC3", 4 * 1024 * 1024, "64DD_IPL_v12_JPN.bin", "N64DD JPN IPL v1.2 (Retail)");
|
||||
Option("N64DD", "IPL JPN", in ddv10, FirmwareOptionStatus.Unacceptable);
|
||||
Option("N64DD", "IPL JPN", in ddv11, FirmwareOptionStatus.Unacceptable);
|
||||
Option("N64DD", "IPL JPN", in ddv12, FirmwareOptionStatus.Ideal);
|
||||
FirmwareAndOption("10C4173C2A7EB09C6579818F72EF18FA0B6D32DE", 4 * 1024 * 1024, "N64DD", "IPL DEV", "64DD_IPL_DEV.bin", "N64DD Development IPL");
|
||||
FirmwareAndOption("3C5B93CA231550C68693A14F03CEA8D5DBD1BE9E", 4 * 1024 * 1024, "N64DD", "IPL USA", "64DD_IPL_USA.bin", "N64DD Prototype USA IPL");
|
||||
|
||||
/*Firmware("PS2", "BIOS", "PS2 Bios");
|
||||
Option("PS2", "BIOS", File("FBD54BFC020AF34008B317DCB80B812DD29B3759", 4 * 1024 * 1024, "ps2-0230j-20080220.bin", "PS2 Bios"));
|
||||
Option("PS2", "BIOS", File("8361D615CC895962E0F0838489337574DBDC9173", 4 * 1024 * 1024, "ps2-0220a-20060905.bin", "PS2 Bios"));
|
||||
|
|
|
@ -11,9 +11,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
private Ares64Settings _settings;
|
||||
private Ares64SyncSettings _syncSettings;
|
||||
|
||||
public Ares64Settings GetSettings() => _settings.Clone();
|
||||
public Ares64Settings GetSettings()
|
||||
=> _settings.Clone();
|
||||
|
||||
public Ares64SyncSettings GetSyncSettings() => _syncSettings.Clone();
|
||||
public Ares64SyncSettings GetSyncSettings()
|
||||
=> _syncSettings.Clone();
|
||||
|
||||
public PutSettingsDirtyBits PutSettings(Ares64Settings o)
|
||||
{
|
||||
|
@ -37,11 +39,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
[DefaultValue(LibAres64.DeinterlacerType.Weave)]
|
||||
public LibAres64.DeinterlacerType Deinterlacer { get; set; }
|
||||
|
||||
public Ares64Settings() => SettingsUtil.SetDefaultValues(this);
|
||||
public Ares64Settings()
|
||||
=> SettingsUtil.SetDefaultValues(this);
|
||||
|
||||
public Ares64Settings Clone() => MemberwiseClone() as Ares64Settings;
|
||||
public Ares64Settings Clone()
|
||||
=> (Ares64Settings)MemberwiseClone();
|
||||
|
||||
public static bool NeedsReboot(Ares64Settings x, Ares64Settings y) => !DeepEquality.DeepEquals(x, y);
|
||||
public static bool NeedsReboot(Ares64Settings x, Ares64Settings y)
|
||||
=> !DeepEquality.DeepEquals(x, y);
|
||||
}
|
||||
|
||||
public class Ares64SyncSettings
|
||||
|
@ -57,6 +62,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
[DefaultValue(false)]
|
||||
public bool UseRealTime { get; set; }
|
||||
|
||||
[DisplayName("DD IPL Version")]
|
||||
[Description("")]
|
||||
[DefaultValue(LibAres64.IplVer.Japan)]
|
||||
public LibAres64.IplVer IPLVersion { get; set; }
|
||||
|
||||
[DisplayName("Player 1 Controller")]
|
||||
[Description("")]
|
||||
[DefaultValue(LibAres64.ControllerType.Mempak)]
|
||||
|
@ -77,16 +87,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
[DefaultValue(LibAres64.ControllerType.Unplugged)]
|
||||
public LibAres64.ControllerType P4Controller { get; set; }
|
||||
|
||||
[DisplayName("Restrict Analog Range")]
|
||||
[Description("Restricts analog range to account for physical limitations.")]
|
||||
[DefaultValue(false)]
|
||||
public bool RestrictAnalogRange { get; set; }
|
||||
public Ares64SyncSettings()
|
||||
=> SettingsUtil.SetDefaultValues(this);
|
||||
|
||||
public Ares64SyncSettings() => SettingsUtil.SetDefaultValues(this);
|
||||
public Ares64SyncSettings Clone()
|
||||
=> (Ares64SyncSettings)MemberwiseClone();
|
||||
|
||||
public Ares64SyncSettings Clone() => MemberwiseClone() as Ares64SyncSettings;
|
||||
|
||||
public static bool NeedsReboot(Ares64SyncSettings x, Ares64SyncSettings y) => !DeepEquality.DeepEquals(x, y);
|
||||
public static bool NeedsReboot(Ares64SyncSettings x, Ares64SyncSettings y)
|
||||
=> !DeepEquality.DeepEquals(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
@ -8,7 +9,7 @@ using BizHawk.Emulation.Cores.Waterbox;
|
|||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
||||
{
|
||||
[PortedCore(CoreNames.Ares64, "ares team, Near", "v128", "https://ares-emulator.github.io/")]
|
||||
[PortedCore(CoreNames.Ares64, "ares team, Near", "v130.1", "https://ares-emu.net/")]
|
||||
[ServiceNotApplicable(new[] { typeof(IDriveLight), })]
|
||||
public partial class Ares64 : WaterboxCore, IRegionable
|
||||
{
|
||||
|
@ -32,6 +33,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
_settings = lp.Settings ?? new();
|
||||
_syncSettings = lp.SyncSettings ?? new();
|
||||
|
||||
DeterministicEmulation = lp.DeterministicEmulationRequested || (!_syncSettings.UseRealTime);
|
||||
InitializeRtc(_syncSettings.InitialTime);
|
||||
|
||||
ControllerSettings = new[]
|
||||
{
|
||||
_syncSettings.P1Controller,
|
||||
|
@ -55,12 +59,34 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
|
||||
}, new[] { _tracecb, });
|
||||
|
||||
var rom = lp.Roms[0].RomData;
|
||||
static bool IsGBRom(byte[] rom)
|
||||
{
|
||||
// GB roms will have the nintendo logo at 0x104 - 0x133
|
||||
const string ninLogoSha1 = "0745FDEF34132D1B3D488CFBDF0379A39FD54B4C";
|
||||
return ninLogoSha1 == SHA1Checksum.ComputeDigestHex(new ReadOnlySpan<byte>(rom).Slice(0x104, 48));
|
||||
}
|
||||
|
||||
Region = rom[0x3E] switch
|
||||
var gbRoms = lp.Roms.FindAll(r => IsGBRom(r.FileData)).Select(r => r.FileData).ToList();
|
||||
var rom = lp.Roms.Find(r => !gbRoms.Contains(r.FileData) && (char)r.RomData[0x3B] is 'N' or 'C')?.RomData;
|
||||
var disk = lp.Roms.Find(r => !gbRoms.Contains(r.FileData) && (char)r.RomData[0x3B] is 'D' or 'E')?.RomData;
|
||||
|
||||
if (rom is null && disk is null)
|
||||
{
|
||||
if (gbRoms.Count == 0 && lp.Roms.Count == 1) // let's just assume it's an N64 ROM then
|
||||
{
|
||||
rom = lp.Roms[0].RomData;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Could not identify ROM or Disk with given files!");
|
||||
}
|
||||
}
|
||||
|
||||
var regionByte = rom is null ? 0 : rom[0x3E];
|
||||
Region = regionByte switch
|
||||
{
|
||||
0x44 or 0x46 or 0x49 or 0x50 or 0x53 or 0x55 or 0x58 or 0x59 => DisplayType.PAL,
|
||||
_ => DisplayType.NTSC,
|
||||
_ => DisplayType.NTSC, // note that N64DD is only valid as NTSC
|
||||
};
|
||||
|
||||
var pal = Region == DisplayType.PAL;
|
||||
|
@ -71,43 +97,56 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
VsyncDenominator = 1;
|
||||
}
|
||||
|
||||
LibAres64.LoadFlags loadFlags = 0;
|
||||
if (_syncSettings.RestrictAnalogRange)
|
||||
loadFlags |= LibAres64.LoadFlags.RestrictAnalogRange;
|
||||
if (pal)
|
||||
loadFlags |= LibAres64.LoadFlags.Pal;
|
||||
if (_settings.Deinterlacer == LibAres64.DeinterlacerType.Bob)
|
||||
loadFlags |= LibAres64.LoadFlags.BobDeinterlace;
|
||||
|
||||
var pif = Zstd.DecompressZstdStream(new MemoryStream(pal ? Resources.PIF_PAL_ROM.Value : Resources.PIF_NTSC_ROM.Value)).ToArray();
|
||||
|
||||
var gbRoms = new byte[][] { null, null, null, null };
|
||||
var numGbRoms = lp.Roms.Count - 1;
|
||||
for (int i = 0; i < numGbRoms; i++)
|
||||
IsDD = disk is not null;
|
||||
byte[] ipl = null;
|
||||
if (IsDD)
|
||||
{
|
||||
gbRoms[i] = lp.Roms[i + 1].RomData;
|
||||
ipl = _syncSettings.IPLVersion switch
|
||||
{
|
||||
LibAres64.IplVer.Japan => lp.Comm.CoreFileProvider.GetFirmwareOrThrow(new("N64DD", "IPL JPN")),
|
||||
LibAres64.IplVer.Dev => lp.Comm.CoreFileProvider.GetFirmwareOrThrow(new("N64DD", "IPL DEV")),
|
||||
LibAres64.IplVer.USA => lp.Comm.CoreFileProvider.GetFirmwareOrThrow(new("N64DD", "IPL USA")),
|
||||
_ => throw new InvalidOperationException(),
|
||||
};
|
||||
}
|
||||
|
||||
byte[] GetGBRomOrNull(int n)
|
||||
=> n < gbRoms.Count ? gbRoms[n] : null;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* pifPtr = pif, romPtr = rom, gb1RomPtr = gbRoms[0], gb2RomPtr = gbRoms[1], gb3RomPtr = gbRoms[2], gb4RomPtr = gbRoms[3])
|
||||
fixed (byte*
|
||||
pifPtr = pif,
|
||||
iplPtr = ipl,
|
||||
romPtr = rom,
|
||||
diskPtr = disk,
|
||||
gb1RomPtr = GetGBRomOrNull(0),
|
||||
gb2RomPtr = GetGBRomOrNull(1),
|
||||
gb3RomPtr = GetGBRomOrNull(2),
|
||||
gb4RomPtr = GetGBRomOrNull(3))
|
||||
{
|
||||
var loadData = new LibAres64.LoadData()
|
||||
var loadData = new LibAres64.LoadData
|
||||
{
|
||||
PifData = (IntPtr)pifPtr,
|
||||
PifLen = pif.Length,
|
||||
IplData = (IntPtr)iplPtr,
|
||||
IplLen = ipl?.Length ?? 0,
|
||||
RomData = (IntPtr)romPtr,
|
||||
RomLen = rom.Length,
|
||||
RomLen = rom?.Length ?? 0,
|
||||
DiskData = (IntPtr)diskPtr,
|
||||
DiskLen = disk?.Length ?? 0,
|
||||
Gb1RomData = (IntPtr)gb1RomPtr,
|
||||
Gb1RomLen = gbRoms[0]?.Length ?? 0,
|
||||
Gb1RomLen = GetGBRomOrNull(0)?.Length ?? 0,
|
||||
Gb2RomData = (IntPtr)gb2RomPtr,
|
||||
Gb2RomLen = gbRoms[1]?.Length ?? 0,
|
||||
Gb2RomLen = GetGBRomOrNull(1)?.Length ?? 0,
|
||||
Gb3RomData = (IntPtr)gb3RomPtr,
|
||||
Gb3RomLen = gbRoms[2]?.Length ?? 0,
|
||||
Gb3RomLen = GetGBRomOrNull(2)?.Length ?? 0,
|
||||
Gb4RomData = (IntPtr)gb4RomPtr,
|
||||
Gb4RomLen = gbRoms[3]?.Length ?? 0,
|
||||
Gb4RomLen = GetGBRomOrNull(3)?.Length ?? 0,
|
||||
};
|
||||
if (!_core.Init(ref loadData, ControllerSettings, loadFlags))
|
||||
if (!_core.Init(ref loadData, ControllerSettings, pal, _settings.Deinterlacer == LibAres64.DeinterlacerType.Bob, GetRtcTime(!DeterministicEmulation)))
|
||||
{
|
||||
throw new InvalidOperationException("Init returned false!");
|
||||
}
|
||||
|
@ -121,9 +160,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
|
||||
_disassembler = new(_core);
|
||||
_serviceProvider.Register<IDisassemblable>(_disassembler);
|
||||
|
||||
DeterministicEmulation = lp.DeterministicEmulationRequested || (!_syncSettings.UseRealTime);
|
||||
InitializeRtc(_syncSettings.InitialTime);
|
||||
}
|
||||
|
||||
public DisplayType Region { get; }
|
||||
|
@ -134,6 +170,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
|
||||
public LibAres64.ControllerType[] ControllerSettings { get; }
|
||||
|
||||
public bool IsDD { get; }
|
||||
|
||||
private static ControllerDefinition CreateControllerDefinition(LibAres64.ControllerType[] controllerSettings)
|
||||
{
|
||||
var ret = new ControllerDefinition("Nintendo 64 Controller");
|
||||
|
@ -161,7 +199,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
ret.BoolButtons.Add($"P{i + 1} C Right");
|
||||
ret.BoolButtons.Add($"P{i + 1} L");
|
||||
ret.BoolButtons.Add($"P{i + 1} R");
|
||||
ret.AddXYPair($"P{i + 1} {{0}} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0);
|
||||
ret.AddXYPair($"P{i + 1} {{0}} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0, new CircularAxisConstraint("Natural Circle", $"P{i + 1} Y Axis", 127.0f));
|
||||
if (controllerSettings[i] == LibAres64.ControllerType.Rumblepak)
|
||||
{
|
||||
ret.HapticsChannels.Add($"P{i + 1} Rumble Pak");
|
||||
|
|
|
@ -43,6 +43,13 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
Bob,
|
||||
}
|
||||
|
||||
public enum IplVer : uint
|
||||
{
|
||||
Japan,
|
||||
Dev,
|
||||
USA,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public new class FrameInfo : LibWaterboxCore.FrameInfo
|
||||
{
|
||||
|
@ -69,33 +76,29 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
|
|||
public bool Power;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum LoadFlags : uint
|
||||
{
|
||||
RestrictAnalogRange = 1 << 0,
|
||||
Pal = 1 << 1,
|
||||
BobDeinterlace = 1 << 2, // weave otherwise
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct LoadData
|
||||
{
|
||||
public IntPtr PifData;
|
||||
public int PifLen;
|
||||
public long PifLen;
|
||||
public IntPtr IplData;
|
||||
public long IplLen;
|
||||
public IntPtr RomData;
|
||||
public int RomLen;
|
||||
public long RomLen;
|
||||
public IntPtr DiskData;
|
||||
public long DiskLen;
|
||||
public IntPtr Gb1RomData;
|
||||
public int Gb1RomLen;
|
||||
public long Gb1RomLen;
|
||||
public IntPtr Gb2RomData;
|
||||
public int Gb2RomLen;
|
||||
public long Gb2RomLen;
|
||||
public IntPtr Gb3RomData;
|
||||
public int Gb3RomLen;
|
||||
public long Gb3RomLen;
|
||||
public IntPtr Gb4RomData;
|
||||
public int Gb4RomLen;
|
||||
public long Gb4RomLen;
|
||||
}
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract bool Init(ref LoadData loadData, ControllerType[] controllerSettings, LoadFlags loadFlags);
|
||||
public abstract bool Init(ref LoadData loadData, ControllerType[] controllerSettings, bool isPal, bool bobDeinterlace, long initTime);
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract bool GetRumbleStatus(int num);
|
||||
|
|
|
@ -98,7 +98,7 @@ auto BizPlatform::audio(ares::Node::Audio::Stream stream) -> void
|
|||
stream->read(buf);
|
||||
soundbuf[nsamps * 2 + 0] = (s16)std::clamp(buf[0] * 32768, -32768.0, 32767.0);
|
||||
soundbuf[nsamps * 2 + 1] = (s16)std::clamp(buf[1] * 32768, -32768.0, 32767.0);
|
||||
if (!hack) nsamps++;
|
||||
if (nsamps < 1023) nsamps++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,18 +117,12 @@ auto BizPlatform::input(ares::Node::Input::Input node) -> void
|
|||
static ares::Node::System root = nullptr;
|
||||
static BizPlatform* platform = nullptr;
|
||||
static array_view<u8>* pifData = nullptr;
|
||||
static array_view<u8>* iplData = nullptr;
|
||||
static array_view<u8>* romData = nullptr;
|
||||
static array_view<u8>* diskData = nullptr;
|
||||
static array_view<u8>* saveData = nullptr;
|
||||
static array_view<u8>* gbRomData[4] = { nullptr, nullptr, nullptr, nullptr, };
|
||||
|
||||
static inline void HackeryDoo()
|
||||
{
|
||||
platform->hack = true;
|
||||
root->run();
|
||||
root->run();
|
||||
platform->hack = false;
|
||||
}
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NONE,
|
||||
|
@ -353,30 +347,27 @@ static inline SaveType DetectSaveType(u8* rom)
|
|||
return ret;
|
||||
}
|
||||
|
||||
namespace ares::Nintendo64 { extern bool RestrictAnalogRange; extern bool BobDeinterlace; }
|
||||
namespace ares::Nintendo64 { extern bool BobDeinterlace; }
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8* GbRomData;
|
||||
u32 GbRomLen;
|
||||
u64 GbRomLen;
|
||||
} GbRom;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
u8* PifData;
|
||||
u32 PifLen;
|
||||
u64 PifLen;
|
||||
u8* IplData;
|
||||
u64 IplLen;
|
||||
u8* RomData;
|
||||
u32 RomLen;
|
||||
u64 RomLen;
|
||||
u8* DiskData;
|
||||
u64 DiskLen;
|
||||
GbRom GbRoms[4];
|
||||
} LoadData;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RESTRICT_ANALOG_RANGE = 1 << 0,
|
||||
IS_PAL = 1 << 1,
|
||||
BOB_DEINTERLACE = 1 << 2, // weave otherwise (todo: implement this)
|
||||
} LoadFlags;
|
||||
|
||||
#define SET_RTC_CALLBACK(NUM) do { \
|
||||
if (auto pad = dynamic_cast<ares::Nintendo64::Gamepad*>(ares::Nintendo64::controllerPort##NUM.device.data())) \
|
||||
{ \
|
||||
|
@ -387,24 +378,12 @@ typedef enum
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
EXPORT bool Init(LoadData* loadData, ControllerType* controllers, LoadFlags loadFlags)
|
||||
static bool LoadRom(LoadData* loadData, bool isPal)
|
||||
{
|
||||
platform = new BizPlatform;
|
||||
platform->bizpak = new vfs::directory;
|
||||
|
||||
u8* data;
|
||||
u32 len;
|
||||
string name;
|
||||
|
||||
bool pal = loadFlags & IS_PAL;
|
||||
|
||||
name = pal ? "pif.pal.rom" : "pif.ntsc.rom";
|
||||
len = loadData->PifLen;
|
||||
data = new u8[len];
|
||||
memcpy(data, loadData->PifData, len);
|
||||
pifData = new array_view<u8>(data, len);
|
||||
platform->bizpak->append(name, *pifData);
|
||||
|
||||
name = "program.rom";
|
||||
len = loadData->RomLen;
|
||||
data = new u8[len];
|
||||
|
@ -412,16 +391,13 @@ EXPORT bool Init(LoadData* loadData, ControllerType* controllers, LoadFlags load
|
|||
romData = new array_view<u8>(data, len);
|
||||
platform->bizpak->append(name, *romData);
|
||||
|
||||
string region = pal ? "PAL" : "NTSC";
|
||||
platform->bizpak->setAttribute("region", region);
|
||||
|
||||
string cic = pal ? "CIC-NUS-7101" : "CIC-NUS-6102";
|
||||
string cic = isPal ? "CIC-NUS-7101" : "CIC-NUS-6102";
|
||||
u32 crc32 = Hash::CRC32({&data[0x40], 0x9C0}).value();
|
||||
if (crc32 == 0x1DEB51A9) cic = pal ? "CIC-NUS-7102" : "CIC-NUS-6101";
|
||||
if (crc32 == 0xC08E5BD6) cic = pal ? "CIC-NUS-7101" : "CIC-NUS-6102";
|
||||
if (crc32 == 0x03B8376A) cic = pal ? "CIC-NUS-7103" : "CIC-NUS-6103";
|
||||
if (crc32 == 0xCF7F41DC) cic = pal ? "CIC-NUS-7105" : "CIC-NUS-6105";
|
||||
if (crc32 == 0xD1059C6A) cic = pal ? "CIC-NUS-7106" : "CIC-NUS-6106";
|
||||
if (crc32 == 0x1DEB51A9) cic = isPal ? "CIC-NUS-7102" : "CIC-NUS-6101";
|
||||
if (crc32 == 0xC08E5BD6) cic = isPal ? "CIC-NUS-7101" : "CIC-NUS-6102";
|
||||
if (crc32 == 0x03B8376A) cic = isPal ? "CIC-NUS-7103" : "CIC-NUS-6103";
|
||||
if (crc32 == 0xCF7F41DC) cic = isPal ? "CIC-NUS-7105" : "CIC-NUS-6105";
|
||||
if (crc32 == 0xD1059C6A) cic = isPal ? "CIC-NUS-7106" : "CIC-NUS-6106";
|
||||
platform->bizpak->setAttribute("cic", cic);
|
||||
|
||||
SaveType save = DetectSaveType(data);
|
||||
|
@ -443,6 +419,100 @@ EXPORT bool Init(LoadData* loadData, ControllerType* controllers, LoadFlags load
|
|||
platform->bizpak->append(name, *saveData);
|
||||
}
|
||||
|
||||
if (auto port = root->find<ares::Node::Port>("Cartridge Slot"))
|
||||
{
|
||||
port->allocate();
|
||||
port->connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool LoadDisk(LoadData* loadData)
|
||||
{
|
||||
u8* data;
|
||||
u32 len;
|
||||
string name;
|
||||
|
||||
name = "64dd.ipl.rom";
|
||||
len = loadData->IplLen;
|
||||
data = new u8[len];
|
||||
memcpy(data, loadData->IplData, len);
|
||||
iplData = new array_view<u8>(data, len);
|
||||
platform->bizpak->append(name, *iplData);
|
||||
|
||||
name = "program.disk";
|
||||
len = loadData->DiskLen;
|
||||
data = new u8[len];
|
||||
memcpy(data, loadData->DiskData, len);
|
||||
diskData = new array_view<u8>(data, len);
|
||||
platform->bizpak->append(name, *diskData);
|
||||
|
||||
ares::Nintendo64::dd.rtcCallback = GetBizTime;
|
||||
|
||||
if (auto port = root->find<ares::Node::Port>("Nintendo 64DD/Disk Drive"))
|
||||
{
|
||||
port->allocate();
|
||||
port->connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EXPORT bool Init(LoadData* loadData, ControllerType* controllers, bool isPal, bool bobDeinterlace, u64 initTime)
|
||||
{
|
||||
platform = new BizPlatform;
|
||||
platform->bizpak = new vfs::directory;
|
||||
biztime = initTime;
|
||||
|
||||
u8* data;
|
||||
u32 len;
|
||||
string name;
|
||||
|
||||
name = isPal ? "pif.pal.rom" : "pif.ntsc.rom";
|
||||
len = loadData->PifLen;
|
||||
data = new u8[len];
|
||||
memcpy(data, loadData->PifData, len);
|
||||
pifData = new array_view<u8>(data, len);
|
||||
platform->bizpak->append(name, *pifData);
|
||||
|
||||
string region = isPal ? "PAL" : "NTSC";
|
||||
platform->bizpak->setAttribute("region", region);
|
||||
|
||||
ares::platform = platform;
|
||||
|
||||
name = {"[Nintendo] Nintendo 64 (", region, ")"};
|
||||
if (loadData->DiskData) name = {"[Nintendo] Nintendo 64DD (", region, ")"};
|
||||
|
||||
if (!ares::Nintendo64::load(root, name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loadData->RomData)
|
||||
{
|
||||
if (!LoadRom(loadData, isPal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadData->DiskData)
|
||||
{
|
||||
if (!LoadDisk(loadData))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (loadData->GbRoms[i].GbRomData)
|
||||
|
@ -454,23 +524,6 @@ EXPORT bool Init(LoadData* loadData, ControllerType* controllers, LoadFlags load
|
|||
}
|
||||
}
|
||||
|
||||
ares::platform = platform;
|
||||
|
||||
if (!ares::Nintendo64::load(root, {"[Nintendo] Nintendo 64 (", region, ")"}))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto port = root->find<ares::Node::Port>("Cartridge Slot"))
|
||||
{
|
||||
port->allocate();
|
||||
port->connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0, j = 0; i < 4; i++)
|
||||
{
|
||||
if (auto port = root->find<ares::Node::Port>({"Controller Port ", 1 + i}))
|
||||
|
@ -525,11 +578,9 @@ EXPORT bool Init(LoadData* loadData, ControllerType* controllers, LoadFlags load
|
|||
SET_RTC_CALLBACK(3);
|
||||
SET_RTC_CALLBACK(4);
|
||||
|
||||
ares::Nintendo64::RestrictAnalogRange = loadFlags & RESTRICT_ANALOG_RANGE;
|
||||
ares::Nintendo64::BobDeinterlace = loadFlags & BOB_DEINTERLACE;
|
||||
ares::Nintendo64::BobDeinterlace = bobDeinterlace;
|
||||
|
||||
root->power(false);
|
||||
HackeryDoo();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -726,15 +777,15 @@ struct MyFrameInfo : public FrameInfo
|
|||
|
||||
EXPORT void FrameAdvance(MyFrameInfo* f)
|
||||
{
|
||||
biztime = f->Time;
|
||||
|
||||
if (f->Power)
|
||||
{
|
||||
root->power(false);
|
||||
HackeryDoo();
|
||||
}
|
||||
else if (f->Reset)
|
||||
{
|
||||
root->power(true);
|
||||
HackeryDoo();
|
||||
}
|
||||
|
||||
UPDATE_CONTROLLER(1);
|
||||
|
@ -744,7 +795,6 @@ EXPORT void FrameAdvance(MyFrameInfo* f)
|
|||
|
||||
platform->lagged = true;
|
||||
platform->nsamps = 0;
|
||||
biztime = f->Time;
|
||||
|
||||
root->run();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ CXXFLAGS := -std=c++17 -msse4.2 \
|
|||
-I../libco -I.$(ROOT_DIR)/ares/ -I.$(ROOT_DIR)/ares/thirdparty/ -I.$(ARES_PATH) \
|
||||
-Werror=int-to-pointer-cast -Wno-unused-but-set-variable -Wno-format-security \
|
||||
-Wno-parentheses -Wno-reorder -Wno-unused-variable -Wno-delete-non-virtual-dtor \
|
||||
-Wno-sign-compare -Wno-switch -Wno-unused-local-typedefs \
|
||||
-Wno-sign-compare -Wno-switch -Wno-unused-local-typedefs -Wno-bool-operation \
|
||||
-fno-strict-aliasing -fwrapv -fno-operator-names \
|
||||
-I.$(ANGRYLION_PATH) -DANGRYLION_RDP
|
||||
|
||||
|
@ -26,8 +26,6 @@ SRCS_N64 = \
|
|||
$(ARES_PATH)/n64/cartridge/cartridge.cpp \
|
||||
$(ARES_PATH)/n64/controller/controller.cpp \
|
||||
$(ARES_PATH)/n64/dd/dd.cpp \
|
||||
$(ARES_PATH)/n64/sp/sp.cpp \
|
||||
$(ARES_PATH)/n64/dp/dp.cpp \
|
||||
$(ARES_PATH)/n64/mi/mi.cpp \
|
||||
$(ARES_PATH)/n64/vi/vi.cpp \
|
||||
$(ARES_PATH)/n64/ai/ai.cpp \
|
||||
|
@ -37,8 +35,8 @@ SRCS_N64 = \
|
|||
$(ARES_PATH)/n64/si/si.cpp \
|
||||
$(ARES_PATH)/n64/rdram/rdram.cpp \
|
||||
$(ARES_PATH)/n64/cpu/cpu.cpp \
|
||||
$(ARES_PATH)/n64/rdp/rdp.cpp \
|
||||
$(ARES_PATH)/n64/rsp/rsp.cpp
|
||||
$(ARES_PATH)/n64/rsp/rsp.cpp \
|
||||
$(ARES_PATH)/n64/rdp/rdp.cpp
|
||||
|
||||
SRCS_ANGRYLION = \
|
||||
$(ANGRYLION_PATH)/main.cpp \
|
||||
|
|
|
@ -45,108 +45,6 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
MAME
|
||||
Copyright (c) 1997-2021 MAMEdev and contributors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
libchdr
|
||||
|
||||
Copyright Romain Tisserand
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
LZMA SDK is placed in the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute
|
||||
the original LZMA SDK code, either in source code form or as a compiled binary,
|
||||
for any purpose, commercial or non-commercial, and by any means.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
zlib
|
||||
|
||||
(C) 1995-2017 Jean-loup Gailly and Mark Adler
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Jean-loup Gailly Mark Adler
|
||||
jloup@gzip.org madler@alumni.caltech.edu
|
||||
|
||||
If you use the zlib library in a product, we would appreciate *not* receiving
|
||||
lengthy legal documents to sign. The sources are provided for free but without
|
||||
warranty of any kind. The library has been entirely written by Jean-loup
|
||||
Gailly and Mark Adler; it does not include third-party code.
|
||||
|
||||
If you redistribute modified sources, we would appreciate that you include in
|
||||
the file ChangeLog history information documenting your changes. Please read
|
||||
the FAQ for more information on the distribution of modified source versions.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
xxHash - Extremely Fast Hash algorithm
|
||||
|
@ -181,3 +79,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
You can contact the author at:
|
||||
- xxHash homepage: https://www.xxhash.com
|
||||
- xxHash source repository: https://github.com/Cyan4973/xxHash
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <nall/directory.hpp>
|
||||
#include <nall/dl.hpp>
|
||||
#include <nall/endian.hpp>
|
||||
#include <nall/hashset.hpp>
|
||||
#include <nall/image.hpp>
|
||||
#include <nall/literals.hpp>
|
||||
#include <nall/priority-queue.hpp>
|
||||
|
@ -40,7 +41,7 @@ using namespace nall;
|
|||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "129";
|
||||
static const string Version = "130.1";
|
||||
static const string Copyright = "ares team, Near";
|
||||
static const string License = "ISC";
|
||||
static const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
|
@ -49,7 +50,7 @@ namespace ares {
|
|||
|
||||
//incremented only when serialization format changes
|
||||
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
|
||||
static const string SerializerVersion = "129";
|
||||
static const string SerializerVersion = "130.3";
|
||||
|
||||
namespace VFS {
|
||||
using Pak = shared_pointer<vfs::directory>;
|
||||
|
|
|
@ -4,19 +4,19 @@ struct Debug {
|
|||
auto reset() -> void;
|
||||
|
||||
template<typename... P> auto unhandled(P&&... p) -> void {
|
||||
return _unhandled({forward<P>(p)...});
|
||||
return _unhandled({std::forward<P>(p)...});
|
||||
}
|
||||
|
||||
template<typename... P> auto unimplemented(P&&... p) -> void {
|
||||
return _unimplemented({forward<P>(p)...});
|
||||
return _unimplemented({std::forward<P>(p)...});
|
||||
}
|
||||
|
||||
template<typename... P> auto unusual(P&&... p) -> void {
|
||||
return _unusual({forward<P>(p)...});
|
||||
return _unusual({std::forward<P>(p)...});
|
||||
}
|
||||
|
||||
template<typename... P> auto unverified(P&&... p) -> void {
|
||||
return _unverified({forward<P>(p)...});
|
||||
return _unverified({std::forward<P>(p)...});
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace ares::Memory {
|
||||
|
||||
#if defined(PLATFORM_MACOS) && defined(ARCHITECTURE_ARM64)
|
||||
#if defined(PLATFORM_MACOS)
|
||||
//stub for unsupported platforms
|
||||
FixedAllocator::FixedAllocator() {
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ struct Stream : Audio {
|
|||
template<typename... P>
|
||||
auto frame(P&&... p) -> void {
|
||||
if(runAhead()) return;
|
||||
f64 samples[sizeof...(p)] = {forward<P>(p)...};
|
||||
f64 samples[sizeof...(p)] = {std::forward<P>(p)...};
|
||||
write(samples);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,13 +18,14 @@ struct Instruction : Tracer {
|
|||
|
||||
auto setMask(bool mask) -> void {
|
||||
_mask = mask;
|
||||
_masks.reset();
|
||||
}
|
||||
|
||||
auto setDepth(u32 depth) -> void {
|
||||
_depth = depth;
|
||||
_history.reset();
|
||||
_history.resize(depth);
|
||||
for(auto& history : _history) history = ~0;
|
||||
for(auto& history : _history) history = ~0ull;
|
||||
}
|
||||
|
||||
auto address(u64 address) -> bool {
|
||||
|
@ -32,9 +33,10 @@ struct Instruction : Tracer {
|
|||
_address = address;
|
||||
/*address >>= _addressMask; //clip unneeded alignment bits (to reduce _masks size)
|
||||
|
||||
if(_mask && updateMasks()) {
|
||||
if(_masks[address >> 3] & 1 << (address & 7)) return false; //do not trace twice
|
||||
_masks[address >> 3] |= 1 << (address & 7);
|
||||
if(_mask) {
|
||||
auto mask = _masks.find(address);
|
||||
if(!mask) mask = _masks.insert(address);
|
||||
if(mask->visit(address)) return false; //do not trace twice
|
||||
}
|
||||
|
||||
if(_depth) {
|
||||
|
@ -56,10 +58,12 @@ struct Instruction : Tracer {
|
|||
//mark an already-executed address as not executed yet for trace masking.
|
||||
//call when writing to executable RAM to support self-modifying code.
|
||||
auto invalidate(u64 address) -> void {
|
||||
/*if(unlikely(_mask && updateMasks())) {
|
||||
/*if(unlikely(_mask)) {
|
||||
address &= ~0ull >> (64 - _addressBits);
|
||||
address >>= _addressMask;
|
||||
_masks[address >> 3] &= ~(1 << (address & 7));
|
||||
|
||||
auto mask = _masks.find(address);
|
||||
if(mask) mask->unvisit(address);
|
||||
}*/
|
||||
}
|
||||
|
||||
|
@ -103,14 +107,27 @@ struct Instruction : Tracer {
|
|||
}
|
||||
|
||||
protected:
|
||||
auto updateMasks() -> bool {
|
||||
auto size = 1ull << (_addressBits - _addressMask - 3);
|
||||
if(!_mask || !size) return _masks.reset(), false;
|
||||
if(_masks.size() == size) return true;
|
||||
_masks.reset();
|
||||
_masks.resize(size);
|
||||
return true;
|
||||
}
|
||||
struct VisitMask {
|
||||
VisitMask(u64 address) : upper(address >> 6), mask(0) {}
|
||||
auto operator==(const VisitMask& source) const -> bool { return upper == source.upper; }
|
||||
auto hash() const -> u32 { return upper; }
|
||||
|
||||
auto visit(u64 address) -> bool {
|
||||
const u64 bit = 1ull << (address & 0x3f);
|
||||
if(mask & bit) return true;
|
||||
mask |= bit;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto unvisit(u64 address) -> void {
|
||||
const u64 bit = 1ull << (address & 0x3f);
|
||||
mask &= ~bit;
|
||||
}
|
||||
|
||||
private:
|
||||
u64 upper;
|
||||
u64 mask;
|
||||
};
|
||||
|
||||
u32 _addressBits = 32;
|
||||
u32 _addressMask = 0;
|
||||
|
@ -120,6 +137,6 @@ protected:
|
|||
//unserialized:
|
||||
n64 _address = 0;
|
||||
n64 _omitted = 0;
|
||||
vector<u32> _history;
|
||||
vector<u08> _masks;
|
||||
vector<u64> _history;
|
||||
hashset<VisitMask> _masks;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
struct Input : Object {
|
||||
DeclareClass(Input, "input")
|
||||
using Object::Object;
|
||||
u64 lastPoll;
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ struct Object : shared_pointer_this<Object> {
|
|||
template<typename T, typename... P>
|
||||
auto prepend(P&&... p) -> Node::Object {
|
||||
using Type = typename T::type;
|
||||
return prepend(shared_pointer<Type>::create(forward<P>(p)...));
|
||||
return prepend(shared_pointer<Type>::create(std::forward<P>(p)...));
|
||||
}
|
||||
|
||||
auto append(Node::Object node) -> Node::Object {
|
||||
|
@ -47,7 +47,7 @@ struct Object : shared_pointer_this<Object> {
|
|||
template<typename T, typename... P>
|
||||
auto append(P&&... p) -> Node::Object {
|
||||
using Type = typename T::type;
|
||||
return append(shared_pointer<Type>::create(forward<P>(p)...));
|
||||
return append(shared_pointer<Type>::create(std::forward<P>(p)...));
|
||||
}
|
||||
|
||||
auto remove(Node::Object node) -> void {
|
||||
|
|
|
@ -82,10 +82,10 @@ protected:
|
|||
u32 _rotation = 0; //counter-clockwise (90 = left, 270 = right)
|
||||
|
||||
function<n64 (n32)> _color;
|
||||
unique_pointer<u32> _inputA;
|
||||
unique_pointer<u32> _inputB;
|
||||
unique_pointer<u32> _output;
|
||||
unique_pointer<u32> _rotate;
|
||||
unique_pointer<u32[]> _inputA;
|
||||
unique_pointer<u32[]> _inputB;
|
||||
unique_pointer<u32[]> _output;
|
||||
unique_pointer<u32[]> _rotate;
|
||||
unique_pointer<u32[]> _palette;
|
||||
vector<Node::Video::Sprite> _sprites;
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ inline auto Thread::synchronize(Thread& thread, P&&... p) -> void {
|
|||
co_switch(thread.handle());
|
||||
}
|
||||
//convenience: allow synchronizing multiple threads with one function call.
|
||||
if constexpr(sizeof...(p) > 0) synchronize(forward<P>(p)...);
|
||||
if constexpr(sizeof...(p) > 0) synchronize(std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
inline auto Thread::serialize(serializer& s) -> void {
|
||||
|
|
|
@ -15,7 +15,7 @@ struct Accuracy {
|
|||
static constexpr bool Recompiler = !Interpreter;
|
||||
|
||||
//VU instructions
|
||||
static constexpr bool SISD = 0;// | Reference | !Architecture::sse41;
|
||||
static constexpr bool SISD = 0;// | Reference | !ARCHITECTURE_SUPPORTS_SSE4_1;
|
||||
static constexpr bool SIMD = !SISD;
|
||||
};
|
||||
|
||||
|
|
|
@ -213,8 +213,6 @@ auto Gamepad::comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 {
|
|||
return status;
|
||||
}
|
||||
|
||||
bool RestrictAnalogRange;
|
||||
|
||||
auto Gamepad::read() -> n32 {
|
||||
platform->input(x);
|
||||
platform->input(y);
|
||||
|
@ -233,53 +231,9 @@ auto Gamepad::read() -> n32 {
|
|||
platform->input(z);
|
||||
platform->input(start);
|
||||
|
||||
auto ax = x->value() * 1.0;
|
||||
auto ay = y->value() * 1.0;
|
||||
|
||||
if (RestrictAnalogRange) {
|
||||
//scale {-128 ... +127} to {-84 ... +84}
|
||||
ax = ax * 85.0 / 127.0;
|
||||
ay = ay * 85.0 / 127.0;
|
||||
|
||||
//create square dead-zone in range {-7 ... +7}
|
||||
auto lengthAbsoluteX = abs (ax);
|
||||
auto lengthAbsoluteY = abs (ay);
|
||||
if (lengthAbsoluteX < 7.0) {
|
||||
lengthAbsoluteX = 0.0;
|
||||
ax *= lengthAbsoluteX;
|
||||
}
|
||||
if (lengthAbsoluteY < 7.0) {
|
||||
lengthAbsoluteY = 0.0;
|
||||
ay *= lengthAbsoluteY;
|
||||
}
|
||||
|
||||
//create outer circular dead-zone in ranges {-inf ... -85} and {+85 ... +inf} and scale between the two dead-zones according to the two-dimensional length
|
||||
auto length = sqrt(ax * ax + ay * ay);
|
||||
if(length > 85.0) {
|
||||
length = 85.0 / length;
|
||||
} else {
|
||||
length = (length - 7.0) * 85.0 / (85.0 - 7.0) / length;
|
||||
}
|
||||
ax *= length;
|
||||
ay *= length;
|
||||
|
||||
//bound diagonals to an octagonal range {-68 ... +68}
|
||||
if(ax != 0.0 && ay != 0.0) {
|
||||
auto slope = ay / ax;
|
||||
auto edgex = copysign(85.0 / (abs(slope) + 16.0 / 69.0), ax);
|
||||
auto edgey = copysign(min(abs(edgex * slope), 85.0 / (1.0 / abs(slope) + 16.0 / 69.0)), ay);
|
||||
edgex = edgey / slope;
|
||||
|
||||
auto scale = sqrt(edgex * edgex + edgey * edgey) / 85.0;
|
||||
ax *= scale;
|
||||
ay *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
n32 data;
|
||||
//data.byte(0) = -ay;
|
||||
data.byte(0) = +ay;
|
||||
data.byte(1) = +ax;
|
||||
data.byte(0) = y->value();
|
||||
data.byte(1) = x->value();
|
||||
data.bit(16) = cameraRight->value();
|
||||
data.bit(17) = cameraLeft->value();
|
||||
data.bit(18) = cameraDown->value();
|
||||
|
|
|
@ -25,6 +25,7 @@ auto ControllerPort::save() -> void {
|
|||
}
|
||||
|
||||
auto ControllerPort::allocate(string name) -> Node::Peripheral {
|
||||
device = {};
|
||||
if(name == "Gamepad") device = new Gamepad(port);
|
||||
if(name == "Mouse" ) device = new Mouse(port);
|
||||
if(device) return device->node;
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
template <typename T>
|
||||
auto CPU::roundNearest(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_ARM64)
|
||||
return vrndns_f32(f);
|
||||
#elif defined(ARCHITECTURE_AMD64)
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_TO_NEAREST_INT);
|
||||
return _mm_cvtss_f32(t);
|
||||
#else
|
||||
return round(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto CPU::roundNearest(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_ARM64)
|
||||
float64x1_t vf = {f};
|
||||
return vrndn_f64(vf)[0];
|
||||
#elif defined(ARCHITECTURE_AMD64)
|
||||
__m128d t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_TO_NEAREST_INT);
|
||||
return _mm_cvtsd_f64(t);
|
||||
#else
|
||||
return round(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto CPU::roundCeil(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_TO_POS_INF);
|
||||
return _mm_cvtss_f32(t);
|
||||
#else
|
||||
return ceil(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto CPU::roundCeil(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
__m128d t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_TO_POS_INF);
|
||||
return _mm_cvtsd_f64(t);
|
||||
#else
|
||||
return ceil(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto CPU::roundCurrent(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
auto t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_CUR_DIRECTION);
|
||||
return _mm_cvtss_f32(t);
|
||||
#else
|
||||
return rint(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto CPU::roundCurrent(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
auto t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_CUR_DIRECTION);
|
||||
return _mm_cvtsd_f64(t);
|
||||
#else
|
||||
return rint(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto CPU::roundFloor(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_TO_NEG_INF);
|
||||
return _mm_cvtss_f32(t);
|
||||
#else
|
||||
return floor(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto CPU::roundFloor(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
__m128d t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_TO_NEG_INF);
|
||||
return _mm_cvtsd_f64(t);
|
||||
#else
|
||||
return floor(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto CPU::roundTrunc(f32 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_TO_ZERO);
|
||||
return _mm_cvtss_f32(t);
|
||||
#else
|
||||
return trunc(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto CPU::roundTrunc(f64 f) -> T {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
__m128d t = _mm_set_sd(f);
|
||||
t = _mm_round_sd(t, t, _MM_FROUND_TO_ZERO);
|
||||
return _mm_cvtsd_f64(t);
|
||||
#else
|
||||
return trunc(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto CPU::squareRoot(f32 f) -> f32 {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_sqrt_ss(t);
|
||||
return _mm_cvtss_f32(t);
|
||||
#else
|
||||
return sqrt(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto CPU::squareRoot(f64 f) -> f64 {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
__m128d t = _mm_set_sd(f);
|
||||
t = _mm_sqrt_sd(t, t);
|
||||
return _mm_cvtsd_f64(t);
|
||||
#else
|
||||
return sqrt(f);
|
||||
#endif
|
||||
}
|
|
@ -8,10 +8,12 @@ CPU cpu;
|
|||
#include "tlb.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "exceptions.cpp"
|
||||
#include "algorithms.cpp"
|
||||
#include "interpreter.cpp"
|
||||
#include "interpreter-ipu.cpp"
|
||||
#include "interpreter-scc.cpp"
|
||||
#include "interpreter-fpu.cpp"
|
||||
#include "interpreter-cop2.cpp"
|
||||
#include "recompiler.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
@ -57,6 +59,10 @@ auto CPU::synchronize() -> void {
|
|||
case Queue::PI_BUS_Write: return pi.writeFinished();
|
||||
case Queue::SI_DMA_Read: return si.dmaRead();
|
||||
case Queue::SI_DMA_Write: return si.dmaWrite();
|
||||
case Queue::DD_Clock_Tick: return dd.rtcTickClock();
|
||||
case Queue::DD_MECHA_Response: return dd.mechaResponse();
|
||||
case Queue::DD_BM_Request: return dd.bmRequest();
|
||||
case Queue::DD_Motor_Mode: return dd.motorChange();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -132,7 +138,8 @@ auto CPU::power(bool reset) -> void {
|
|||
scc = {};
|
||||
for(auto& r : fpu.r) r.u64 = 0;
|
||||
fpu.csr = {};
|
||||
fesetround(FE_TONEAREST);
|
||||
cop2 = {};
|
||||
fenv.setRound(float_env::toNearest);
|
||||
context.setMode();
|
||||
|
||||
if constexpr(Accuracy::CPU::Recompiler) {
|
||||
|
|
|
@ -287,6 +287,7 @@ struct CPU : Thread {
|
|||
auto systemCall() -> void;
|
||||
auto breakpoint() -> void;
|
||||
auto reservedInstruction() -> void;
|
||||
auto reservedInstructionCop2() -> void;
|
||||
auto coprocessor0() -> void;
|
||||
auto coprocessor1() -> void;
|
||||
auto coprocessor2() -> void;
|
||||
|
@ -343,6 +344,20 @@ struct CPU : Thread {
|
|||
u64 pc; //program counter
|
||||
} ipu;
|
||||
|
||||
//algorithms.cpp
|
||||
template<typename T> auto roundNearest(f32 f) -> T;
|
||||
template<typename T> auto roundNearest(f64 f) -> T;
|
||||
template<typename T> auto roundCeil(f32 f) -> T;
|
||||
template<typename T> auto roundCeil(f64 f) -> T;
|
||||
template<typename T> auto roundCurrent(f32 f) -> T;
|
||||
template<typename T> auto roundCurrent(f64 f) -> T;
|
||||
template<typename T> auto roundFloor(f32 f) -> T;
|
||||
template<typename T> auto roundFloor(f64 f) -> T;
|
||||
template<typename T> auto roundTrunc(f32 f) -> T;
|
||||
template<typename T> auto roundTrunc(f64 f) -> T;
|
||||
auto squareRoot(f32 f) -> f32;
|
||||
auto squareRoot(f64 f) -> f64;
|
||||
|
||||
//interpreter-ipu.cpp
|
||||
auto ADD(r64& rd, cr64& rs, cr64& rt) -> void;
|
||||
auto ADDI(r64& rt, cr64& rs, s16 imm) -> void;
|
||||
|
@ -631,18 +646,37 @@ struct CPU : Thread {
|
|||
n1 unimplementedOperation = 0;
|
||||
} cause;
|
||||
n1 compare = 0;
|
||||
n1 flushed = 0;
|
||||
n1 flushSubnormals = 0;
|
||||
} csr;
|
||||
} fpu;
|
||||
|
||||
//interpreter-fpu.cpp
|
||||
float_env fenv;
|
||||
|
||||
template<typename T> auto fgr(u32) -> T&;
|
||||
auto getControlRegisterFPU(n5) -> u32;
|
||||
auto setControlRegisterFPU(n5, n32) -> void;
|
||||
template<bool CVT> auto checkFPUExceptions() -> bool;
|
||||
auto fpeDivisionByZero() -> bool;
|
||||
auto fpeInexact() -> bool;
|
||||
auto fpeUnderflow() -> bool;
|
||||
auto fpeOverflow() -> bool;
|
||||
auto fpeInvalidOperation() -> bool;
|
||||
auto fpeUnimplemented() -> bool;
|
||||
auto fpuCheckStart() -> bool;
|
||||
auto fpuCheckInput(f32& f) -> bool;
|
||||
auto fpuCheckInput(f64& f) -> bool;
|
||||
auto fpuCheckOutput(f32& f) -> bool;
|
||||
auto fpuCheckOutput(f64& f) -> bool;
|
||||
auto fpuClearCause() -> void;
|
||||
template<typename DST, typename SF>
|
||||
auto fpuCheckInputConv(SF& f) -> bool;
|
||||
|
||||
auto BC1(bool value, bool likely, s16 imm) -> void;
|
||||
auto CFC1(r64& rt, u8 rd) -> void;
|
||||
auto CTC1(cr64& rt, u8 rd) -> void;
|
||||
auto DCFC1(r64& rt, u8 rd) -> void;
|
||||
auto DCTC1(cr64& rt, u8 rd) -> void;
|
||||
auto DMFC1(r64& rt, u8 fs) -> void;
|
||||
auto DMTC1(cr64& rt, u8 fs) -> void;
|
||||
auto FABS_S(u8 fd, u8 fs) -> void;
|
||||
|
@ -651,8 +685,12 @@ struct CPU : Thread {
|
|||
auto FADD_D(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto FCEIL_L_S(u8 fd, u8 fs) -> void;
|
||||
auto FCEIL_L_D(u8 fd, u8 fs) -> void;
|
||||
auto FCEIL_L_W(u8 fd, u8 fs) -> void;
|
||||
auto FCEIL_L_L(u8 fd, u8 fs) -> void;
|
||||
auto FCEIL_W_S(u8 fd, u8 fs) -> void;
|
||||
auto FCEIL_W_D(u8 fd, u8 fs) -> void;
|
||||
auto FCEIL_W_W(u8 fd, u8 fs) -> void;
|
||||
auto FCEIL_W_L(u8 fd, u8 fs) -> void;
|
||||
auto FC_EQ_S(u8 fs, u8 ft) -> void;
|
||||
auto FC_EQ_D(u8 fs, u8 ft) -> void;
|
||||
auto FC_F_S(u8 fs, u8 ft) -> void;
|
||||
|
@ -685,22 +723,32 @@ struct CPU : Thread {
|
|||
auto FC_ULT_D(u8 fs, u8 ft) -> void;
|
||||
auto FC_UN_S(u8 fs, u8 ft) -> void;
|
||||
auto FC_UN_D(u8 fs, u8 ft) -> void;
|
||||
auto FCVT_S_S(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_S_D(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_S_W(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_S_L(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_D_S(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_D_D(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_D_W(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_D_L(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_L_S(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_L_D(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_L_W(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_L_L(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_W_S(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_W_D(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_W_W(u8 fd, u8 fs) -> void;
|
||||
auto FCVT_W_L(u8 fd, u8 fs) -> void;
|
||||
auto FDIV_S(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto FDIV_D(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto FFLOOR_L_S(u8 fd, u8 fs) -> void;
|
||||
auto FFLOOR_L_D(u8 fd, u8 fs) -> void;
|
||||
auto FFLOOR_L_W(u8 fd, u8 fs) -> void;
|
||||
auto FFLOOR_L_L(u8 fd, u8 fs) -> void;
|
||||
auto FFLOOR_W_S(u8 fd, u8 fs) -> void;
|
||||
auto FFLOOR_W_D(u8 fd, u8 fs) -> void;
|
||||
auto FFLOOR_W_W(u8 fd, u8 fs) -> void;
|
||||
auto FFLOOR_W_L(u8 fd, u8 fs) -> void;
|
||||
auto FMOV_S(u8 fd, u8 fs) -> void;
|
||||
auto FMOV_D(u8 fd, u8 fs) -> void;
|
||||
auto FMUL_S(u8 fd, u8 fs, u8 ft) -> void;
|
||||
|
@ -709,22 +757,44 @@ struct CPU : Thread {
|
|||
auto FNEG_D(u8 fd, u8 fs) -> void;
|
||||
auto FROUND_L_S(u8 fd, u8 fs) -> void;
|
||||
auto FROUND_L_D(u8 fd, u8 fs) -> void;
|
||||
auto FROUND_L_W(u8 fd, u8 fs) -> void;
|
||||
auto FROUND_L_L(u8 fd, u8 fs) -> void;
|
||||
auto FROUND_W_S(u8 fd, u8 fs) -> void;
|
||||
auto FROUND_W_D(u8 fd, u8 fs) -> void;
|
||||
auto FROUND_W_W(u8 fd, u8 fs) -> void;
|
||||
auto FROUND_W_L(u8 fd, u8 fs) -> void;
|
||||
auto FSQRT_S(u8 fd, u8 fs) -> void;
|
||||
auto FSQRT_D(u8 fd, u8 fs) -> void;
|
||||
auto FSUB_S(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto FSUB_D(u8 fd, u8 fs, u8 ft) -> void;
|
||||
auto FTRUNC_L_S(u8 fd, u8 fs) -> void;
|
||||
auto FTRUNC_L_D(u8 fd, u8 fs) -> void;
|
||||
auto FTRUNC_L_W(u8 fd, u8 fs) -> void;
|
||||
auto FTRUNC_L_L(u8 fd, u8 fs) -> void;
|
||||
auto FTRUNC_W_S(u8 fd, u8 fs) -> void;
|
||||
auto FTRUNC_W_D(u8 fd, u8 fs) -> void;
|
||||
auto FTRUNC_W_W(u8 fd, u8 fs) -> void;
|
||||
auto FTRUNC_W_L(u8 fd, u8 fs) -> void;
|
||||
auto LDC1(u8 ft, cr64& rs, s16 imm) -> void;
|
||||
auto LWC1(u8 ft, cr64& rs, s16 imm) -> void;
|
||||
auto MFC1(r64& rt, u8 fs) -> void;
|
||||
auto MTC1(cr64& rt, u8 fs) -> void;
|
||||
auto SDC1(u8 ft, cr64& rs, s16 imm) -> void;
|
||||
auto SWC1(u8 ft, cr64& rs, s16 imm) -> void;
|
||||
auto COP1UNIMPLEMENTED() -> void;
|
||||
|
||||
//interpreter-cop2.cpp
|
||||
struct COP2 {
|
||||
u64 latch;
|
||||
} cop2;
|
||||
|
||||
auto MFC2(r64& rt, u8 rd) -> void;
|
||||
auto DMFC2(r64& rt, u8 rd) -> void;
|
||||
auto CFC2(r64& rt, u8 rd) -> void;
|
||||
auto MTC2(cr64& rt, u8 rd) -> void;
|
||||
auto DMTC2(cr64& rt, u8 rd) -> void;
|
||||
auto CTC2(cr64& rt, u8 rd) -> void;
|
||||
auto COP2INVALID() -> void;
|
||||
|
||||
//decoder.cpp
|
||||
auto decoderEXECUTE() -> void;
|
||||
|
@ -732,8 +802,8 @@ struct CPU : Thread {
|
|||
auto decoderREGIMM() -> void;
|
||||
auto decoderSCC() -> void;
|
||||
auto decoderFPU() -> void;
|
||||
auto decoderCOP2() -> void;
|
||||
|
||||
auto COP2() -> void;
|
||||
auto COP3() -> void;
|
||||
auto INVALID() -> void;
|
||||
|
||||
|
@ -795,6 +865,7 @@ struct CPU : Thread {
|
|||
auto emitREGIMM(u32 instruction) -> bool;
|
||||
auto emitSCC(u32 instruction) -> bool;
|
||||
auto emitFPU(u32 instruction) -> bool;
|
||||
auto emitCOP2(u32 instruction) -> bool;
|
||||
|
||||
bump_allocator allocator;
|
||||
Pool* pools[1 << 21]; //2_MiB * sizeof(void*) == 16_MiB
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
auto CPU::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.instruction = parent->append<Node::Debugger::Tracer::Instruction>("Instruction", "CPU");
|
||||
tracer.instruction->setAddressBits(64, 2);
|
||||
tracer.instruction->setDepth(64);
|
||||
|
||||
tracer.exception = parent->append<Node::Debugger::Tracer::Notification>("Exception", "CPU");
|
||||
tracer.interrupt = parent->append<Node::Debugger::Tracer::Notification>("Interrupt", "CPU");
|
||||
|
@ -48,6 +49,7 @@ auto CPU::Debugger::exception(u8 code) -> void {
|
|||
case 15: type = "floating point"; break;
|
||||
case 23: type = "watch address"; break;
|
||||
}
|
||||
type.append(string{" (PC=", hex(cpu.ipu.pc, 16L), ")"});
|
||||
tracer.exception->notify(type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -476,6 +476,6 @@ auto CPU::Disassembler::ccrRegisterValue(u32 index) const -> string {
|
|||
|
||||
template<typename... P>
|
||||
auto CPU::Disassembler::hint(P&&... p) const -> string {
|
||||
if(showColors) return {"\e[0m\e[37m", forward<P>(p)..., "\e[0m"};
|
||||
return {forward<P>(p)...};
|
||||
if(showColors) return {"\e[0m\e[37m", std::forward<P>(p)..., "\e[0m"};
|
||||
return {std::forward<P>(p)...};
|
||||
}
|
||||
|
|
|
@ -29,24 +29,25 @@ auto CPU::Exception::trigger(u32 code, u32 coprocessor, bool tlbMiss) -> void {
|
|||
self.context.setMode();
|
||||
}
|
||||
|
||||
auto CPU::Exception::interrupt() -> void { trigger( 0); }
|
||||
auto CPU::Exception::tlbModification() -> void { trigger( 1); }
|
||||
auto CPU::Exception::tlbLoadInvalid() -> void { trigger( 2, 0, 0); }
|
||||
auto CPU::Exception::tlbLoadMiss() -> void { trigger( 2, 0, 1); }
|
||||
auto CPU::Exception::tlbStoreInvalid() -> void { trigger( 3, 0, 0); }
|
||||
auto CPU::Exception::tlbStoreMiss() -> void { trigger( 3, 0, 1); }
|
||||
auto CPU::Exception::addressLoad() -> void { trigger( 4); }
|
||||
auto CPU::Exception::addressStore() -> void { trigger( 5); }
|
||||
auto CPU::Exception::busInstruction() -> void { trigger( 6); }
|
||||
auto CPU::Exception::busData() -> void { trigger( 7); }
|
||||
auto CPU::Exception::systemCall() -> void { trigger( 8); }
|
||||
auto CPU::Exception::breakpoint() -> void { trigger( 9); }
|
||||
auto CPU::Exception::reservedInstruction() -> void { trigger(10); }
|
||||
auto CPU::Exception::coprocessor0() -> void { trigger(11, 0); }
|
||||
auto CPU::Exception::coprocessor1() -> void { trigger(11, 1); }
|
||||
auto CPU::Exception::coprocessor2() -> void { trigger(11, 2); }
|
||||
auto CPU::Exception::coprocessor3() -> void { trigger(11, 3); }
|
||||
auto CPU::Exception::arithmeticOverflow() -> void { trigger(12); }
|
||||
auto CPU::Exception::trap() -> void { trigger(13); }
|
||||
auto CPU::Exception::floatingPoint() -> void { trigger(15); }
|
||||
auto CPU::Exception::watchAddress() -> void { trigger(23); }
|
||||
auto CPU::Exception::interrupt() -> void { trigger( 0); }
|
||||
auto CPU::Exception::tlbModification() -> void { trigger( 1); }
|
||||
auto CPU::Exception::tlbLoadInvalid() -> void { trigger( 2, 0, 0); }
|
||||
auto CPU::Exception::tlbLoadMiss() -> void { trigger( 2, 0, 1); }
|
||||
auto CPU::Exception::tlbStoreInvalid() -> void { trigger( 3, 0, 0); }
|
||||
auto CPU::Exception::tlbStoreMiss() -> void { trigger( 3, 0, 1); }
|
||||
auto CPU::Exception::addressLoad() -> void { trigger( 4); }
|
||||
auto CPU::Exception::addressStore() -> void { trigger( 5); }
|
||||
auto CPU::Exception::busInstruction() -> void { trigger( 6); }
|
||||
auto CPU::Exception::busData() -> void { trigger( 7); }
|
||||
auto CPU::Exception::systemCall() -> void { trigger( 8); }
|
||||
auto CPU::Exception::breakpoint() -> void { trigger( 9); }
|
||||
auto CPU::Exception::reservedInstruction() -> void { trigger(10); }
|
||||
auto CPU::Exception::reservedInstructionCop2() -> void { trigger(10, 2); }
|
||||
auto CPU::Exception::coprocessor0() -> void { trigger(11, 0); }
|
||||
auto CPU::Exception::coprocessor1() -> void { trigger(11, 1); }
|
||||
auto CPU::Exception::coprocessor2() -> void { trigger(11, 2); }
|
||||
auto CPU::Exception::coprocessor3() -> void { trigger(11, 3); }
|
||||
auto CPU::Exception::arithmeticOverflow() -> void { trigger(12); }
|
||||
auto CPU::Exception::trap() -> void { trigger(13); }
|
||||
auto CPU::Exception::floatingPoint() -> void { trigger(15); }
|
||||
auto CPU::Exception::watchAddress() -> void { trigger(23); }
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
auto CPU::MFC2(r64& rt, u8 rd) -> void {
|
||||
if(!scc.status.enable.coprocessor2) return exception.coprocessor2();
|
||||
rt.u64 = s32(cop2.latch);
|
||||
}
|
||||
|
||||
auto CPU::DMFC2(r64& rt, u8 rd) -> void {
|
||||
if(!scc.status.enable.coprocessor2) return exception.coprocessor2();
|
||||
rt.u64 = cop2.latch;
|
||||
}
|
||||
|
||||
auto CPU::CFC2(r64& rt, u8 rd) -> void {
|
||||
if(!scc.status.enable.coprocessor2) return exception.coprocessor2();
|
||||
rt.u64 = s32(cop2.latch);
|
||||
}
|
||||
|
||||
auto CPU::MTC2(cr64& rt, u8 rd) -> void {
|
||||
if(!scc.status.enable.coprocessor2) return exception.coprocessor2();
|
||||
cop2.latch = rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::DMTC2(cr64& rt, u8 rd) -> void {
|
||||
if(!scc.status.enable.coprocessor2) return exception.coprocessor2();
|
||||
cop2.latch = rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::CTC2(cr64& rt, u8 rd) -> void {
|
||||
if(!scc.status.enable.coprocessor2) return exception.coprocessor2();
|
||||
cop2.latch = rt.u64;
|
||||
}
|
||||
|
||||
auto CPU::COP2INVALID() -> void {
|
||||
if(!scc.status.enable.coprocessor2) return exception.coprocessor2();
|
||||
exception.reservedInstructionCop2();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -38,7 +38,7 @@ auto CPU::decoderEXECUTE() -> void {
|
|||
op(0x0f, LUI, RT, IMMu16);
|
||||
jp(0x10, SCC);
|
||||
jp(0x11, FPU);
|
||||
br(0x12, COP2);
|
||||
jp(0x12, COP2);
|
||||
br(0x13, COP3);
|
||||
br(0x14, BEQL, RS, RT, IMMi16);
|
||||
br(0x15, BNEL, RS, RT, IMMi16);
|
||||
|
@ -70,19 +70,19 @@ auto CPU::decoderEXECUTE() -> void {
|
|||
op(0x2f, CACHE, OP >> 16 & 31, RS, IMMi16);
|
||||
op(0x30, LL, RT, RS, IMMi16);
|
||||
op(0x31, LWC1, FT, RS, IMMi16);
|
||||
br(0x32, COP2); //LWC2
|
||||
jp(0x32, COP2); //LWC2
|
||||
br(0x33, COP3); //LWC3
|
||||
op(0x34, LLD, RT, RS, IMMi16);
|
||||
op(0x35, LDC1, FT, RS, IMMi16);
|
||||
br(0x36, COP2); //LDC2
|
||||
jp(0x36, COP2); //LDC2
|
||||
op(0x37, LD, RT, RS, IMMi16);
|
||||
op(0x38, SC, RT, RS, IMMi16);
|
||||
op(0x39, SWC1, FT, RS, IMMi16);
|
||||
br(0x3a, COP2); //SWC2
|
||||
jp(0x3a, COP2); //SWC2
|
||||
br(0x3b, COP3); //SWC3
|
||||
op(0x3c, SCD, RT, RS, IMMi16);
|
||||
op(0x3d, SDC1, FT, RS, IMMi16);
|
||||
br(0x3e, COP2); //SDC2
|
||||
jp(0x3e, COP2); //SDC2
|
||||
op(0x3f, SD, RT, RS, IMMi16);
|
||||
}
|
||||
}
|
||||
|
@ -229,11 +229,11 @@ auto CPU::decoderFPU() -> void {
|
|||
op(0x00, MFC1, RT, FS);
|
||||
op(0x01, DMFC1, RT, FS);
|
||||
op(0x02, CFC1, RT, RDn);
|
||||
br(0x03, INVALID);
|
||||
br(0x03, DCFC1, RT, RDn);
|
||||
op(0x04, MTC1, RT, FS);
|
||||
op(0x05, DMTC1, RT, FS);
|
||||
op(0x06, CTC1, RT, RDn);
|
||||
br(0x07, INVALID);
|
||||
br(0x07, DCTC1, RT, RDn);
|
||||
br(0x08, BC1, OP >> 16 & 1, OP >> 17 & 1, IMMi16);
|
||||
br(0x09, INVALID);
|
||||
br(0x0a, INVALID);
|
||||
|
@ -262,6 +262,7 @@ auto CPU::decoderFPU() -> void {
|
|||
op(0x0d, FTRUNC_W_S, FD, FS);
|
||||
op(0x0e, FCEIL_W_S, FD, FS);
|
||||
op(0x0f, FFLOOR_W_S, FD, FS);
|
||||
op(0x20, FCVT_S_S, FD, FS);
|
||||
op(0x21, FCVT_D_S, FD, FS);
|
||||
op(0x24, FCVT_W_S, FD, FS);
|
||||
op(0x25, FCVT_L_S, FD, FS);
|
||||
|
@ -302,6 +303,7 @@ auto CPU::decoderFPU() -> void {
|
|||
op(0x0e, FCEIL_W_D, FD, FS);
|
||||
op(0x0f, FFLOOR_W_D, FD, FS);
|
||||
op(0x20, FCVT_S_D, FD, FS);
|
||||
op(0x21, FCVT_D_D, FD, FS);
|
||||
op(0x24, FCVT_W_D, FD, FS);
|
||||
op(0x25, FCVT_L_D, FD, FS);
|
||||
op(0x30, FC_F_D, FS, FT);
|
||||
|
@ -324,25 +326,53 @@ auto CPU::decoderFPU() -> void {
|
|||
|
||||
if((OP >> 21 & 31) == 20)
|
||||
switch(OP & 0x3f) {
|
||||
op(0x08, FROUND_L_W, FD, FS);
|
||||
op(0x09, FTRUNC_L_W, FD, FS);
|
||||
op(0x0a, FCEIL_L_W, FD, FS);
|
||||
op(0x0b, FFLOOR_L_W, FD, FS);
|
||||
op(0x0c, FROUND_W_W, FD, FS);
|
||||
op(0x0d, FTRUNC_W_W, FD, FS);
|
||||
op(0x0e, FCEIL_W_W, FD, FS);
|
||||
op(0x0f, FFLOOR_W_W, FD, FS);
|
||||
op(0x20, FCVT_S_W, FD, FS);
|
||||
op(0x21, FCVT_D_W, FD, FS);
|
||||
op(0x24, FCVT_W_W, FD, FS);
|
||||
op(0x25, FCVT_L_W, FD, FS);
|
||||
}
|
||||
|
||||
if((OP >> 21 & 31) == 21)
|
||||
switch(OP & 0x3f) {
|
||||
op(0x08, FROUND_L_L, FD, FS);
|
||||
op(0x09, FTRUNC_L_L, FD, FS);
|
||||
op(0x0a, FCEIL_L_L, FD, FS);
|
||||
op(0x0b, FFLOOR_L_L, FD, FS);
|
||||
op(0x0c, FROUND_W_L, FD, FS);
|
||||
op(0x0d, FTRUNC_W_L, FD, FS);
|
||||
op(0x0e, FCEIL_W_L, FD, FS);
|
||||
op(0x0f, FFLOOR_W_L, FD, FS);
|
||||
op(0x20, FCVT_S_L, FD, FS);
|
||||
op(0x21, FCVT_D_L, FD, FS);
|
||||
op(0x24, FCVT_W_L, FD, FS);
|
||||
op(0x25, FCVT_L_L, FD, FS);
|
||||
}
|
||||
|
||||
//undefined instructions do not throw a reserved instruction exception
|
||||
}
|
||||
|
||||
auto CPU::COP2() -> void {
|
||||
exception.coprocessor2();
|
||||
auto CPU::decoderCOP2() -> void {
|
||||
switch(OP >> 21 & 0x1f) {
|
||||
op(0x00, MFC2, RT, RDn);
|
||||
op(0x01, DMFC2, RT, RDn);
|
||||
op(0x02, CFC2, RT, RDn);
|
||||
op(0x04, MTC2, RT, RDn);
|
||||
op(0x05, DMTC2, RT, RDn);
|
||||
op(0x06, CTC2, RT, RDn);
|
||||
}
|
||||
COP2INVALID();
|
||||
}
|
||||
|
||||
auto CPU::COP3() -> void {
|
||||
exception.coprocessor3();
|
||||
exception.reservedInstruction();
|
||||
}
|
||||
|
||||
auto CPU::INVALID() -> void {
|
||||
|
|
|
@ -204,8 +204,7 @@ auto CPU::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
|
||||
//COP2
|
||||
case 0x12: {
|
||||
call(&CPU::COP2);
|
||||
return 1;
|
||||
return emitCOP2(instruction);
|
||||
}
|
||||
|
||||
//COP3
|
||||
|
@ -454,7 +453,7 @@ auto CPU::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
|
||||
//LWC2
|
||||
case 0x32: {
|
||||
call(&CPU::COP2);
|
||||
call(&CPU::COP2INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -484,7 +483,7 @@ auto CPU::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
|
||||
//LDC2
|
||||
case 0x36: {
|
||||
call(&CPU::COP2);
|
||||
call(&CPU::COP2INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -517,7 +516,7 @@ auto CPU::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
|
||||
//SWC2
|
||||
case 0x3a: {
|
||||
call(&CPU::COP2);
|
||||
call(&CPU::COP2INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -547,7 +546,7 @@ auto CPU::Recompiler::emitEXECUTE(u32 instruction) -> bool {
|
|||
|
||||
//SDC2
|
||||
case 0x3e: {
|
||||
call(&CPU::COP2);
|
||||
call(&CPU::COP2INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1303,9 +1302,9 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
return 0;
|
||||
}
|
||||
|
||||
//INVALID
|
||||
//DCFC1 Rt,Rd
|
||||
case 0x03: {
|
||||
call(&CPU::INVALID);
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1333,9 +1332,9 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
return 0;
|
||||
}
|
||||
|
||||
//INVALID
|
||||
//DCTC1 Rt,Rd
|
||||
case 0x07: {
|
||||
call(&CPU::INVALID);
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1491,6 +1490,14 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
return 0;
|
||||
}
|
||||
|
||||
//FCVT.S.S Fd,Fs
|
||||
case 0x20: {
|
||||
mov32(reg(1), imm(Fdn));
|
||||
mov32(reg(2), imm(Fsn));
|
||||
call(&CPU::FCVT_S_S);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//FCVT.D.S Fd,Fs
|
||||
case 0x21: {
|
||||
mov32(reg(1), imm(Fdn));
|
||||
|
@ -1788,6 +1795,14 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
return 0;
|
||||
}
|
||||
|
||||
//FCVT.D.D Fd,Fs
|
||||
case 0x21: {
|
||||
mov32(reg(1), imm(Fdn));
|
||||
mov32(reg(2), imm(Fsn));
|
||||
call(&CPU::FCVT_D_D);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//FCVT.W.D Fd,Fs
|
||||
case 0x24: {
|
||||
mov32(reg(1), imm(Fdn));
|
||||
|
@ -1935,7 +1950,16 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
}
|
||||
|
||||
if((instruction >> 21 & 31) == 20)
|
||||
switch(instruction & 0x3f) {
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x08 ... 0x0f: {
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case 0x24 ... 0x25: {
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//FCVT.S.W Fd,Fs
|
||||
case 0x20: {
|
||||
|
@ -1957,6 +1981,14 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
|
||||
if((instruction >> 21 & 31) == 21)
|
||||
switch(instruction & 0x3f) {
|
||||
case 0x08 ... 0x0f: {
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
case 0x24 ... 0x25: {
|
||||
call(&CPU::COP1UNIMPLEMENTED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//FCVT.S.L
|
||||
case 0x20: {
|
||||
|
@ -1979,6 +2011,73 @@ auto CPU::Recompiler::emitFPU(u32 instruction) -> bool {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::emitCOP2(u32 instruction) -> bool {
|
||||
switch(instruction >> 21 & 0x1f) {
|
||||
|
||||
//MFC2 Rt,Rd
|
||||
case 0x00: {
|
||||
lea(reg(1), Rt);
|
||||
mov32(reg(2), imm(Rdn));
|
||||
call(&CPU::MFC2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//DMFC2 Rt,Rd
|
||||
case 0x01: {
|
||||
lea(reg(1), Rt);
|
||||
mov32(reg(2), imm(Rdn));
|
||||
call(&CPU::DMFC2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//CFC2 Rt,Rd
|
||||
case 0x02: {
|
||||
lea(reg(1), Rt);
|
||||
mov32(reg(2), imm(Rdn));
|
||||
call(&CPU::CFC2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//INVALID
|
||||
case 0x03: {
|
||||
call(&CPU::COP2INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//MTC0 Rt,Rd
|
||||
case 0x04: {
|
||||
lea(reg(1), Rt);
|
||||
mov32(reg(2), imm(Rdn));
|
||||
call(&CPU::MTC2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//DMTC2 Rt,Rd
|
||||
case 0x05: {
|
||||
lea(reg(1), Rt);
|
||||
mov32(reg(2), imm(Rdn));
|
||||
call(&CPU::DMTC2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//CTC2 Rt,Rd
|
||||
case 0x06: {
|
||||
lea(reg(1), Rt);
|
||||
mov32(reg(2), imm(Rdn));
|
||||
call(&CPU::CTC2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//INVALID
|
||||
case 0x07 ... 0x0f: {
|
||||
call(&CPU::COP2INVALID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef Sa
|
||||
#undef Rdn
|
||||
#undef Rtn
|
||||
|
|
|
@ -136,7 +136,9 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
s(fpu.csr.cause.invalidOperation);
|
||||
s(fpu.csr.cause.unimplementedOperation);
|
||||
s(fpu.csr.compare);
|
||||
s(fpu.csr.flushed);
|
||||
s(fpu.csr.flushSubnormals);
|
||||
|
||||
s(cop2.latch);
|
||||
|
||||
if constexpr(Accuracy::CPU::Recompiler) {
|
||||
recompiler.reset();
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
auto DD::command(n16 command) -> void {
|
||||
ctl.error.selfDiagnostic = 0;
|
||||
ctl.error.servoNG = 0;
|
||||
ctl.error.indexGapNG = 0;
|
||||
ctl.error.timeout = 0;
|
||||
ctl.error.undefinedCommand = 0;
|
||||
ctl.error.invalidParam = 0;
|
||||
io.status.mechaError = 0;
|
||||
io.status.writeProtect = 0;
|
||||
|
||||
if(io.status.busyState) return;
|
||||
io.status.busyState = 1;
|
||||
|
||||
//most simple command timing response, based on Command::ReadProgramVersion
|
||||
u32 count = 8000;
|
||||
|
||||
switch(command) {
|
||||
case Command::Nop: {} break;
|
||||
case Command::ReadSeek: {
|
||||
if(disk) {
|
||||
if((io.data.bit(12,15) > 1) || (io.data.bit(0,11) > 0x5D7)) {
|
||||
ctl.error.invalidParam = 1;
|
||||
} else {
|
||||
//TODO: proper research into seek and access times
|
||||
count = 993'000;
|
||||
if(io.status.headRetracted || io.status.spindleMotorStopped)
|
||||
count += 200'700'000;
|
||||
count += 19300 * abs(io.data.bit(0,11) - io.currentTrack.bit(0,11));
|
||||
io.currentTrack = io.data | 0x6000;
|
||||
seekTrack();
|
||||
queue.remove(Queue::DD_Motor_Mode);
|
||||
io.status.headRetracted = 0;
|
||||
io.status.spindleMotorStopped = 0;
|
||||
state.seek = 1;
|
||||
}
|
||||
} else {
|
||||
ctl.error.selfDiagnostic = 1;
|
||||
}
|
||||
} break;
|
||||
case Command::WriteSeek: {
|
||||
if(disk) {
|
||||
if((io.data.bit(12,15) > 1) || (io.data.bit(0,11) > 0x5D7)) {
|
||||
ctl.error.invalidParam = 1;
|
||||
} else {
|
||||
//TODO: proper research into seek and access times
|
||||
count = 993'000;
|
||||
if(io.status.headRetracted || io.status.spindleMotorStopped)
|
||||
count += 200'700'000;
|
||||
count += 19300 * abs(io.data.bit(0,11) - io.currentTrack.bit(0,11));
|
||||
io.currentTrack = io.data | 0x6000;
|
||||
io.status.writeProtect = seekTrack();
|
||||
queue.remove(Queue::DD_Motor_Mode);
|
||||
io.status.headRetracted = 0;
|
||||
io.status.spindleMotorStopped = 0;
|
||||
state.seek = 1;
|
||||
}
|
||||
} else {
|
||||
ctl.error.selfDiagnostic = 1;
|
||||
}
|
||||
} break;
|
||||
case Command::Recalibration:
|
||||
case Command::Start: {
|
||||
//identical commands
|
||||
if(disk) {
|
||||
//TODO: proper research into seek and access times
|
||||
//seek to head 0 track 0
|
||||
count = 993'000;
|
||||
if(io.status.headRetracted || io.status.spindleMotorStopped)
|
||||
count += 200'700'000;
|
||||
count += 19300 * abs(0 - io.currentTrack.bit(0,11));
|
||||
io.currentTrack = 0x6000;
|
||||
seekTrack();
|
||||
queue.remove(Queue::DD_Motor_Mode);
|
||||
io.status.headRetracted = 0;
|
||||
io.status.spindleMotorStopped = 0;
|
||||
state.seek = 1;
|
||||
} else {
|
||||
ctl.error.selfDiagnostic = 1;
|
||||
}
|
||||
} break;
|
||||
case Command::Sleep: {
|
||||
if(!io.status.headRetracted || !io.status.spindleMotorStopped)
|
||||
count = 83'000'000;
|
||||
motorStop();
|
||||
} break;
|
||||
case Command::SetStandby: {
|
||||
if (!io.data.bit(24)) {
|
||||
ctl.standbyDelayDisable = 1;
|
||||
} else {
|
||||
ctl.standbyDelayDisable = 0;
|
||||
|
||||
ctl.standbyDelay = io.data.bit(16,23);
|
||||
if (ctl.standbyDelay < 1) ctl.standbyDelay = 1;
|
||||
if (ctl.standbyDelay > 0x10) ctl.standbyDelay = 0x10;
|
||||
ctl.standbyDelay *= 0x17;
|
||||
}
|
||||
} break;
|
||||
case Command::SetSleep: {
|
||||
if (!io.data.bit(24)) {
|
||||
ctl.sleepDelayDisable = 1;
|
||||
} else {
|
||||
ctl.sleepDelayDisable = 0;
|
||||
|
||||
ctl.sleepDelay = io.data.bit(16,23);
|
||||
if (ctl.sleepDelay > 0x96) ctl.sleepDelay = 0x96;
|
||||
ctl.sleepDelay *= 0x17;
|
||||
}
|
||||
} break;
|
||||
case Command::ClearChangeFlag: {
|
||||
io.status.diskChanged = 0;
|
||||
} break;
|
||||
case Command::ClearResetFlag: {
|
||||
io.status.diskChanged = 0; //that's how it works
|
||||
io.status.resetState = 0;
|
||||
} break;
|
||||
case Command::ReadVersion: {
|
||||
if (!io.data.bit(0)) io.data = 0x0114;
|
||||
else io.data = 0x5300;
|
||||
} break;
|
||||
case Command::SetDiskType: {
|
||||
ctl.diskType = io.data.bit(0, 3);
|
||||
} break;
|
||||
case Command::RequestStatus: {
|
||||
io.data.bit(0) = ctl.error.selfDiagnostic;
|
||||
io.data.bit(1) = ctl.error.servoNG;
|
||||
io.data.bit(2) = ctl.error.indexGapNG;
|
||||
io.data.bit(3) = ctl.error.timeout;
|
||||
io.data.bit(4) = ctl.error.undefinedCommand;
|
||||
io.data.bit(5) = ctl.error.invalidParam;
|
||||
io.data.bit(6) = ctl.error.unknown;
|
||||
} break;
|
||||
case Command::Standby: {
|
||||
if(!disk) {
|
||||
ctl.error.selfDiagnostic = 1;
|
||||
} else {
|
||||
if(!io.status.headRetracted || !io.status.spindleMotorStopped)
|
||||
count = 64'000'000;
|
||||
motorStandby();
|
||||
}
|
||||
} break;
|
||||
case Command::IndexLockRetry: {
|
||||
if(!disk) {
|
||||
ctl.error.selfDiagnostic = 1;
|
||||
}
|
||||
} break;
|
||||
case Command::SetRTCYearMonth: {
|
||||
rtc.write<Half>(0, io.data);
|
||||
} break;
|
||||
case Command::SetRTCDayHour: {
|
||||
rtc.write<Half>(2, io.data);
|
||||
} break;
|
||||
case Command::SetRTCMinuteSecond: {
|
||||
rtc.write<Half>(4, io.data);
|
||||
} break;
|
||||
case Command::GetRTCYearMonth: {
|
||||
io.data = rtc.read<Half>(0);
|
||||
} break;
|
||||
case Command::GetRTCDayHour: {
|
||||
io.data = rtc.read<Half>(2);
|
||||
} break;
|
||||
case Command::GetRTCMinuteSecond: {
|
||||
io.data = rtc.read<Half>(4);
|
||||
} break;
|
||||
case Command::SetLEDBlinkRate: {
|
||||
if (io.data.bit(24,31) != 0) ctl.ledOnTime = io.data.bit(24,31);
|
||||
if (io.data.bit(16,23) != 0) ctl.ledOffTime = io.data.bit(16,23);
|
||||
} break;
|
||||
case Command::ReadProgramVersion: {
|
||||
io.data = 0x0003;
|
||||
} break;
|
||||
default: {
|
||||
ctl.error.undefinedCommand = 1;
|
||||
} break;
|
||||
}
|
||||
|
||||
if(ctl.error.selfDiagnostic) io.status.mechaError = 1;
|
||||
else if(ctl.error.servoNG) io.status.mechaError = 1;
|
||||
else if(ctl.error.indexGapNG) io.status.mechaError = 1;
|
||||
else if(ctl.error.timeout) io.status.mechaError = 1;
|
||||
else if(ctl.error.undefinedCommand) io.status.mechaError = 1;
|
||||
else if(ctl.error.invalidParam) io.status.mechaError = 1;
|
||||
else if(io.status.writeProtect) io.status.mechaError = 1;
|
||||
|
||||
queue.insert(Queue::DD_MECHA_Response, count);
|
||||
}
|
||||
|
||||
auto DD::mechaResponse() -> void {
|
||||
if(state.seek) {
|
||||
state.seek = 0;
|
||||
motorActive();
|
||||
}
|
||||
io.status.busyState = 0;
|
||||
raise(IRQ::MECHA);
|
||||
}
|
|
@ -3,41 +3,149 @@
|
|||
namespace ares::Nintendo64 {
|
||||
|
||||
DD dd;
|
||||
#include "controller.cpp"
|
||||
#include "drive.cpp"
|
||||
#include "rtc.cpp"
|
||||
#include "io.cpp"
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto DD::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("Disk Drive");
|
||||
obj = parent->append<Node::Object>("Nintendo 64DD");
|
||||
port = obj->append<Node::Port>("Disk Drive");
|
||||
port->setFamily("Nintendo 64DD");
|
||||
port->setType("Floppy Disk");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(port); });
|
||||
port->setConnect([&] { return connect(); });
|
||||
port->setDisconnect([&] { return disconnect(); });
|
||||
|
||||
iplrom.allocate(4_MiB);
|
||||
c2s.allocate(0x400);
|
||||
ds.allocate(0x100);
|
||||
ms.allocate(0x40);
|
||||
rtc.allocate(0x10);
|
||||
|
||||
if(node->setPak(pak = platform->pak(node))) {
|
||||
if(auto fp = pak->read("64dd.ipl.rom")) {
|
||||
iplrom.load(fp);
|
||||
}
|
||||
// TODO: Detect correct CIC from ipl rom
|
||||
if(auto fp = system.pak->read("64dd.ipl.rom")) {
|
||||
iplrom.load(fp);
|
||||
}
|
||||
|
||||
debugger.load(node);
|
||||
debugger.load(parent->append<Node::Object>("Nintendo 64DD"));
|
||||
}
|
||||
|
||||
auto DD::unload() -> void {
|
||||
if(!node) return;
|
||||
disconnect();
|
||||
|
||||
debugger = {};
|
||||
iplrom.reset();
|
||||
c2s.reset();
|
||||
ds.reset();
|
||||
ms.reset();
|
||||
pak.reset();
|
||||
rtc.reset();
|
||||
disk.reset();
|
||||
port.reset();
|
||||
node.reset();
|
||||
obj.reset();
|
||||
}
|
||||
|
||||
auto DD::allocate(Node::Port parent) -> Node::Peripheral {
|
||||
return node = parent->append<Node::Peripheral>("Nintendo 64DD Disk");
|
||||
}
|
||||
|
||||
auto DD::connect() -> void {
|
||||
if(!node->setPak(pak = platform->pak(node))) return;
|
||||
|
||||
information = {};
|
||||
information.title = pak->attribute("title");
|
||||
|
||||
if(iplrom) {
|
||||
string id;
|
||||
id.append((char)iplrom.read<Byte>(0x3b));
|
||||
id.append((char)iplrom.read<Byte>(0x3c));
|
||||
id.append((char)iplrom.read<Byte>(0x3d));
|
||||
id.append((char)iplrom.read<Byte>(0x3e));
|
||||
if(id.match("NDDJ")) dd.information.cic = "CIC-NUS-8303";
|
||||
if(id.match("NDDE")) dd.information.cic = "CIC-NUS-DDUS";
|
||||
if(id.match("NDXJ")) dd.information.cic = "CIC-NUS-8401";
|
||||
}
|
||||
|
||||
if(auto fp = pak->read("program.disk.error")) {
|
||||
error.allocate(fp->size());
|
||||
error.load(fp);
|
||||
}
|
||||
|
||||
if(auto fp = pak->read("program.disk")) {
|
||||
disk.allocate(fp->size());
|
||||
disk.load(fp);
|
||||
}
|
||||
|
||||
rtcLoad();
|
||||
}
|
||||
|
||||
auto DD::disconnect() -> void {
|
||||
if(!port) return;
|
||||
|
||||
save();
|
||||
pak.reset();
|
||||
information = {};
|
||||
}
|
||||
|
||||
auto DD::save() -> void {
|
||||
/*if(disk)
|
||||
if(auto fp = pak->write("program.disk")) {
|
||||
disk.save(fp);
|
||||
}*/
|
||||
|
||||
rtcSave();
|
||||
}
|
||||
|
||||
auto DD::power(bool reset) -> void {
|
||||
c2s.fill();
|
||||
ds.fill();
|
||||
ms.fill();
|
||||
|
||||
irq = {};
|
||||
ctl = {};
|
||||
io = {};
|
||||
state = {};
|
||||
|
||||
io.status.resetState = 1;
|
||||
io.id = 3;
|
||||
if(dd.information.cic.match("CIC-NUS-8401")) io.id = 4;
|
||||
|
||||
motorStop();
|
||||
|
||||
queue.insert(Queue::DD_Clock_Tick, 187'500'000);
|
||||
queue.remove(Queue::DD_MECHA_Response);
|
||||
queue.remove(Queue::DD_BM_Request);
|
||||
lower(IRQ::MECHA);
|
||||
lower(IRQ::BM);
|
||||
}
|
||||
|
||||
auto DD::raise(IRQ source) -> void {
|
||||
debugger.interrupt((u32)source);
|
||||
switch(source) {
|
||||
case IRQ::MECHA: irq.mecha.line = 1; break;
|
||||
case IRQ::BM: irq.bm.line = 1; break;
|
||||
}
|
||||
poll();
|
||||
}
|
||||
|
||||
auto DD::lower(IRQ source) -> void {
|
||||
switch(source) {
|
||||
case IRQ::MECHA: irq.mecha.line = 0; break;
|
||||
case IRQ::BM: irq.bm.line = 0; break;
|
||||
}
|
||||
poll();
|
||||
}
|
||||
|
||||
auto DD::poll() -> void {
|
||||
bool line = 0;
|
||||
line |= irq.mecha.line & irq.mecha.mask;
|
||||
line |= irq.bm.line & irq.bm.mask;
|
||||
cpu.scc.cause.interruptPending.bit(3) = line;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,34 +1,209 @@
|
|||
//Disk Drive
|
||||
|
||||
struct DD : Memory::IO<DD> {
|
||||
Node::Object node;
|
||||
Node::Object obj;
|
||||
Node::Port port;
|
||||
Node::Peripheral node;
|
||||
VFS::Pak pak;
|
||||
Memory::Readable iplrom;
|
||||
Memory::Writable c2s;
|
||||
Memory::Writable ds;
|
||||
Memory::Writable ms;
|
||||
Memory::Writable rtc;
|
||||
Memory::Writable disk;
|
||||
Memory::Writable error;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto interrupt(u8 source) -> void;
|
||||
auto io(bool mode, u32 address, u32 data) -> void;
|
||||
|
||||
struct Tracer {
|
||||
Node::Debugger::Tracer::Notification interrupt;
|
||||
Node::Debugger::Tracer::Notification io;
|
||||
} tracer;
|
||||
} debugger;
|
||||
|
||||
auto title() const -> string { return information.title; }
|
||||
auto cic() const -> string { return information.cic; }
|
||||
|
||||
//dd.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
auto allocate(Node::Port) -> Node::Peripheral;
|
||||
auto connect() -> void;
|
||||
auto disconnect() -> void;
|
||||
|
||||
auto save() -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
enum class IRQ : u32 { MECHA, BM };
|
||||
auto raise(IRQ) -> void;
|
||||
auto lower(IRQ) -> void;
|
||||
auto poll() -> void;
|
||||
|
||||
//controller.cpp
|
||||
auto command(n16 command) -> void;
|
||||
auto mechaResponse() -> void;
|
||||
|
||||
//drive.cpp
|
||||
auto seekTrack() -> n1;
|
||||
auto seekSector(n8 sector) -> u32;
|
||||
auto bmRequest() -> void;
|
||||
auto motorActive() -> void;
|
||||
auto motorStandby() -> void;
|
||||
auto motorStop() -> void;
|
||||
auto motorChange() -> void;
|
||||
|
||||
//rtc.cpp
|
||||
auto rtcLoad() -> void;
|
||||
auto rtcSave() -> void;
|
||||
auto rtcTick(u32 offset) -> void;
|
||||
auto rtcTickClock() -> void;
|
||||
auto rtcTickSecond() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto readWord(u32 address) -> u32;
|
||||
auto writeWord(u32 address, u32 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct Information {
|
||||
string title;
|
||||
string cic;
|
||||
} information;
|
||||
|
||||
struct BCD {
|
||||
static auto encode(u8 value) -> u8 { return value / 10 << 4 | value % 10; }
|
||||
static auto decode(u8 value) -> u8 { return (value >> 4) * 10 + (value & 15); }
|
||||
};
|
||||
|
||||
std::function<u64()> rtcCallback = []() { return 0; };
|
||||
|
||||
private:
|
||||
struct Interrupt {
|
||||
b1 line = 0;
|
||||
b1 mask = 1;
|
||||
};
|
||||
|
||||
struct IRQs {
|
||||
Interrupt mecha;
|
||||
Interrupt bm;
|
||||
} irq;
|
||||
|
||||
struct Controller {
|
||||
n8 diskType;
|
||||
struct {
|
||||
n1 selfDiagnostic;
|
||||
n1 servoNG;
|
||||
n1 indexGapNG;
|
||||
n1 timeout;
|
||||
n1 undefinedCommand;
|
||||
n1 invalidParam;
|
||||
n1 unknown;
|
||||
} error;
|
||||
n1 standbyDelayDisable = 0;
|
||||
n16 standbyDelay = 3 * 0x17;
|
||||
n1 sleepDelayDisable = 0;
|
||||
n16 sleepDelay = 1 * 0x17;
|
||||
n8 ledOnTime = 0x4;
|
||||
n8 ledOffTime = 0x4;
|
||||
} ctl;
|
||||
|
||||
struct IO {
|
||||
n16 data;
|
||||
|
||||
struct {
|
||||
n1 requestUserSector;
|
||||
n1 requestC2Sector;
|
||||
n1 busyState;
|
||||
n1 resetState;
|
||||
|
||||
n1 spindleMotorStopped;
|
||||
n1 headRetracted;
|
||||
|
||||
n1 writeProtect;
|
||||
n1 mechaError;
|
||||
n1 diskChanged;
|
||||
} status;
|
||||
|
||||
n16 currentTrack;
|
||||
n8 currentSector;
|
||||
|
||||
n8 sectorSizeBuf;
|
||||
n8 sectorSize;
|
||||
n8 sectorBlock;
|
||||
n16 id;
|
||||
|
||||
struct {
|
||||
//stat
|
||||
n1 start;
|
||||
n1 reset;
|
||||
n1 error;
|
||||
n1 blockTransfer;
|
||||
|
||||
n1 c1Correct;
|
||||
n1 c1Double;
|
||||
n1 c1Single;
|
||||
n1 c1Error;
|
||||
|
||||
//ctl
|
||||
n1 readMode;
|
||||
|
||||
n1 disableORcheck;
|
||||
n1 disableC1Correction;
|
||||
} bm;
|
||||
|
||||
struct {
|
||||
n1 am;
|
||||
|
||||
n1 spindle;
|
||||
n1 overrun;
|
||||
n1 offTrack;
|
||||
n1 clockUnlock;
|
||||
n1 selfStop;
|
||||
|
||||
n8 sector;
|
||||
} error;
|
||||
|
||||
struct {
|
||||
n1 enable;
|
||||
n1 error;
|
||||
} micro;
|
||||
} io;
|
||||
|
||||
struct State {
|
||||
n1 seek;
|
||||
} state;
|
||||
|
||||
struct Command { enum : u16 {
|
||||
Nop = 0x0, //no operation
|
||||
ReadSeek = 0x1, //seek (read)
|
||||
WriteSeek = 0x2, //seek (write)
|
||||
Recalibration = 0x3, //recalibration
|
||||
Sleep = 0x4, //drive sleep (stop motor and retract heads)
|
||||
Start = 0x5, //start the drive
|
||||
SetStandby = 0x6, //set auto standby time
|
||||
SetSleep = 0x7, //set auto sleep time
|
||||
ClearChangeFlag = 0x8, //clear disk changed flag
|
||||
ClearResetFlag = 0x9, //clear reset and disk changed flag
|
||||
ReadVersion = 0xa, //read asic version
|
||||
SetDiskType = 0xb, //set disk type (defines write protection)
|
||||
RequestStatus = 0xc, //request internal drive error status
|
||||
Standby = 0xd, //drive standby (set heads)
|
||||
IndexLockRetry = 0xe, //index lock retry (track index)
|
||||
SetRTCYearMonth = 0xf, //set year and month
|
||||
SetRTCDayHour = 0x10, //set day and hour
|
||||
SetRTCMinuteSecond = 0x11, //set minute and second
|
||||
GetRTCYearMonth = 0x12, //get year and month
|
||||
GetRTCDayHour = 0x13, //get day and hour
|
||||
GetRTCMinuteSecond = 0x14, //get minute and second (must be done first)
|
||||
SetLEDBlinkRate = 0x15, //set led blink rate
|
||||
ReadProgramVersion = 0x1b, //read program version
|
||||
};};
|
||||
};
|
||||
|
||||
extern DD dd;
|
||||
|
|
|
@ -1,7 +1,17 @@
|
|||
auto DD::Debugger::load(Node::Object parent) -> void {
|
||||
tracer.interrupt = parent->append<Node::Debugger::Tracer::Notification>("Interrupt", "DD");
|
||||
tracer.io = parent->append<Node::Debugger::Tracer::Notification>("I/O", "DD");
|
||||
}
|
||||
|
||||
auto DD::Debugger::interrupt(u8 source) -> void {
|
||||
if(unlikely(tracer.interrupt->enabled())) {
|
||||
string type = "unknown";
|
||||
if(source == (u32)DD::IRQ::MECHA) type = "MECHA";
|
||||
if(source == (u32)DD::IRQ::BM) type = "BM";
|
||||
tracer.interrupt->notify(type);
|
||||
}
|
||||
}
|
||||
|
||||
auto DD::Debugger::io(bool mode, u32 address, u32 data) -> void {
|
||||
static const vector<string> registerNames = {
|
||||
"ASIC_DATA",
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
auto DD::seekTrack() -> n1 {
|
||||
n16 trackCalc = io.currentTrack.bit(0,11);
|
||||
n1 headCalc = io.currentTrack.bit(12);
|
||||
u16 trackPhysicalTable[] = {0x09E, 0x13C, 0x1D1, 0x266, 0x2FB, 0x390, 0x425};
|
||||
n8 pzone = 0;
|
||||
for(u32 n : range(7)) {
|
||||
if(trackCalc >= trackPhysicalTable[n]) pzone++;
|
||||
}
|
||||
pzone += headCalc;
|
||||
|
||||
//return 1 if ROM area, return 0 if RAM area
|
||||
if (pzone <= (ctl.diskType + 2)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto DD::seekSector(n8 sector) -> u32 {
|
||||
n1 blockCalc = (sector >= 0x5A) ? 1 : 0;
|
||||
n8 sectorCalc = sector % 0x5A;
|
||||
n16 trackCalc = io.currentTrack.bit(0,11);
|
||||
n1 headCalc = io.currentTrack.bit(12);
|
||||
|
||||
u32 startOffsetTable[16] = {0x0,0x5F15E0,0xB79D00,0x10801A0,0x1523720,0x1963D80,0x1D414C0,0x20BBCE0,
|
||||
0x23196E0,0x28A1E00,0x2DF5DC0,0x3299340,0x36D99A0,0x3AB70E0,0x3E31900,0x4149200};
|
||||
|
||||
u16 trackPhysicalTable[] = {0x000, 0x09E, 0x13C, 0x1D1, 0x266, 0x2FB, 0x390, 0x425};
|
||||
u16 blockSizeTable[] = {0x4D08, 0x47B8, 0x4510, 0x3FC0, 0x3A70, 0x3520, 0x2FD0, 0x2A80,
|
||||
0x47B8, 0x4510, 0x3FC0, 0x3A70, 0x3520, 0x2FD0, 0x2A80, 0x2530};
|
||||
n8 pzone = 0;
|
||||
for(u32 n : range(7)) {
|
||||
if(trackCalc >= trackPhysicalTable[n + 1]) pzone++;
|
||||
}
|
||||
trackCalc -= trackPhysicalTable[pzone];
|
||||
pzone += (headCalc) ? 8 : 0;
|
||||
|
||||
u32 offsetCalc = startOffsetTable[pzone];
|
||||
offsetCalc += blockSizeTable[pzone] * 2 * trackCalc;
|
||||
offsetCalc += blockCalc * blockSizeTable[pzone];
|
||||
offsetCalc += sectorCalc * (io.sectorSizeBuf + 1);
|
||||
|
||||
//return disk data offset
|
||||
return offsetCalc;
|
||||
}
|
||||
|
||||
auto DD::bmRequest() -> void {
|
||||
//if BM not started make sure to not do anything
|
||||
if(!io.bm.start) {
|
||||
queue.remove(Queue::DD_BM_Request);
|
||||
lower(IRQ::BM);
|
||||
return;
|
||||
}
|
||||
|
||||
//reset register state
|
||||
io.status.requestUserSector = 0;
|
||||
io.status.requestC2Sector = 0;
|
||||
io.micro.error = 0;
|
||||
io.bm.error = 0;
|
||||
io.bm.c1Single = 0;
|
||||
io.bm.c1Double = 0;
|
||||
io.error.am = 0;
|
||||
io.error.clockUnlock = 0;
|
||||
io.error.offTrack = 0;
|
||||
io.error.overrun = 0;
|
||||
io.error.selfStop = 0;
|
||||
io.error.spindle = 0;
|
||||
io.error.sector = 0;
|
||||
|
||||
n1 blockCalc = (io.currentSector >= 0x5A) ? 1 : 0;
|
||||
n8 sectorCalc = io.currentSector - (blockCalc * 0x5A);
|
||||
n16 trackCalc = io.currentTrack.bit(0,11);
|
||||
n1 headCalc = io.currentTrack.bit(12);
|
||||
|
||||
n32 errorCalc = (headCalc * (1175*2)) + (trackCalc * 2) + blockCalc;
|
||||
|
||||
if(io.bm.readMode) {
|
||||
//read mode
|
||||
if(error.read<Byte>(errorCalc) != 0) {
|
||||
//copy protection (C1 fail all over, retail disk only)
|
||||
io.bm.c1Single = 1;
|
||||
io.bm.c1Double = 1;
|
||||
}
|
||||
|
||||
if(sectorCalc < 0x55) {
|
||||
//user sector
|
||||
auto offsetCalc = seekSector(io.currentSector);
|
||||
for(u32 n : range(io.sectorSizeBuf + 1)) {
|
||||
ds.write<Byte>(n, disk.read<Byte>(offsetCalc + n));
|
||||
}
|
||||
io.status.requestUserSector = 1;
|
||||
io.currentSector++;
|
||||
} else if (sectorCalc < 0x58) {
|
||||
//c2 sector
|
||||
io.currentSector++;
|
||||
} else if (sectorCalc == 0x58) {
|
||||
//last c2 sector
|
||||
io.status.requestC2Sector = 1;
|
||||
if (io.bm.blockTransfer) {
|
||||
//wrap
|
||||
io.bm.blockTransfer = 0;
|
||||
sectorCalc = 0;
|
||||
blockCalc = 1 - blockCalc;
|
||||
io.currentSector = sectorCalc + (blockCalc * 0x5A);
|
||||
} else {
|
||||
//stop
|
||||
io.bm.start = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//write mode
|
||||
//first interrupt: bm interrupt, request data, don't write anything to disk, wait
|
||||
//next interrupt: assume sector data to be written is on buffer, write to disk
|
||||
// and request next sector data
|
||||
//therefore take into account writing previous sector
|
||||
//no need to write C1/C2 data, drive handles it automatically
|
||||
if(sectorCalc <= 0x55) {
|
||||
if (sectorCalc > 0) {
|
||||
auto offsetCalc = seekSector(io.currentSector - 1);
|
||||
for(u32 n : range(io.sectorSizeBuf + 1)) {
|
||||
disk.write<Byte>(offsetCalc + n, ds.read<Byte>(n));
|
||||
}
|
||||
}
|
||||
io.status.requestUserSector = 1;
|
||||
}
|
||||
|
||||
//manage next sector
|
||||
if (sectorCalc >= 0x55) {
|
||||
//if next sector is on the other block, wrap around the track
|
||||
if (io.bm.blockTransfer) {
|
||||
io.bm.blockTransfer = 0;
|
||||
sectorCalc = 0;
|
||||
blockCalc = 1 - blockCalc;
|
||||
io.currentSector = sectorCalc + (blockCalc * 0x5A);
|
||||
} else {
|
||||
//last interrupt is basically acknowledge sector write, don't request data, stop afterwards
|
||||
io.status.requestUserSector = 0;
|
||||
io.bm.start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
io.currentSector++;
|
||||
}
|
||||
|
||||
raise(IRQ::BM);
|
||||
}
|
||||
|
||||
auto DD::motorActive() -> void {
|
||||
queue.remove(Queue::DD_Motor_Mode);
|
||||
io.status.headRetracted = 0;
|
||||
io.status.spindleMotorStopped = 0;
|
||||
if(!ctl.standbyDelayDisable)
|
||||
queue.insert(Queue::DD_Motor_Mode, (187'500'000 / 0x17) * ctl.standbyDelay);
|
||||
}
|
||||
|
||||
auto DD::motorStandby() -> void {
|
||||
queue.remove(Queue::DD_Motor_Mode);
|
||||
io.status.headRetracted = 1;
|
||||
io.status.spindleMotorStopped = 0;
|
||||
if(!ctl.sleepDelayDisable)
|
||||
queue.insert(Queue::DD_Motor_Mode, (187'500'000 / 0x17) * ctl.sleepDelay);
|
||||
}
|
||||
|
||||
auto DD::motorStop() -> void {
|
||||
queue.remove(Queue::DD_Motor_Mode);
|
||||
io.status.headRetracted = 1;
|
||||
io.status.spindleMotorStopped = 1;
|
||||
}
|
||||
|
||||
auto DD::motorChange() -> void {
|
||||
//to sleep mode
|
||||
if(io.status.headRetracted && !io.status.spindleMotorStopped) {
|
||||
motorStop();
|
||||
}
|
||||
|
||||
//to standby mode
|
||||
if(!io.status.headRetracted && !io.status.spindleMotorStopped) {
|
||||
motorStandby();
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ auto DD::readWord(u32 address) -> u32 {
|
|||
|
||||
//ASIC_DATA
|
||||
if(address == 0) {
|
||||
data.bit(16,31) = io.data;
|
||||
}
|
||||
|
||||
//ASIC_MISC_REG
|
||||
|
@ -12,20 +13,56 @@ auto DD::readWord(u32 address) -> u32 {
|
|||
|
||||
//ASIC_STATUS
|
||||
if(address == 2) {
|
||||
//required to indicate the 64DD is missing
|
||||
data = 0xffff'ffff;
|
||||
data.bit(16) = io.status.diskChanged;
|
||||
data.bit(17) = io.status.mechaError;
|
||||
data.bit(18) = io.status.writeProtect;
|
||||
data.bit(19) = io.status.headRetracted;
|
||||
data.bit(20) = io.status.spindleMotorStopped;
|
||||
data.bit(22) = io.status.resetState;
|
||||
data.bit(23) = io.status.busyState;
|
||||
data.bit(24) = (bool)disk; //disk present
|
||||
data.bit(25) = irq.mecha.line;
|
||||
data.bit(26) = irq.bm.line;
|
||||
data.bit(27) = io.bm.error;
|
||||
data.bit(28) = io.status.requestC2Sector;
|
||||
data.bit(30) = io.status.requestUserSector;
|
||||
|
||||
//acknowledge bm interrupt (tested on real hardware)
|
||||
if(irq.bm.line) {
|
||||
//TODO: proper research into seek and access times
|
||||
queue.insert(Queue::DD_BM_Request, 38'000 + (io.currentTrack.bit(0,11) / 15));
|
||||
lower(IRQ::BM);
|
||||
}
|
||||
}
|
||||
|
||||
//ASIC_CUR_TK
|
||||
if(address == 3) {
|
||||
data.bit(16,31) = io.currentTrack;
|
||||
}
|
||||
|
||||
//ASIC_BM_STATUS
|
||||
if(address == 4) {
|
||||
data.bit(16) = io.bm.c1Error;
|
||||
data.bit(21) = io.bm.c1Single;
|
||||
data.bit(22) = io.bm.c1Double;
|
||||
data.bit(23) = io.bm.c1Correct;
|
||||
data.bit(24) = io.bm.blockTransfer;
|
||||
data.bit(25) = io.micro.error;
|
||||
data.bit(26) = io.bm.error;
|
||||
data.bit(31) = io.bm.start;
|
||||
}
|
||||
|
||||
//ASIC_ERR_SECTOR
|
||||
if(address == 5) {
|
||||
data.bit(16,23) = io.error.sector;
|
||||
data.bit(24) = io.error.selfStop;
|
||||
data.bit(25) = io.error.clockUnlock;
|
||||
data.bit(26) = ~(bool)disk; //no disk
|
||||
data.bit(27) = io.error.offTrack;
|
||||
data.bit(28) = io.error.overrun;
|
||||
data.bit(29) = io.error.spindle;
|
||||
data.bit(30) = io.micro.error;
|
||||
data.bit(31) = io.error.am;
|
||||
}
|
||||
|
||||
//ASIC_SEQ_STATUS
|
||||
|
@ -34,6 +71,8 @@ auto DD::readWord(u32 address) -> u32 {
|
|||
|
||||
//ASIC_CUR_SECTOR
|
||||
if(address == 7) {
|
||||
data.bit(24,31) = io.currentSector;
|
||||
data.bit(16,23) = 0xc3;
|
||||
}
|
||||
|
||||
//ASIC_HARD_RESET
|
||||
|
@ -46,6 +85,7 @@ auto DD::readWord(u32 address) -> u32 {
|
|||
|
||||
//ASIC_HOST_SECBYTE
|
||||
if(address == 10) {
|
||||
data.bit(16,23) = io.sectorSizeBuf;
|
||||
}
|
||||
|
||||
//ASIC_C1_S2
|
||||
|
@ -54,6 +94,8 @@ auto DD::readWord(u32 address) -> u32 {
|
|||
|
||||
//ASIC_SEC_BYTE
|
||||
if(address == 12) {
|
||||
data.bit(16,23) = io.sectorSize;
|
||||
data.bit(24,31) = io.sectorBlock;
|
||||
}
|
||||
|
||||
//ASIC_C1_S4
|
||||
|
@ -70,6 +112,7 @@ auto DD::readWord(u32 address) -> u32 {
|
|||
|
||||
//ASIC_ID_REG
|
||||
if(address == 16) {
|
||||
data.bit(16,31) = io.id;
|
||||
}
|
||||
|
||||
//ASIC_TEST_REG
|
||||
|
@ -90,6 +133,7 @@ auto DD::writeWord(u32 address, u32 data_) -> void {
|
|||
|
||||
//ASIC_DATA
|
||||
if(address == 0) {
|
||||
io.data = data.bit(16,31);
|
||||
}
|
||||
|
||||
//ASIC_MISC_REG
|
||||
|
@ -98,6 +142,7 @@ auto DD::writeWord(u32 address, u32 data_) -> void {
|
|||
|
||||
//ASIC_CMD
|
||||
if(address == 2) {
|
||||
command(data.bit(16,31));
|
||||
}
|
||||
|
||||
//ASIC_CUR_TK
|
||||
|
@ -106,6 +151,33 @@ auto DD::writeWord(u32 address, u32 data_) -> void {
|
|||
|
||||
//ASIC_BM_CTL
|
||||
if(address == 4) {
|
||||
io.bm.reset |= data.bit(28);
|
||||
io.bm.readMode = data.bit(30);
|
||||
//irq.bm.mask = ~data.bit(29);
|
||||
io.bm.disableORcheck = data.bit(27);
|
||||
io.bm.disableC1Correction = data.bit(26);
|
||||
io.bm.blockTransfer = data.bit(25);
|
||||
if (data.bit(24)) {
|
||||
//mecha int reset
|
||||
lower(IRQ::MECHA);
|
||||
}
|
||||
io.currentSector = data.bit(16,23);
|
||||
if (!data.bit(28) && io.bm.reset) {
|
||||
//BM reset
|
||||
io.bm.start = 0;
|
||||
io.bm.error = 0;
|
||||
io.status.requestUserSector = 0;
|
||||
io.status.requestC2Sector = 0;
|
||||
io.bm.reset = 0;
|
||||
lower(IRQ::BM);
|
||||
}
|
||||
|
||||
if(data.bit(31) && disk) {
|
||||
//start BM
|
||||
io.bm.start |= data.bit(31);
|
||||
//TODO: proper research into seek and access times
|
||||
queue.insert(Queue::DD_BM_Request, 50'000 + (io.currentTrack.bit(0,11) / 15));
|
||||
}
|
||||
}
|
||||
|
||||
//ASIC_ERR_SECTOR
|
||||
|
@ -114,6 +186,7 @@ auto DD::writeWord(u32 address, u32 data_) -> void {
|
|||
|
||||
//ASIC_SEQ_CTL
|
||||
if(address == 6) {
|
||||
io.micro.enable = data.bit(30);
|
||||
}
|
||||
|
||||
//ASIC_CUR_SECTOR
|
||||
|
@ -122,6 +195,9 @@ auto DD::writeWord(u32 address, u32 data_) -> void {
|
|||
|
||||
//ASIC_HARD_RESET
|
||||
if(address == 8) {
|
||||
if((data >> 16) == 0xAAAA) {
|
||||
power(true);
|
||||
}
|
||||
}
|
||||
|
||||
//ASIC_C1_S0
|
||||
|
@ -130,10 +206,13 @@ auto DD::writeWord(u32 address, u32 data_) -> void {
|
|||
|
||||
//ASIC_HOST_SECBYTE
|
||||
if(address == 10) {
|
||||
io.sectorSizeBuf = data.bit(16,23);
|
||||
}
|
||||
|
||||
//ASIC_C1_S2
|
||||
if(address == 11) {
|
||||
io.sectorSize = data.bit(16,23);
|
||||
io.sectorBlock = data.bit(24,31);
|
||||
}
|
||||
|
||||
//ASIC_SEC_BYTE
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
auto DD::rtcLoad() -> void {
|
||||
/*if(auto fp = system.pak->read("time.rtc")) {
|
||||
rtc.load(fp);
|
||||
}*/
|
||||
|
||||
n64 check = 0;
|
||||
for(auto n : range(8)) check.byte(n) = rtc.read<Byte>(n);
|
||||
if(!~check) return; //new save file
|
||||
|
||||
n64 timestamp = 0;
|
||||
for(auto n : range(8)) timestamp.byte(n) = rtc.read<Byte>(8 + n);
|
||||
if(!~timestamp) return; //new save file
|
||||
|
||||
timestamp = rtcCallback() - timestamp;
|
||||
while(timestamp--) rtcTickSecond();
|
||||
}
|
||||
|
||||
auto DD::rtcSave() -> void {
|
||||
n64 timestamp = rtcCallback();
|
||||
for(auto n : range(8)) rtc.write<Byte>(8 + n, timestamp.byte(n));
|
||||
|
||||
/*if(auto fp = system.pak->write("time.rtc")) {
|
||||
rtc.save(fp);
|
||||
}*/
|
||||
}
|
||||
|
||||
auto DD::rtcTick(u32 offset) -> void {
|
||||
u8 n = rtc.read<Byte>(offset);
|
||||
if((++n & 0xf) > 9) n = (n & 0xf0) + 0x10;
|
||||
if((n & 0xf0) > 0x90) n = 0;
|
||||
rtc.write<Byte>(offset, n);
|
||||
}
|
||||
|
||||
auto DD::rtcTickClock() -> void {
|
||||
rtcTickSecond();
|
||||
queue.remove(Queue::DD_Clock_Tick);
|
||||
queue.insert(Queue::DD_Clock_Tick, 187'500'000);
|
||||
}
|
||||
|
||||
auto DD::rtcTickSecond() -> void {
|
||||
//second
|
||||
rtcTick(5);
|
||||
if(rtc.read<Byte>(5) < 0x60) return;
|
||||
rtc.write<Byte>(5, 0);
|
||||
|
||||
//minute
|
||||
rtcTick(4);
|
||||
if(rtc.read<Byte>(4) < 0x60) return;
|
||||
rtc.write<Byte>(4, 0);
|
||||
|
||||
//hour
|
||||
rtcTick(3);
|
||||
if(rtc.read<Byte>(3) < 0x24) return;
|
||||
rtc.write<Byte>(3, 0);
|
||||
|
||||
//day
|
||||
u32 daysInMonth[12] = {0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31};
|
||||
if(rtc.read<Byte>(0) && !(BCD::decode(rtc.read<Byte>(0)) % 4)) daysInMonth[1]++;
|
||||
|
||||
rtcTick(2);
|
||||
if(rtc.read<Byte>(2) <= daysInMonth[BCD::decode(rtc.read<Byte>(1))-1]) return;
|
||||
rtc.write<Byte>(2, 1);
|
||||
|
||||
//month
|
||||
rtcTick(1);
|
||||
if(rtc.read<Byte>(1) < 0x12) return;
|
||||
rtc.write<Byte>(1, 1);
|
||||
|
||||
//year
|
||||
rtcTick(0);
|
||||
}
|
|
@ -1,2 +1,66 @@
|
|||
auto DD::serialize(serializer& s) -> void {
|
||||
s(irq.bm.line);
|
||||
s(irq.bm.mask);
|
||||
s(irq.mecha.line);
|
||||
s(irq.mecha.mask);
|
||||
|
||||
s(ctl.diskType);
|
||||
s(ctl.error.selfDiagnostic);
|
||||
s(ctl.error.servoNG);
|
||||
s(ctl.error.indexGapNG);
|
||||
s(ctl.error.timeout);
|
||||
s(ctl.error.undefinedCommand);
|
||||
s(ctl.error.invalidParam);
|
||||
s(ctl.error.unknown);
|
||||
s(ctl.standbyDelayDisable);
|
||||
s(ctl.standbyDelay);
|
||||
s(ctl.sleepDelayDisable);
|
||||
s(ctl.sleepDelay);
|
||||
s(ctl.ledOnTime);
|
||||
s(ctl.ledOffTime);
|
||||
|
||||
s(io.data);
|
||||
|
||||
s(io.status.requestUserSector);
|
||||
s(io.status.requestC2Sector);
|
||||
s(io.status.busyState);
|
||||
s(io.status.resetState);
|
||||
s(io.status.spindleMotorStopped);
|
||||
s(io.status.headRetracted);
|
||||
s(io.status.writeProtect);
|
||||
s(io.status.mechaError);
|
||||
s(io.status.diskChanged);
|
||||
|
||||
s(io.currentTrack);
|
||||
s(io.currentSector);
|
||||
|
||||
s(io.sectorSizeBuf);
|
||||
s(io.sectorSize);
|
||||
s(io.sectorBlock);
|
||||
s(io.id);
|
||||
|
||||
s(io.bm.start);
|
||||
s(io.bm.reset);
|
||||
s(io.bm.error);
|
||||
s(io.bm.blockTransfer);
|
||||
s(io.bm.c1Correct);
|
||||
s(io.bm.c1Double);
|
||||
s(io.bm.c1Single);
|
||||
s(io.bm.c1Error);
|
||||
s(io.bm.readMode);
|
||||
s(io.bm.disableORcheck);
|
||||
s(io.bm.disableC1Correction);
|
||||
|
||||
s(io.error.am);
|
||||
s(io.error.spindle);
|
||||
s(io.error.overrun);
|
||||
s(io.error.offTrack);
|
||||
s(io.error.clockUnlock);
|
||||
s(io.error.selfStop);
|
||||
s(io.error.sector);
|
||||
|
||||
s(io.micro.enable);
|
||||
s(io.micro.error);
|
||||
|
||||
s(state.seek);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
#define XXH_INLINE_ALL
|
||||
#include <xxhash.h>
|
||||
#include <float.h>
|
||||
#include <ares/ares.hpp>
|
||||
#include <nall/float-env.hpp>
|
||||
#include <nall/hashset.hpp>
|
||||
#include <nall/recompiler/generic/generic.hpp>
|
||||
#include <component/processor/sm5k/sm5k.hpp>
|
||||
|
@ -33,6 +35,8 @@ namespace ares::Nintendo64 {
|
|||
static inline auto PAL() -> bool;
|
||||
};
|
||||
|
||||
inline static auto _DD() -> bool;
|
||||
|
||||
struct Thread {
|
||||
auto reset() -> void {
|
||||
clock = 0;
|
||||
|
@ -53,6 +57,10 @@ namespace ares::Nintendo64 {
|
|||
PI_BUS_Write,
|
||||
SI_DMA_Read,
|
||||
SI_DMA_Write,
|
||||
DD_Clock_Tick,
|
||||
DD_MECHA_Response,
|
||||
DD_BM_Request,
|
||||
DD_Motor_Mode,
|
||||
};
|
||||
};
|
||||
extern Queue queue;
|
||||
|
|
|
@ -11,22 +11,40 @@ inline auto PI::readWord(u32 address) -> u32 {
|
|||
template <u32 Size>
|
||||
inline auto PI::busRead(u32 address) -> u32 {
|
||||
static_assert(Size == Half || Size == Word); //PI bus will do 32-bit (CPU) or 16-bit (DMA) only
|
||||
static constexpr u32 unmapped = 0;
|
||||
const u32 unmapped = (address & 0xFFFF) | (address << 16);
|
||||
|
||||
if(address <= 0x04ff'ffff) return unmapped; //Address range not memory mapped, only accessible via DMA
|
||||
if(address <= 0x0500'03ff) return dd.c2s.read<Size>(address);
|
||||
if(address <= 0x0500'04ff) return dd.ds.read<Size>(address);
|
||||
if(address <= 0x0500'057f) return dd.read<Size>(address);
|
||||
if(address <= 0x0500'05bf) return dd.ms.read<Size>(address);
|
||||
if(address <= 0x0500'03ff) {
|
||||
if(_DD()) return dd.c2s.read<Size>(address);
|
||||
return unmapped;
|
||||
}
|
||||
if(address <= 0x0500'04ff) {
|
||||
if(_DD()) return dd.ds.read<Size>(address);
|
||||
return unmapped;
|
||||
}
|
||||
if(address <= 0x0500'057f) {
|
||||
if(_DD()) return dd.read<Size>(address);
|
||||
return unmapped;
|
||||
}
|
||||
if(address <= 0x0500'05bf) {
|
||||
if(_DD()) return dd.ms.read<Size>(address);
|
||||
return unmapped;
|
||||
}
|
||||
if(address <= 0x05ff'ffff) return unmapped;
|
||||
if(address <= 0x063f'ffff) return dd.iplrom.read<Size>(address);
|
||||
if(address <= 0x063f'ffff) {
|
||||
if(_DD()) return dd.iplrom.read<Size>(address);
|
||||
return unmapped;
|
||||
}
|
||||
if(address <= 0x07ff'ffff) return unmapped;
|
||||
if(address <= 0x0fff'ffff) {
|
||||
if(cartridge.ram ) return cartridge.ram.read<Size>(address);
|
||||
if(cartridge.flash) return cartridge.flash.read<Size>(address);
|
||||
return unmapped;
|
||||
}
|
||||
if(address <= 0x13fe'ffff) return cartridge.rom.read<Size>(address);
|
||||
if(address <= 0x13fe'ffff) {
|
||||
if(cartridge.rom ) return cartridge.rom.read<Size>(address);
|
||||
return unmapped;
|
||||
}
|
||||
if(address <= 0x13ff'ffff) return cartridge.isviewer.read<Size>(address);
|
||||
if(address <= 0x7fff'ffff) return unmapped;
|
||||
return unmapped; //accesses here actually lock out the RCP
|
||||
|
@ -46,19 +64,37 @@ template <u32 Size>
|
|||
inline auto PI::busWrite(u32 address, u32 data) -> void {
|
||||
static_assert(Size == Half || Size == Word); //PI bus will do 32-bit (CPU) or 16-bit (DMA) only
|
||||
if(address <= 0x04ff'ffff) return; //Address range not memory mapped, only accessible via DMA
|
||||
if(address <= 0x0500'03ff) return dd.c2s.write<Size>(address, data);
|
||||
if(address <= 0x0500'04ff) return dd.ds.write<Size>(address, data);
|
||||
if(address <= 0x0500'057f) return dd.write<Size>(address, data);
|
||||
if(address <= 0x0500'05bf) return dd.ms.write<Size>(address, data);
|
||||
if(address <= 0x0500'03ff) {
|
||||
if(_DD()) return dd.c2s.write<Size>(address, data);
|
||||
return;
|
||||
}
|
||||
if(address <= 0x0500'04ff) {
|
||||
if(_DD()) return dd.ds.write<Size>(address, data);
|
||||
return;
|
||||
}
|
||||
if(address <= 0x0500'057f) {
|
||||
if(_DD()) return dd.write<Size>(address, data);
|
||||
return;
|
||||
}
|
||||
if(address <= 0x0500'05bf) {
|
||||
if(_DD()) return dd.ms.write<Size>(address, data);
|
||||
return;
|
||||
}
|
||||
if(address <= 0x05ff'ffff) return;
|
||||
if(address <= 0x063f'ffff) return dd.iplrom.write<Size>(address, data);
|
||||
if(address <= 0x063f'ffff) {
|
||||
if(_DD()) return dd.iplrom.write<Size>(address, data);
|
||||
return;
|
||||
}
|
||||
if(address <= 0x07ff'ffff) return;
|
||||
if(address <= 0x0fff'ffff) {
|
||||
if(cartridge.ram ) return cartridge.ram.write<Size>(address, data);
|
||||
if(cartridge.flash) return cartridge.flash.write<Size>(address, data);
|
||||
return;
|
||||
}
|
||||
if(address <= 0x13fe'ffff) return cartridge.rom.write<Size>(address, data);
|
||||
if(address <= 0x13fe'ffff) {
|
||||
if(cartridge.rom ) return cartridge.rom.write<Size>(address, data);
|
||||
return;
|
||||
}
|
||||
if(address <= 0x13ff'ffff) {
|
||||
writeForceFinish(); //Debugging channel for homebrew, be gentle
|
||||
return cartridge.isviewer.write<Size>(address, data);
|
||||
|
|
|
@ -210,8 +210,11 @@ auto PIF::scan() -> void {
|
|||
if(over) {
|
||||
ram.write<Byte>(recvOffset, 0x40 | recv & 0x3f);
|
||||
}
|
||||
for(u32 index : range(recv)) {
|
||||
ram.write<Byte>(offset++, output[index]);
|
||||
|
||||
if (valid) {
|
||||
for(u32 index : range(recv)) {
|
||||
ram.write<Byte>(offset++, output[index]);
|
||||
}
|
||||
}
|
||||
channel++;
|
||||
}
|
||||
|
@ -275,7 +278,7 @@ auto PIF::challenge() -> void {
|
|||
}
|
||||
|
||||
auto PIF::power(bool reset) -> void {
|
||||
string pifrom = cartridge.region() == "NTSC" ? "pif.ntsc.rom" : "pif.pal.rom";
|
||||
string pifrom = Region::PAL() ? "pif.pal.rom" : "pif.ntsc.rom";
|
||||
if(auto fp = system.pak->read(pifrom)) {
|
||||
rom.load(fp);
|
||||
}
|
||||
|
@ -284,21 +287,24 @@ auto PIF::power(bool reset) -> void {
|
|||
io = {};
|
||||
|
||||
//write CIC seeds into PIF RAM so that cartridge checksum function passes
|
||||
string cic = cartridge.cic();
|
||||
string cic = cartridge.node ? cartridge.cic() : dd.cic();
|
||||
n8 seed = 0x3f;
|
||||
n1 version = 0;
|
||||
n1 type = 0;
|
||||
if(cic == "CIC-NUS-6101" || cic == "CIC-NUS-7102") seed = 0x3f, version = 1;
|
||||
if(cic == "CIC-NUS-6102" || cic == "CIC-NUS-7101") seed = 0x3f;
|
||||
if(cic == "CIC-NUS-6103" || cic == "CIC-NUS-7103") seed = 0x78;
|
||||
if(cic == "CIC-NUS-6105" || cic == "CIC-NUS-7105") seed = 0x91;
|
||||
if(cic == "CIC-NUS-6106" || cic == "CIC-NUS-7106") seed = 0x85;
|
||||
if(cic == "CIC-NUS-8303" || cic == "CIC-NUS-8401") seed = 0xdd, type = 1;
|
||||
if(cic == "CIC-NUS-DDUS") seed = 0xde, type = 1;
|
||||
|
||||
n32 data;
|
||||
data.bit(0, 7) = 0x3f; //CIC IPL2 seed
|
||||
data.bit(8,15) = seed; //CIC IPL3 seed
|
||||
data.bit(17) = reset; //osResetType (0 = power; 1 = reset (NMI))
|
||||
data.bit(18) = version; //osVersion
|
||||
data.bit(19) = 0; //osRomType (0 = Gamepak; 1 = 64DD)
|
||||
data.bit(19) = type; //osRomType (0 = Gamepak; 1 = 64DD)
|
||||
ram.write<Word>(0x24, data);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ auto RDP::readWord(u32 address) -> u32 {
|
|||
if(address == 3) {
|
||||
//DPC_STATUS
|
||||
data.bit( 0) = command.source;
|
||||
data.bit( 1) = command.freeze;
|
||||
data.bit( 1) = command.freeze || command.crashed;
|
||||
data.bit( 2) = command.flush;
|
||||
data.bit( 3) = command.startGclk;
|
||||
data.bit( 4) = command.tmemBusy > 0;
|
||||
|
@ -88,9 +88,9 @@ auto RDP::writeWord(u32 address, u32 data_) -> void {
|
|||
if(data.bit(3)) command.freeze = 1;
|
||||
if(data.bit(4)) command.flush = 0;
|
||||
if(data.bit(5)) command.flush = 1;
|
||||
if(data.bit(6)) command.tmemBusy = 0;
|
||||
if(data.bit(7)) command.pipeBusy = 0;
|
||||
if(data.bit(8)) command.bufferBusy = 0;
|
||||
if(data.bit(6) && !command.crashed) command.tmemBusy = 0;
|
||||
if(data.bit(7) && !command.crashed) command.pipeBusy = 0;
|
||||
if(data.bit(8) && !command.crashed) command.bufferBusy = 0;
|
||||
if(data.bit(9)) command.clock = 0;
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,8 @@ auto RDP::IO::writeWord(u32 address, u32 data_) -> void {
|
|||
}
|
||||
|
||||
auto RDP::flushCommands() -> void {
|
||||
if(command.freeze) return;
|
||||
if(command.freeze || command.crashed) return;
|
||||
command.bufferBusy = 1;
|
||||
command.pipeBusy = 1;
|
||||
command.startGclk = 1;
|
||||
if(command.end > command.current) render();
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include <n64/n64.hpp>
|
||||
|
||||
#if defined(ANGRYLION_RDP)
|
||||
#include "Gfx #1.3.h"
|
||||
#endif
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
|
@ -16,19 +14,22 @@ auto RDP::load(Node::Object parent) -> void {
|
|||
node = parent->append<Node::Object>("RDP");
|
||||
debugger.load(node);
|
||||
|
||||
#if defined(ANGRYLION_RDP)
|
||||
puts("starting RDP video");
|
||||
angrylion::RomOpen();
|
||||
#endif
|
||||
}
|
||||
|
||||
auto RDP::unload() -> void {
|
||||
debugger = {};
|
||||
node.reset();
|
||||
|
||||
#if defined(ANGRYLION_RDP)
|
||||
angrylion::RomClosed();
|
||||
#endif
|
||||
}
|
||||
|
||||
auto RDP::crash(const char *reason) -> void {
|
||||
debug(unusual, "[RDP] software triggered a hardware bug; RDP crashed and will stop responding. Reason: ", reason);
|
||||
command.crashed = 1;
|
||||
//guard against asynchronous reporting of crash state. We want the RDP to report that it's busy forever
|
||||
command.pipeBusy = 1;
|
||||
command.bufferBusy = 1;
|
||||
}
|
||||
|
||||
auto RDP::main() -> void {
|
||||
|
|
|
@ -23,6 +23,7 @@ struct RDP : Thread, Memory::IO<RDP> {
|
|||
auto main() -> void;
|
||||
auto step(u32 clocks) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
auto crash(const char *reason) -> void;
|
||||
|
||||
//render.cpp
|
||||
auto render() -> void;
|
||||
|
@ -82,6 +83,7 @@ struct RDP : Thread, Memory::IO<RDP> {
|
|||
n24 tmemBusy;
|
||||
n1 source; //0 = RDRAM, 1 = DMEM
|
||||
n1 freeze;
|
||||
n1 crashed;
|
||||
n1 flush;
|
||||
n1 startValid;
|
||||
n1 endValid;
|
||||
|
|
|
@ -45,511 +45,8 @@ static const vector<string> commandNames = {
|
|||
};
|
||||
|
||||
auto RDP::render() -> void {
|
||||
#if defined(VULKAN)
|
||||
if(vulkan.enable && vulkan.render()) return;
|
||||
#endif
|
||||
|
||||
#if defined(ANGRYLION_RDP)
|
||||
angrylion::ProcessRDPList();
|
||||
command.current = command.end;
|
||||
return;
|
||||
#endif
|
||||
|
||||
auto& memory = !command.source ? rdram.ram : rsp.dmem;
|
||||
|
||||
auto fetch = [&]() -> u64 {
|
||||
u64 op = memory.readUnaligned<Dual>(command.current);
|
||||
command.current += 8;
|
||||
return op;
|
||||
};
|
||||
|
||||
auto fetchEdge = [&](u64 op) {
|
||||
edge.lmajor = n1 (op >> 55);
|
||||
edge.level = n3 (op >> 51);
|
||||
edge.tile = n3 (op >> 48);
|
||||
edge.y.lo = n14(op >> 32);
|
||||
edge.y.md = n14(op >> 16);
|
||||
edge.y.hi = n14(op >> 0);
|
||||
op = fetch();
|
||||
edge.x.lo.c.i = n16(op >> 48);
|
||||
edge.x.lo.c.f = n16(op >> 32);
|
||||
edge.x.lo.s.i = n16(op >> 16);
|
||||
edge.x.lo.s.f = n16(op >> 0);
|
||||
op = fetch();
|
||||
edge.x.hi.c.i = n16(op >> 48);
|
||||
edge.x.hi.c.f = n16(op >> 32);
|
||||
edge.x.hi.s.i = n16(op >> 16);
|
||||
edge.x.hi.s.f = n16(op >> 0);
|
||||
op = fetch();
|
||||
edge.x.md.c.i = n16(op >> 48);
|
||||
edge.x.md.c.f = n16(op >> 32);
|
||||
edge.x.md.s.i = n16(op >> 16);
|
||||
edge.x.md.s.f = n16(op >> 0);
|
||||
};
|
||||
|
||||
auto fetchShade = [&](u64 op) {
|
||||
op = fetch();
|
||||
shade.r.c.i = n16(op >> 48);
|
||||
shade.g.c.i = n16(op >> 32);
|
||||
shade.b.c.i = n16(op >> 16);
|
||||
shade.a.c.i = n16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.x.i = n16(op >> 48);
|
||||
shade.g.x.i = n16(op >> 32);
|
||||
shade.b.x.i = n16(op >> 16);
|
||||
shade.a.x.i = n16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.c.f = n16(op >> 48);
|
||||
shade.g.c.f = n16(op >> 32);
|
||||
shade.b.c.f = n16(op >> 16);
|
||||
shade.a.c.f = n16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.x.f = n16(op >> 48);
|
||||
shade.g.x.f = n16(op >> 32);
|
||||
shade.b.x.f = n16(op >> 16);
|
||||
shade.a.x.f = n16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.e.i = n16(op >> 48);
|
||||
shade.g.e.i = n16(op >> 32);
|
||||
shade.b.e.i = n16(op >> 16);
|
||||
shade.a.e.i = n16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.y.i = n16(op >> 48);
|
||||
shade.g.y.i = n16(op >> 32);
|
||||
shade.b.y.i = n16(op >> 16);
|
||||
shade.a.y.i = n16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.e.f = n16(op >> 48);
|
||||
shade.g.e.f = n16(op >> 32);
|
||||
shade.b.e.f = n16(op >> 16);
|
||||
shade.a.e.f = n16(op >> 0);
|
||||
op = fetch();
|
||||
shade.r.y.f = n16(op >> 48);
|
||||
shade.g.y.f = n16(op >> 32);
|
||||
shade.b.y.f = n16(op >> 16);
|
||||
shade.a.y.f = n16(op >> 0);
|
||||
};
|
||||
|
||||
auto fetchTexture = [&](u64 op) {
|
||||
op = fetch();
|
||||
texture.s.c.i = n16(op >> 48);
|
||||
texture.t.c.i = n16(op >> 32);
|
||||
texture.w.c.i = n16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.x.i = n16(op >> 48);
|
||||
texture.t.x.i = n16(op >> 32);
|
||||
texture.w.x.i = n16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.c.f = n16(op >> 48);
|
||||
texture.t.c.f = n16(op >> 32);
|
||||
texture.w.c.f = n16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.x.f = n16(op >> 48);
|
||||
texture.t.x.f = n16(op >> 32);
|
||||
texture.w.x.f = n16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.e.i = n16(op >> 48);
|
||||
texture.t.e.i = n16(op >> 32);
|
||||
texture.w.e.i = n16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.y.i = n16(op >> 48);
|
||||
texture.t.y.i = n16(op >> 32);
|
||||
texture.w.y.i = n16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.e.f = n16(op >> 48);
|
||||
texture.t.e.f = n16(op >> 32);
|
||||
texture.w.e.f = n16(op >> 16);
|
||||
op = fetch();
|
||||
texture.s.y.f = n16(op >> 48);
|
||||
texture.t.y.f = n16(op >> 32);
|
||||
texture.w.y.f = n16(op >> 16);
|
||||
};
|
||||
|
||||
auto fetchZBuffer = [&](u64 op) {
|
||||
op = fetch();
|
||||
zbuffer.d.i = n16(op >> 48);
|
||||
zbuffer.d.f = n16(op >> 32);
|
||||
zbuffer.x.i = n16(op >> 16);
|
||||
zbuffer.x.f = n16(op >> 0);
|
||||
op = fetch();
|
||||
zbuffer.e.i = n16(op >> 48);
|
||||
zbuffer.e.f = n16(op >> 32);
|
||||
zbuffer.y.i = n16(op >> 16);
|
||||
zbuffer.y.f = n16(op >> 0);
|
||||
};
|
||||
|
||||
auto fetchRectangle = [&](u64 op) {
|
||||
rectangle.x.lo = n12(op >> 44);
|
||||
rectangle.y.lo = n12(op >> 32);
|
||||
rectangle.tile = n3 (op >> 24);
|
||||
rectangle.x.hi = n12(op >> 12);
|
||||
rectangle.y.hi = n12(op >> 0);
|
||||
op = fetch();
|
||||
rectangle.s.i = n16(op >> 48);
|
||||
rectangle.t.i = n16(op >> 32);
|
||||
rectangle.s.f = n16(op >> 16);
|
||||
rectangle.t.f = n16(op >> 0);
|
||||
};
|
||||
|
||||
while(command.current < command.end) {
|
||||
u64 op = fetch();
|
||||
|
||||
if(debugger.tracer.command->enabled()) {
|
||||
debugger.command({hex(op, 16L), " ", commandNames(op >> 56 & 0x3f, "Invalid")});
|
||||
}
|
||||
|
||||
switch(op >> 56 & 0x3f) {
|
||||
|
||||
case 0x00: {
|
||||
noOperation();
|
||||
} break;
|
||||
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07: {
|
||||
invalidOperation();
|
||||
} break;
|
||||
|
||||
case 0x08: {
|
||||
fetchEdge(op);
|
||||
unshadedTriangle();
|
||||
} break;
|
||||
|
||||
case 0x09: {
|
||||
fetchEdge(op);
|
||||
fetchZBuffer(op);
|
||||
unshadedZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0a: {
|
||||
fetchEdge(op);
|
||||
fetchTexture(op);
|
||||
textureTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0b: {
|
||||
fetchEdge(op);
|
||||
fetchTexture(op);
|
||||
fetchZBuffer(op);
|
||||
textureZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0c: {
|
||||
fetchEdge(op);
|
||||
fetchShade(op);
|
||||
shadedTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0d: {
|
||||
fetchEdge(op);
|
||||
fetchShade(op);
|
||||
fetchZBuffer(op);
|
||||
shadedZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0e: {
|
||||
fetchEdge(op);
|
||||
fetchShade(op);
|
||||
fetchTexture(op);
|
||||
shadedTextureTriangle();
|
||||
} break;
|
||||
|
||||
case 0x0f: {
|
||||
fetchEdge(op);
|
||||
fetchShade(op);
|
||||
fetchTexture(op);
|
||||
fetchZBuffer(op);
|
||||
shadedTextureZbufferTriangle();
|
||||
} break;
|
||||
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
case 0x14:
|
||||
case 0x15:
|
||||
case 0x16:
|
||||
case 0x17:
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1a:
|
||||
case 0x1b:
|
||||
case 0x1c:
|
||||
case 0x1d:
|
||||
case 0x1e:
|
||||
case 0x1f:
|
||||
case 0x20:
|
||||
case 0x21:
|
||||
case 0x22:
|
||||
case 0x23: {
|
||||
invalidOperation();
|
||||
} break;
|
||||
|
||||
case 0x24: {
|
||||
fetchRectangle(op);
|
||||
textureRectangle();
|
||||
} break;
|
||||
|
||||
case 0x25: {
|
||||
fetchRectangle(op);
|
||||
textureRectangleFlip();
|
||||
} break;
|
||||
|
||||
case 0x26: {
|
||||
syncLoad();
|
||||
} break;
|
||||
|
||||
case 0x27: {
|
||||
syncPipe();
|
||||
} break;
|
||||
|
||||
case 0x28: {
|
||||
syncTile();
|
||||
} break;
|
||||
|
||||
case 0x29: {
|
||||
syncFull();
|
||||
} break;
|
||||
|
||||
case 0x2a: {
|
||||
key.g.width = n12(op >> 44);
|
||||
key.b.width = n12(op >> 32);
|
||||
key.g.center = n8 (op >> 24);
|
||||
key.g.scale = n8 (op >> 16);
|
||||
key.b.center = n8 (op >> 8);
|
||||
key.b.scale = n8 (op >> 0);
|
||||
setKeyGB();
|
||||
} break;
|
||||
|
||||
case 0x2b: {
|
||||
key.r.width = n12(op >> 16);
|
||||
key.r.center = n8 (op >> 8);
|
||||
key.r.scale = n8 (op >> 0);
|
||||
setKeyR();
|
||||
} break;
|
||||
|
||||
case 0x2c: {
|
||||
convert.k[0] = n9(op >> 45);
|
||||
convert.k[1] = n9(op >> 36);
|
||||
convert.k[2] = n9(op >> 27);
|
||||
convert.k[3] = n9(op >> 18);
|
||||
convert.k[4] = n9(op >> 9);
|
||||
convert.k[5] = n8(op >> 0);
|
||||
setConvert();
|
||||
} break;
|
||||
|
||||
case 0x2d: {
|
||||
scissor.x.hi = n12(op >> 44);
|
||||
scissor.y.hi = n12(op >> 32);
|
||||
scissor.field = n1 (op >> 25);
|
||||
scissor.odd = n1 (op >> 24);
|
||||
scissor.x.lo = n12(op >> 12);
|
||||
scissor.y.lo = n12(op >> 0);
|
||||
setScissor();
|
||||
} break;
|
||||
|
||||
case 0x2e: {
|
||||
primitiveDepth.z = n16(op >> 16);
|
||||
primitiveDepth.deltaZ = n16(op >> 0);
|
||||
setPrimitiveDepth();
|
||||
} break;
|
||||
|
||||
case 0x2f: {
|
||||
other.atomicPrimitive = n1(op >> 55);
|
||||
other.reserved1 = n1(op >> 54);
|
||||
other.cycleType = n2(op >> 52);
|
||||
other.perspective = n1(op >> 51);
|
||||
other.detailTexture = n1(op >> 50);
|
||||
other.sharpenTexture = n1(op >> 49);
|
||||
other.lodTexture = n1(op >> 48);
|
||||
other.tlut = n1(op >> 47);
|
||||
other.tlutType = n1(op >> 46);
|
||||
other.sampleType = n1(op >> 45);
|
||||
other.midTexel = n1(op >> 44);
|
||||
other.bilerp[0] = n1(op >> 43);
|
||||
other.bilerp[1] = n1(op >> 42);
|
||||
other.convertOne = n1(op >> 41);
|
||||
other.colorKey = n1(op >> 40);
|
||||
other.colorDitherMode = n2(op >> 38);
|
||||
other.alphaDitherMode = n2(op >> 36);
|
||||
other.reserved2 = n4(op >> 32);
|
||||
other.blend1a[0] = n2(op >> 30);
|
||||
other.blend1a[1] = n2(op >> 28);
|
||||
other.blend1b[0] = n2(op >> 26);
|
||||
other.blend1b[1] = n2(op >> 24);
|
||||
other.blend2a[0] = n2(op >> 22);
|
||||
other.blend2a[1] = n2(op >> 20);
|
||||
other.blend2b[0] = n2(op >> 18);
|
||||
other.blend2b[1] = n2(op >> 16);
|
||||
other.reserved3 = n1(op >> 15);
|
||||
other.forceBlend = n1(op >> 14);
|
||||
other.alphaCoverage = n1(op >> 13);
|
||||
other.coverageXalpha = n1(op >> 12);
|
||||
other.zMode = n2(op >> 10);
|
||||
other.coverageMode = n2(op >> 8);
|
||||
other.colorOnCoverage = n1(op >> 7);
|
||||
other.imageRead = n1(op >> 6);
|
||||
other.zUpdate = n1(op >> 5);
|
||||
other.zCompare = n1(op >> 4);
|
||||
other.antialias = n1(op >> 3);
|
||||
other.zSource = n1(op >> 2);
|
||||
other.ditherAlpha = n1(op >> 1);
|
||||
other.alphaCompare = n1(op >> 0);
|
||||
setOtherModes();
|
||||
} break;
|
||||
|
||||
case 0x30: {
|
||||
tlut.s.lo = n12(op >> 44);
|
||||
tlut.t.lo = n12(op >> 32);
|
||||
tlut.index = n3 (op >> 24);
|
||||
tlut.s.hi = n12(op >> 12);
|
||||
tlut.t.hi = n12(op >> 0);
|
||||
loadTLUT();
|
||||
} break;
|
||||
|
||||
case 0x31: {
|
||||
invalidOperation();
|
||||
} break;
|
||||
|
||||
case 0x32: {
|
||||
tileSize.s.lo = n12(op >> 44);
|
||||
tileSize.t.lo = n12(op >> 32);
|
||||
tileSize.index = n3 (op >> 24);
|
||||
tileSize.s.hi = n12(op >> 12);
|
||||
tileSize.t.hi = n12(op >> 0);
|
||||
setTileSize();
|
||||
} break;
|
||||
|
||||
case 0x33: {
|
||||
load_.block.s.lo = n12(op >> 44);
|
||||
load_.block.t.lo = n12(op >> 32);
|
||||
load_.block.index = n3 (op >> 24);
|
||||
load_.block.s.hi = n12(op >> 12);
|
||||
load_.block.t.hi = n12(op >> 0);
|
||||
loadBlock();
|
||||
} break;
|
||||
|
||||
case 0x34: {
|
||||
load_.tile.s.lo = n12(op >> 44);
|
||||
load_.tile.t.lo = n12(op >> 32);
|
||||
load_.tile.index = n3 (op >> 24);
|
||||
load_.tile.s.hi = n12(op >> 12);
|
||||
load_.tile.t.hi = n12(op >> 0);
|
||||
loadTile();
|
||||
} break;
|
||||
|
||||
case 0x35: {
|
||||
tile.format = n3(op >> 53);
|
||||
tile.size = n2(op >> 51);
|
||||
tile.line = n9(op >> 41);
|
||||
tile.address = n9(op >> 32);
|
||||
tile.index = n3(op >> 24);
|
||||
tile.palette = n4(op >> 20);
|
||||
tile.t.clamp = n1(op >> 19);
|
||||
tile.t.mirror = n1(op >> 18);
|
||||
tile.t.mask = n4(op >> 14);
|
||||
tile.t.shift = n4(op >> 10);
|
||||
tile.s.clamp = n1(op >> 9);
|
||||
tile.s.mirror = n1(op >> 8);
|
||||
tile.s.mask = n4(op >> 4);
|
||||
tile.s.shift = n4(op >> 0);
|
||||
setTile();
|
||||
} break;
|
||||
|
||||
case 0x36: {
|
||||
fillRectangle_.x.lo = n12(op >> 44);
|
||||
fillRectangle_.y.lo = n12(op >> 32);
|
||||
fillRectangle_.x.hi = n12(op >> 12);
|
||||
fillRectangle_.y.hi = n12(op >> 0);
|
||||
fillRectangle();
|
||||
} break;
|
||||
|
||||
case 0x37: {
|
||||
set.fill.color = n32(op >> 0);
|
||||
setFillColor();
|
||||
} break;
|
||||
|
||||
case 0x38: {
|
||||
fog.red = n8(op >> 24);
|
||||
fog.green = n8(op >> 16);
|
||||
fog.blue = n8(op >> 8);
|
||||
fog.alpha = n8(op >> 0);
|
||||
setFogColor();
|
||||
} break;
|
||||
|
||||
case 0x39: {
|
||||
blend.red = n8(op >> 24);
|
||||
blend.green = n8(op >> 16);
|
||||
blend.blue = n8(op >> 8);
|
||||
blend.alpha = n8(op >> 0);
|
||||
setBlendColor();
|
||||
} break;
|
||||
|
||||
case 0x3a: {
|
||||
primitive.minimum = n4(op >> 40);
|
||||
primitive.fraction = n8(op >> 32);
|
||||
primitive.red = n8(op >> 24);
|
||||
primitive.green = n8(op >> 16);
|
||||
primitive.blue = n8(op >> 8);
|
||||
primitive.alpha = n8(op >> 0);
|
||||
setPrimitiveColor();
|
||||
} break;
|
||||
|
||||
case 0x3b: {
|
||||
environment.red = n8(op >> 24);
|
||||
environment.green = n8(op >> 16);
|
||||
environment.blue = n8(op >> 8);
|
||||
environment.alpha = n8(op >> 0);
|
||||
setEnvironmentColor();
|
||||
} break;
|
||||
|
||||
case 0x3c: {
|
||||
combine.sba.color[0] = n4(op >> 52);
|
||||
combine.mul.color[0] = n5(op >> 47);
|
||||
combine.sba.alpha[0] = n3(op >> 44);
|
||||
combine.mul.alpha[0] = n3(op >> 41);
|
||||
combine.sba.color[1] = n4(op >> 37);
|
||||
combine.mul.color[1] = n5(op >> 32);
|
||||
combine.sbb.color[0] = n4(op >> 28);
|
||||
combine.sbb.color[1] = n4(op >> 24);
|
||||
combine.sba.alpha[1] = n3(op >> 21);
|
||||
combine.mul.alpha[1] = n3(op >> 18);
|
||||
combine.add.color[0] = n3(op >> 15);
|
||||
combine.sbb.alpha[0] = n3(op >> 12);
|
||||
combine.add.alpha[0] = n3(op >> 9);
|
||||
combine.add.color[1] = n3(op >> 6);
|
||||
combine.sbb.alpha[1] = n3(op >> 3);
|
||||
combine.add.alpha[1] = n3(op >> 0);
|
||||
setCombineMode();
|
||||
} break;
|
||||
|
||||
case 0x3d: {
|
||||
set.texture.format = n3 (op >> 53);
|
||||
set.texture.size = n2 (op >> 51);
|
||||
set.texture.width = n10(op >> 32);
|
||||
set.texture.dramAddress = n26(op >> 0);
|
||||
setTextureImage();
|
||||
} break;
|
||||
|
||||
case 0x3e: {
|
||||
set.mask.dramAddress = n26(op >> 0);
|
||||
setMaskImage();
|
||||
} break;
|
||||
|
||||
case 0x3f: {
|
||||
set.color.format = n3 (op >> 53);
|
||||
set.color.size = n2 (op >> 51);
|
||||
set.color.width = n10(op >> 32);
|
||||
set.color.dramAddress = n26(op >> 0);
|
||||
setColorImage();
|
||||
} break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//0x00
|
||||
|
@ -614,8 +111,11 @@ auto RDP::syncTile() -> void {
|
|||
|
||||
//0x29
|
||||
auto RDP::syncFull() -> void {
|
||||
mi.raise(MI::IRQ::DP);
|
||||
command.pipeBusy = 0;
|
||||
if(!command.crashed) {
|
||||
mi.raise(MI::IRQ::DP);
|
||||
command.bufferBusy = 0;
|
||||
command.pipeBusy = 0;
|
||||
}
|
||||
command.startGclk = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ auto RDP::serialize(serializer& s) -> void {
|
|||
s(command.endValid);
|
||||
s(command.startGclk);
|
||||
s(command.ready);
|
||||
s(command.crashed);
|
||||
|
||||
s(io.bist.check);
|
||||
s(io.bist.go);
|
||||
|
|
|
@ -21,6 +21,7 @@ auto RSP::Debugger::load(Node::Object parent) -> void {
|
|||
|
||||
tracer.instruction = parent->append<Node::Debugger::Tracer::Instruction>("Instruction", "RSP");
|
||||
tracer.instruction->setAddressBits(12, 2);
|
||||
tracer.instruction->setDepth(64);
|
||||
|
||||
tracer.io = parent->append<Node::Debugger::Tracer::Notification>("I/O", "RSP");
|
||||
}
|
||||
|
|
|
@ -525,6 +525,6 @@ auto RSP::Disassembler::ccrRegisterValue(u32 index) const -> string {
|
|||
|
||||
template<typename... P>
|
||||
auto RSP::Disassembler::hint(P&&... p) const -> string {
|
||||
if(showColors) return {"\e[0m\e[37m", forward<P>(p)..., "\e[0m"};
|
||||
return {forward<P>(p)...};
|
||||
if(showColors) return {"\e[0m\e[37m", std::forward<P>(p)..., "\e[0m"};
|
||||
return {std::forward<P>(p)...};
|
||||
}
|
||||
|
|
|
@ -12,11 +12,12 @@ auto RSP::dmaTransferStep() -> void {
|
|||
auto& region = !dma.current.pbusRegion ? dmem : imem;
|
||||
|
||||
if(dma.busy.read) {
|
||||
if constexpr(Accuracy::RSP::Recompiler) {
|
||||
if(dma.current.pbusRegion) {
|
||||
recompiler.invalidate(dma.current.pbusAddress, dma.current.length + 8);
|
||||
}
|
||||
}
|
||||
for(u32 i = 0; i <= dma.current.length; i += 8) {
|
||||
if constexpr(Accuracy::RSP::Recompiler) {
|
||||
if(dma.current.pbusRegion) recompiler.invalidate(dma.current.pbusAddress);
|
||||
}
|
||||
|
||||
u64 data = rdram.ram.read<Dual>(dma.current.dramAddress);
|
||||
region.write<Dual>(dma.current.pbusAddress, data);
|
||||
dma.current.dramAddress += 8;
|
||||
|
|
|
@ -36,6 +36,7 @@ auto RSP::r128::operator()(u32 index) const -> r128 {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
static const __m128i shuffle[16] = {
|
||||
//vector
|
||||
_mm_set_epi8(15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0), //01234567
|
||||
|
@ -60,6 +61,7 @@ auto RSP::r128::operator()(u32 index) const -> r128 {
|
|||
};
|
||||
//todo: benchmark to see if testing for cases 0&1 to return value directly is faster
|
||||
return {uint128_t(_mm_shuffle_epi8(v128, shuffle[index]))};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,8 +105,10 @@ auto RSP::CFC2(r32& rt, u8 rd) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
static const v128 reverse = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
|
||||
rt.u32 = s16(_mm_movemask_epi8(_mm_shuffle_epi8(_mm_packs_epi16(hi, lo), reverse)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,9 +130,11 @@ auto RSP::CTC2(cr32& rt, u8 rd) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
static const v128 mask = _mm_set_epi16(0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080);
|
||||
lo->v128 = _mm_cmpeq_epi8(_mm_and_si128(_mm_shuffle_epi8(r128{~rt.u32 >> 0}, zero), mask), zero);
|
||||
hi->v128 = _mm_cmpeq_epi8(_mm_and_si128(_mm_shuffle_epi8(r128{~rt.u32 >> 8}, zero), mask), zero);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -481,6 +487,7 @@ auto RSP::VABS(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vs0, slt;
|
||||
vs0 = _mm_cmpeq_epi16(vs, zero);
|
||||
slt = _mm_srai_epi16(vs, 15);
|
||||
|
@ -488,6 +495,7 @@ auto RSP::VABS(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
vd = _mm_xor_si128(vd, slt);
|
||||
ACCL = _mm_sub_epi16(vd, slt);
|
||||
vd = _mm_subs_epi16(vd, slt);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -505,6 +513,7 @@ auto RSP::VADD(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), sum, min, max;
|
||||
sum = _mm_add_epi16(vs, vte);
|
||||
ACCL = _mm_sub_epi16(sum, VCOL);
|
||||
|
@ -514,6 +523,7 @@ auto RSP::VADD(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
vd = _mm_adds_epi16(min, max);
|
||||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -531,6 +541,7 @@ auto RSP::VADDC(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), sum;
|
||||
sum = _mm_adds_epu16(vs, vte);
|
||||
ACCL = _mm_add_epi16(vs, vte);
|
||||
|
@ -538,6 +549,7 @@ auto RSP::VADDC(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
VCOL = _mm_cmpeq_epi16(VCOL, zero);
|
||||
VCOH = zero;
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,8 +564,10 @@ auto RSP::VAND(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
ACCL = _mm_and_si128(vs, vt(e));
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -584,6 +598,7 @@ auto RSP::VCH(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), nvt, diff, diff0, vtn, dlez, dgez, mask;
|
||||
VCOL = _mm_xor_si128(vs, vte);
|
||||
VCOL = _mm_cmplt_epi16(VCOL, zero);
|
||||
|
@ -604,6 +619,7 @@ auto RSP::VCH(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
mask = _mm_blendv_epi8(VCCH, VCCL, VCOL);
|
||||
ACCL = _mm_blendv_epi8(vs, nvt, mask);
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,6 +655,7 @@ auto RSP::VCL(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), nvt, diff, ncarry, nvce, diff0, lec1, lec2, leeq, geeq, le, ge, mask;
|
||||
nvt = _mm_xor_si128(vte, VCOL);
|
||||
nvt = _mm_sub_epi16(nvt, VCOL);
|
||||
|
@ -666,6 +683,7 @@ auto RSP::VCL(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
VCOL = zero;
|
||||
VCE = zero;
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -689,6 +707,7 @@ auto RSP::VCR(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), sign, dlez, dgez, nvt, mask;
|
||||
sign = _mm_xor_si128(vs, vte);
|
||||
sign = _mm_srai_epi16(sign, 15);
|
||||
|
@ -705,6 +724,7 @@ auto RSP::VCR(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
VCE = zero;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -722,6 +742,7 @@ auto RSP::VEQ(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), eq;
|
||||
eq = _mm_cmpeq_epi16(vs, vte);
|
||||
VCCL = _mm_andnot_si128(VCOH, eq);
|
||||
|
@ -730,6 +751,7 @@ auto RSP::VEQ(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -747,6 +769,7 @@ auto RSP::VGE(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), eq, gt, es;
|
||||
eq = _mm_cmpeq_epi16(vs, vte);
|
||||
gt = _mm_cmpgt_epi16(vs, vte);
|
||||
|
@ -758,6 +781,7 @@ auto RSP::VGE(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -775,6 +799,7 @@ auto RSP::VLT(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), eq, lt;
|
||||
eq = _mm_cmpeq_epi16(vs, vte);
|
||||
lt = _mm_cmplt_epi16(vs, vte);
|
||||
|
@ -786,6 +811,7 @@ auto RSP::VLT(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -805,6 +831,7 @@ auto RSP::VMACF(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), lo, md, hi, carry, omask;
|
||||
lo = _mm_mullo_epi16(vs, vte);
|
||||
hi = _mm_mulhi_epi16(vs, vte);
|
||||
|
@ -840,6 +867,7 @@ auto RSP::VMACF(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
md = _mm_andnot_si128(hmask, md);
|
||||
vd = _mm_or_si128(omask, md);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -867,6 +895,7 @@ auto RSP::VMADH(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), lo, hi, omask;
|
||||
lo = _mm_mullo_epi16(vs, vte);
|
||||
hi = _mm_mulhi_epi16(vs, vte);
|
||||
|
@ -879,6 +908,7 @@ auto RSP::VMADH(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
lo = _mm_unpacklo_epi16(ACCM, ACCH);
|
||||
hi = _mm_unpackhi_epi16(ACCM, ACCH);
|
||||
vd = _mm_packs_epi32(lo, hi);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -893,6 +923,7 @@ auto RSP::VMADL(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), hi, omask, nhi, nmd, shi, smd, cmask, cval;
|
||||
hi = _mm_mulhi_epu16(vs, vte);
|
||||
omask = _mm_adds_epu16(ACCL, hi);
|
||||
|
@ -912,6 +943,7 @@ auto RSP::VMADL(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
cmask = _mm_and_si128(smd, shi);
|
||||
cval = _mm_cmpeq_epi16(nhi, zero);
|
||||
vd = _mm_blendv_epi8(cval, ACCL, cmask);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -926,6 +958,7 @@ auto RSP::VMADM(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), lo, hi, sign, vta, omask;
|
||||
lo = _mm_mullo_epi16(vs, vte);
|
||||
hi = _mm_mulhi_epu16(vs, vte);
|
||||
|
@ -947,6 +980,7 @@ auto RSP::VMADM(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
lo = _mm_unpacklo_epi16(ACCM, ACCH);
|
||||
hi = _mm_unpackhi_epi16(ACCM, ACCH);
|
||||
vd = _mm_packs_epi32(lo, hi);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -961,6 +995,7 @@ auto RSP::VMADN(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), lo, hi, sign, vsa, omask, nhi, nmd, shi, smd, cmask, cval;
|
||||
lo = _mm_mullo_epi16(vs, vte);
|
||||
hi = _mm_mulhi_epu16(vs, vte);
|
||||
|
@ -986,6 +1021,7 @@ auto RSP::VMADN(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
cmask = _mm_and_si128(smd, shi);
|
||||
cval = _mm_cmpeq_epi16(nhi, zero);
|
||||
vd = _mm_blendv_epi8(cval, ACCL, cmask);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1009,10 +1045,12 @@ auto RSP::VMRG(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
ACCL = _mm_blendv_epi8(vt(e), vs, VCCL);
|
||||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1027,6 +1065,7 @@ auto RSP::VMUDH(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), lo, hi;
|
||||
ACCL = zero;
|
||||
ACCM = _mm_mullo_epi16(vs, vte);
|
||||
|
@ -1034,6 +1073,7 @@ auto RSP::VMUDH(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
lo = _mm_unpacklo_epi16(ACCM, ACCH);
|
||||
hi = _mm_unpackhi_epi16(ACCM, ACCH);
|
||||
vd = _mm_packs_epi32(lo, hi);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1048,10 +1088,12 @@ auto RSP::VMUDL(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
ACCL = _mm_mulhi_epu16(vs, vt(e));
|
||||
ACCM = zero;
|
||||
ACCH = zero;
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1066,6 +1108,7 @@ auto RSP::VMUDM(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), sign, vta;
|
||||
ACCL = _mm_mullo_epi16(vs, vte);
|
||||
ACCM = _mm_mulhi_epu16(vs, vte);
|
||||
|
@ -1074,6 +1117,7 @@ auto RSP::VMUDM(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
ACCM = _mm_sub_epi16(ACCM, vta);
|
||||
ACCH = _mm_srai_epi16(ACCM, 15);
|
||||
vd = ACCM;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1088,6 +1132,7 @@ auto RSP::VMUDN(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), sign, vsa;
|
||||
ACCL = _mm_mullo_epi16(vs, vte);
|
||||
ACCM = _mm_mulhi_epu16(vs, vte);
|
||||
|
@ -1096,6 +1141,7 @@ auto RSP::VMUDN(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
ACCM = _mm_sub_epi16(ACCM, vsa);
|
||||
ACCH = _mm_srai_epi16(ACCM, 15);
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1115,6 +1161,7 @@ auto RSP::VMULF(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), lo, hi, round, sign1, sign2, neq, eq, neg;
|
||||
lo = _mm_mullo_epi16(vs, vte);
|
||||
round = _mm_cmpeq_epi16(zero, zero);
|
||||
|
@ -1138,6 +1185,7 @@ auto RSP::VMULF(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
hi = _mm_or_si128(ACCM, neg);
|
||||
vd = _mm_andnot_si128(ACCH, hi);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1165,9 +1213,11 @@ auto RSP::VNAND(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
ACCL = _mm_and_si128(vs, vt(e));
|
||||
ACCL = _mm_xor_si128(ACCL, invert);
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1185,6 +1235,7 @@ auto RSP::VNE(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), eq, ne;
|
||||
eq = _mm_cmpeq_epi16(vs, vte);
|
||||
ne = _mm_cmpeq_epi16(eq, zero);
|
||||
|
@ -1195,6 +1246,7 @@ auto RSP::VNE(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
VCOH = zero;
|
||||
VCOL = zero;
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1212,9 +1264,11 @@ auto RSP::VNOR(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
ACCL = _mm_or_si128(vs, vt(e));
|
||||
ACCL = _mm_xor_si128(ACCL, invert);
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1229,9 +1283,11 @@ auto RSP::VNXOR(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
ACCL = _mm_xor_si128(vs, vt(e));
|
||||
ACCL = _mm_xor_si128(ACCL, invert);
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1246,8 +1302,10 @@ auto RSP::VOR(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
ACCL = _mm_or_si128(vs, vt(e));
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1359,6 +1417,7 @@ auto RSP::VSUB(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), udiff, sdiff, ov;
|
||||
udiff = _mm_sub_epi16(vte, VCOL);
|
||||
sdiff = _mm_subs_epi16(vte, VCOL);
|
||||
|
@ -1368,6 +1427,7 @@ auto RSP::VSUB(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
vd = _mm_adds_epi16(vd, ov);
|
||||
VCOL = zero;
|
||||
VCOH = zero;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1385,6 +1445,7 @@ auto RSP::VSUBC(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), equal, udiff, diff0;
|
||||
udiff = _mm_subs_epu16(vs, vte);
|
||||
equal = _mm_cmpeq_epi16(vs, vte);
|
||||
|
@ -1393,6 +1454,7 @@ auto RSP::VSUBC(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
VCOL = _mm_andnot_si128(equal, diff0);
|
||||
ACCL = _mm_sub_epi16(vs, vte);
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1407,8 +1469,10 @@ auto RSP::VXOR(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
ACCL = _mm_xor_si128(vs, vt(e));
|
||||
vd = ACCL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1424,9 +1488,11 @@ auto RSP::VZERO(r128& vd, cr128& vs, cr128& vt) -> void {
|
|||
}
|
||||
|
||||
if constexpr(Accuracy::RSP::SIMD) {
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
r128 vte = vt(e), sum, min, max;
|
||||
ACCL = _mm_add_epi16(vs, vte);
|
||||
vd = _mm_xor_si128(vd, vd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +1,60 @@
|
|||
auto RSP::Recompiler::pool(u12 address) -> Pool* {
|
||||
if(context[address >> 8]) return context[address >> 8];
|
||||
|
||||
auto hashcode = XXH3_64bits(self.imem.data + address, 256);
|
||||
|
||||
PoolHashPair pair;
|
||||
pair.pool = (Pool*)allocator.acquire();
|
||||
pair.hashcode = hashcode;
|
||||
|
||||
auto result = pools[address >> 8].find(pair);
|
||||
if(result) {
|
||||
return context[address >> 8] = result->pool;
|
||||
auto RSP::Recompiler::measure(u12 address) -> u12 {
|
||||
u12 start = address;
|
||||
bool hasBranched = 0;
|
||||
while(true) {
|
||||
u32 instruction = self.imem.read<Word>(address);
|
||||
bool branched = isTerminal(instruction);
|
||||
address += 4;
|
||||
if(hasBranched || address == start) break;
|
||||
hasBranched = branched;
|
||||
}
|
||||
|
||||
allocator.reserve(sizeof(Pool));
|
||||
if(auto result = pools[address >> 8].insert(pair)) {
|
||||
return context[address >> 8] = result->pool;
|
||||
}
|
||||
return address - start;
|
||||
}
|
||||
|
||||
throw; //should never occur
|
||||
auto RSP::Recompiler::hash(u12 address, u12 size) -> u64 {
|
||||
u12 end = address + size;
|
||||
if(address < end) {
|
||||
return XXH3_64bits(self.imem.data + address, size);
|
||||
} else {
|
||||
return XXH3_64bits(self.imem.data + address, self.imem.size - address)
|
||||
^ XXH3_64bits(self.imem.data, end);
|
||||
}
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::block(u12 address) -> Block* {
|
||||
if(auto block = pool(address)->blocks[address >> 2 & 0x3ff]) return block;
|
||||
if(dirty) {
|
||||
u12 address = 0;
|
||||
for(auto& block : context) {
|
||||
if(block && (dirty & mask(address, block->size)) != 0) {
|
||||
block = nullptr;
|
||||
}
|
||||
address += 4;
|
||||
}
|
||||
dirty = 0;
|
||||
}
|
||||
|
||||
if(auto block = context[address >> 2]) return block;
|
||||
|
||||
auto size = measure(address);
|
||||
auto hashcode = hash(address, size);
|
||||
|
||||
BlockHashPair pair;
|
||||
pair.hashcode = hashcode;
|
||||
if(auto result = blocks.find(pair)) {
|
||||
return context[address >> 2] = result->block;
|
||||
}
|
||||
|
||||
auto block = emit(address);
|
||||
pool(address)->blocks[address >> 2 & 0x3ff] = block;
|
||||
assert(block->size == size);
|
||||
memory::jitprotect(true);
|
||||
return block;
|
||||
|
||||
pair.block = block;
|
||||
if(auto result = blocks.insert(pair)) {
|
||||
return context[address >> 2] = result->block;
|
||||
}
|
||||
|
||||
throw; //should never occur
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::emit(u12 address) -> Block* {
|
||||
|
@ -40,13 +69,14 @@ auto RSP::Recompiler::emit(u12 address) -> Block* {
|
|||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
beginFunction(3);
|
||||
|
||||
u12 start = address;
|
||||
bool hasBranched = 0;
|
||||
while(true) {
|
||||
u32 instruction = self.imem.read<Word>(address);
|
||||
bool branched = emitEXECUTE(instruction);
|
||||
call(&RSP::instructionEpilogue);
|
||||
address += 4;
|
||||
if(hasBranched || (address & 0xffc) == 0) break; //IMEM boundary
|
||||
if(hasBranched || address == start) break;
|
||||
hasBranched = branched;
|
||||
testJumpEpilog();
|
||||
}
|
||||
|
@ -54,6 +84,7 @@ auto RSP::Recompiler::emit(u12 address) -> Block* {
|
|||
|
||||
memory::jitprotect(false);
|
||||
block->code = endFunction();
|
||||
block->size = address - start;
|
||||
|
||||
//print(hex(PC, 8L), " ", instructions, " ", size(), "\n");
|
||||
return block;
|
||||
|
@ -1355,6 +1386,64 @@ auto RSP::Recompiler::emitSWC2(u32 instruction) -> bool {
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto RSP::Recompiler::isTerminal(u32 instruction) -> bool {
|
||||
switch(instruction >> 26) {
|
||||
|
||||
//SPECIAL
|
||||
case 0x00: {
|
||||
switch(instruction & 0x3f) {
|
||||
|
||||
//JR Rs
|
||||
case 0x08:
|
||||
//JALR Rd,Rs
|
||||
case 0x09:
|
||||
//BREAK
|
||||
case 0x0d:
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//REGIMM
|
||||
case 0x01: {
|
||||
switch(instruction >> 16 & 0x1f) {
|
||||
|
||||
//BLTZ Rs,i16
|
||||
case 0x00:
|
||||
//BGEZ Rs,i16
|
||||
case 0x01:
|
||||
//BLTZAL Rs,i16
|
||||
case 0x10:
|
||||
//BGEZAL Rs,i16
|
||||
case 0x11:
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//J n26
|
||||
case 0x02:
|
||||
//JAL n26
|
||||
case 0x03:
|
||||
//BEQ Rs,Rt,i16
|
||||
case 0x04:
|
||||
//BNE Rs,Rt,i16
|
||||
case 0x05:
|
||||
//BLEZ Rs,i16
|
||||
case 0x06:
|
||||
//BGTZ Rs,i16
|
||||
case 0x07:
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef Sa
|
||||
#undef Rdn
|
||||
#undef Rtn
|
||||
|
|
|
@ -175,7 +175,7 @@ struct RSP : Thread, Memory::IO<RSP> {
|
|||
//vpu.cpp: Vector Processing Unit
|
||||
union r128 {
|
||||
struct { uint128_t u128; };
|
||||
#if defined(ARCHITECTURE_AMD64) || defined(ARCHITECTURE_ARM64)
|
||||
#if ARCHITECTURE_SUPPORTS_SSE4_1
|
||||
struct { __m128i v128; };
|
||||
|
||||
operator __m128i() const { return v128; }
|
||||
|
@ -334,31 +334,31 @@ struct RSP : Thread, Memory::IO<RSP> {
|
|||
}
|
||||
|
||||
u8* code;
|
||||
u12 size;
|
||||
};
|
||||
|
||||
struct Pool {
|
||||
Block* blocks[1024];
|
||||
};
|
||||
|
||||
struct PoolHashPair {
|
||||
auto operator==(const PoolHashPair& source) const -> bool { return hashcode == source.hashcode; }
|
||||
auto operator< (const PoolHashPair& source) const -> bool { return hashcode < source.hashcode; }
|
||||
struct BlockHashPair {
|
||||
auto operator==(const BlockHashPair& source) const -> bool { return hashcode == source.hashcode; }
|
||||
auto operator< (const BlockHashPair& source) const -> bool { return hashcode < source.hashcode; }
|
||||
auto hash() const -> u32 { return hashcode; }
|
||||
|
||||
Pool* pool;
|
||||
u32 hashcode;
|
||||
Block* block;
|
||||
u64 hashcode;
|
||||
};
|
||||
|
||||
auto reset() -> void {
|
||||
for(auto n : range(16)) context[n] = nullptr;
|
||||
for(auto n : range(16)) pools[n].reset();
|
||||
context.fill();
|
||||
blocks.reset();
|
||||
dirty = 0;
|
||||
}
|
||||
|
||||
auto invalidate(u32 address) -> void {
|
||||
context[address >> 8] = nullptr;
|
||||
auto invalidate(u12 address, u12 size = 1) -> void {
|
||||
dirty |= mask(address, size);
|
||||
}
|
||||
|
||||
auto pool(u12 address) -> Pool*;
|
||||
auto measure(u12 address) -> u12;
|
||||
auto hash(u12 address, u12 size) -> u64;
|
||||
|
||||
auto block(u12 address) -> Block*;
|
||||
|
||||
auto emit(u12 address) -> Block*;
|
||||
|
@ -370,9 +370,22 @@ struct RSP : Thread, Memory::IO<RSP> {
|
|||
auto emitLWC2(u32 instruction) -> bool;
|
||||
auto emitSWC2(u32 instruction) -> bool;
|
||||
|
||||
auto isTerminal(u32 instruction) -> bool;
|
||||
|
||||
static auto mask(u12 address, u12 size) -> u64 {
|
||||
//1 bit per 64 bytes
|
||||
u6 s = address >> 6;
|
||||
u6 e = address + size - 1 >> 6;
|
||||
u64 smask = ~0ull << s;
|
||||
u64 emask = ~0ull >> 63 - e;
|
||||
//handle wraparound
|
||||
return s <= e ? smask & emask : smask | emask;
|
||||
}
|
||||
|
||||
bump_allocator allocator;
|
||||
Pool* context[16];
|
||||
set<PoolHashPair> pools[16];
|
||||
array<Block*[1024]> context;
|
||||
hashset<BlockHashPair> blocks;
|
||||
u64 dirty;
|
||||
} recompiler{*this};
|
||||
|
||||
struct Disassembler {
|
||||
|
|
|
@ -6,6 +6,9 @@ auto enumerate() -> vector<string> {
|
|||
return {
|
||||
"[Nintendo] Nintendo 64 (NTSC)",
|
||||
"[Nintendo] Nintendo 64 (PAL)",
|
||||
"[Nintendo] Nintendo 64DD (NTSC-U)",
|
||||
"[Nintendo] Nintendo 64DD (NTSC-J)",
|
||||
"[Nintendo] Nintendo 64DD (NTSC-DEV)",
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -15,15 +18,6 @@ auto load(Node::System& node, string name) -> bool {
|
|||
}
|
||||
|
||||
auto option(string name, string value) -> bool {
|
||||
#if defined(VULKAN)
|
||||
if(name == "Enable Vulkan") vulkan.enable = value.boolean();
|
||||
if(name == "Quality" && value == "SD" ) vulkan.internalUpscale = 1;
|
||||
if(name == "Quality" && value == "HD" ) vulkan.internalUpscale = 2;
|
||||
if(name == "Quality" && value == "UHD") vulkan.internalUpscale = 4;
|
||||
if(name == "Supersampling") vulkan.supersampleScanout = value.boolean();
|
||||
if(vulkan.internalUpscale == 1) vulkan.supersampleScanout = false;
|
||||
vulkan.outputUpscale = vulkan.supersampleScanout ? 1 : vulkan.internalUpscale;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -32,6 +26,10 @@ Queue queue;
|
|||
#include "serialization.cpp"
|
||||
|
||||
auto System::game() -> string {
|
||||
if(dd.node && !cartridge.node) {
|
||||
return dd.title();
|
||||
}
|
||||
|
||||
if(cartridge.node) {
|
||||
return cartridge.title();
|
||||
}
|
||||
|
@ -49,9 +47,15 @@ auto System::load(Node::System& root, string name) -> bool {
|
|||
if(node) unload();
|
||||
|
||||
information = {};
|
||||
if(name.find("Nintendo 64")) {
|
||||
if(name.match("[Nintendo] Nintendo 64 (*)")) {
|
||||
information.name = "Nintendo 64";
|
||||
information.dd = 0;
|
||||
}
|
||||
if(name.match("[Nintendo] Nintendo 64DD (*)")) {
|
||||
information.name = "Nintendo 64";
|
||||
information.dd = 1;
|
||||
}
|
||||
|
||||
if(name.find("NTSC")) {
|
||||
information.region = Region::NTSC;
|
||||
}
|
||||
|
@ -86,19 +90,14 @@ auto System::load(Node::System& root, string name) -> bool {
|
|||
cpu.load(node);
|
||||
rsp.load(node);
|
||||
rdp.load(node);
|
||||
dd.load(node);
|
||||
#if defined(VULKAN)
|
||||
vulkan.load(node);
|
||||
#endif
|
||||
if(_DD()) dd.load(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto System::unload() -> void {
|
||||
if(!node) return;
|
||||
save();
|
||||
#if defined(VULKAN)
|
||||
vulkan.unload();
|
||||
#endif
|
||||
if(vi.screen) vi.screen->quit(); //stop video thread
|
||||
cartridgeSlot.unload();
|
||||
controllerPort1.unload();
|
||||
controllerPort2.unload();
|
||||
|
@ -115,7 +114,7 @@ auto System::unload() -> void {
|
|||
cpu.unload();
|
||||
rsp.unload();
|
||||
rdp.unload();
|
||||
dd.unload();
|
||||
if(_DD()) dd.unload();
|
||||
pak.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
@ -128,6 +127,7 @@ auto System::save() -> void {
|
|||
controllerPort2.save();
|
||||
controllerPort3.save();
|
||||
controllerPort4.save();
|
||||
if(_DD()) dd.save();
|
||||
*/
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,7 @@ auto System::power(bool reset) -> void {
|
|||
queue.reset();
|
||||
cartridge.power(reset);
|
||||
rdram.power(reset);
|
||||
dd.power(reset);
|
||||
if(_DD()) dd.power(reset);
|
||||
mi.power(reset);
|
||||
vi.power(reset);
|
||||
ai.power(reset);
|
||||
|
|
|
@ -6,6 +6,7 @@ struct System {
|
|||
|
||||
auto name() const -> string { return information.name; }
|
||||
auto region() const -> Region { return information.region; }
|
||||
auto _DD() const -> bool { return information.dd; }
|
||||
auto frequency() const -> u32 { return information.frequency; }
|
||||
|
||||
//system.cpp
|
||||
|
@ -25,6 +26,7 @@ private:
|
|||
string name = "Nintendo 64";
|
||||
Region region = Region::NTSC;
|
||||
u32 frequency = 93'750'000 * 2;
|
||||
bool dd = false;
|
||||
} information;
|
||||
|
||||
//serialization.cpp
|
||||
|
@ -35,3 +37,4 @@ extern System system;
|
|||
|
||||
auto Region::NTSC() -> bool { return system.region() == System::Region::NTSC; }
|
||||
auto Region::PAL() -> bool { return system.region() == System::Region::PAL; }
|
||||
auto _DD() -> bool { return system._DD(); }
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include <n64/n64.hpp>
|
||||
|
||||
#if defined(ANGRYLION_RDP)
|
||||
#include "Gfx #1.3.h"
|
||||
#endif
|
||||
|
||||
namespace ares::Nintendo64 {
|
||||
|
||||
|
@ -17,12 +15,6 @@ auto VI::load(Node::Object parent) -> void {
|
|||
u32 width = 640;
|
||||
u32 height = 576;
|
||||
|
||||
#if defined(VULKAN)
|
||||
if (vulkan.enable) {
|
||||
width *= vulkan.outputUpscale;
|
||||
height *= vulkan.outputUpscale;
|
||||
}
|
||||
#endif
|
||||
screen = node->append<Node::Video::Screen>("Screen", width, height);
|
||||
screen->setRefresh({&VI::refresh, this});
|
||||
screen->colors((1 << 24) + (1 << 15), [&](n32 color) -> n64 {
|
||||
|
@ -40,26 +32,14 @@ auto VI::load(Node::Object parent) -> void {
|
|||
return a << 48 | r << 32 | g << 16 | b << 0;
|
||||
}
|
||||
});
|
||||
|
||||
#if defined(VULKAN)
|
||||
if(vulkan.enable) {
|
||||
screen->setSize(vulkan.outputUpscale * 640, vulkan.outputUpscale * 480);
|
||||
if(!vulkan.supersampleScanout) {
|
||||
screen->setScale(1.0 / vulkan.outputUpscale, 1.0 / vulkan.outputUpscale);
|
||||
}
|
||||
} else {
|
||||
screen->setSize(640, 480);
|
||||
}
|
||||
#else
|
||||
|
||||
screen->setSize(640, 480);
|
||||
#endif
|
||||
|
||||
debugger.load(node);
|
||||
}
|
||||
|
||||
auto VI::unload() -> void {
|
||||
debugger = {};
|
||||
screen->quit();
|
||||
node->remove(screen);
|
||||
screen.reset();
|
||||
node.reset();
|
||||
|
@ -74,27 +54,10 @@ auto VI::main() -> void {
|
|||
if(++io.vcounter >= (Region::NTSC() ? 262 : 312) + io.field) {
|
||||
io.vcounter = 0;
|
||||
io.field = io.field + 1 & io.serrate;
|
||||
if(/*!io.field*/true) { // ares decided to run at 30 FPS when interlaced, although that isn't correct
|
||||
#if defined(VULKAN)
|
||||
if (vulkan.enable) {
|
||||
gpuOutputValid = vulkan.scanoutAsync(io.field);
|
||||
vulkan.frame();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ANGRYLION_RDP)
|
||||
#if defined(VULKAN)
|
||||
if (!vulkan.enable) {
|
||||
#endif
|
||||
angrylion::UpdateScreen();
|
||||
#if defined(VULKAN)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
refreshed = true;
|
||||
screen->frame();
|
||||
}
|
||||
|
||||
angrylion::UpdateScreen();
|
||||
refreshed = true;
|
||||
screen->frame();
|
||||
}
|
||||
|
||||
if(Region::NTSC()) step(system.frequency() / 60 / 262);
|
||||
|
@ -108,74 +71,12 @@ auto VI::step(u32 clocks) -> void {
|
|||
bool BobDeinterlace = 0;
|
||||
|
||||
auto VI::refresh() -> void {
|
||||
#if defined(VULKAN)
|
||||
if(vulkan.enable && gpuOutputValid) {
|
||||
const u8* rgba = nullptr;
|
||||
u32 width = 0, height = 0;
|
||||
vulkan.mapScanoutRead(rgba, width, height);
|
||||
if(rgba) {
|
||||
screen->setViewport(0, 0, width, height);
|
||||
for(u32 y : range(height)) {
|
||||
auto source = rgba + width * y * sizeof(u32);
|
||||
auto target = screen->pixels(1).data() + y * vulkan.outputUpscale * 640;
|
||||
for(u32 x : range(width)) {
|
||||
target[x] = source[x * 4 + 0] << 16 | source[x * 4 + 1] << 8 | source[x * 4 + 2] << 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
screen->setViewport(0, 0, 1, 1);
|
||||
screen->pixels(1).data()[0] = 0;
|
||||
}
|
||||
vulkan.unmapScanoutRead();
|
||||
vulkan.endScanout();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ANGRYLION_RDP)
|
||||
#if defined(VULKAN)
|
||||
if(!vulkan.enable) {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
u32 width = 640;
|
||||
u32 height = Region::PAL() ? 576 : 480;
|
||||
screen->setViewport(0, 0, width, height);
|
||||
u32* src = angrylion::FinalizeFrame(BobDeinterlace);
|
||||
u32* dst = screen->pixels(1).data();
|
||||
memcpy(dst, src, width * height * sizeof(u32));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 pitch = vi.io.width;
|
||||
u32 width = vi.io.width; //vi.io.xscale <= 0x300 ? 320 : 640;
|
||||
u32 height = vi.io.yscale <= 0x400 ? 239 : 478;
|
||||
u32 width = 640;
|
||||
u32 height = Region::PAL() ? 576 : 480;
|
||||
screen->setViewport(0, 0, width, height);
|
||||
|
||||
if(vi.io.colorDepth == 2) {
|
||||
//15bpp
|
||||
for(u32 y : range(height)) {
|
||||
u32 address = vi.io.dramAddress + y * pitch * 2;
|
||||
auto line = screen->pixels(1).data() + y * 640;
|
||||
for(u32 x : range(min(width, pitch))) {
|
||||
u16 data = bus.read<Half>(address + x * 2);
|
||||
*line++ = 1 << 24 | data >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(vi.io.colorDepth == 3) {
|
||||
//24bpp
|
||||
for(u32 y : range(height)) {
|
||||
u32 address = vi.io.dramAddress + y * pitch * 4;
|
||||
auto line = screen->pixels(1).data() + y * 640;
|
||||
for(u32 x : range(min(width, pitch))) {
|
||||
u32 data = bus.read<Word>(address + x * 4);
|
||||
*line++ = data >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
u32* src = angrylion::FinalizeFrame(BobDeinterlace);
|
||||
u32* dst = screen->pixels(1).data();
|
||||
memcpy(dst, src, width * height * sizeof(u32));
|
||||
}
|
||||
|
||||
auto VI::power(bool reset) -> void {
|
||||
|
@ -183,10 +84,6 @@ auto VI::power(bool reset) -> void {
|
|||
screen->power();
|
||||
io = {};
|
||||
refreshed = false;
|
||||
|
||||
#if defined(VULKAN)
|
||||
gpuOutputValid = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ struct adaptive_array {
|
|||
}
|
||||
|
||||
auto append(T&& value) -> void {
|
||||
new(_pool.t + _size++) T(move(value));
|
||||
new(_pool.t + _size++) T(std::move(value));
|
||||
}
|
||||
|
||||
auto begin() { return &_pool.t[0]; }
|
||||
|
|
|
@ -12,7 +12,7 @@ template<typename T, typename U> constexpr auto min(const T& t, const U& u) -> T
|
|||
}
|
||||
|
||||
template<typename T, typename U, typename... P> constexpr auto min(const T& t, const U& u, P&&... p) -> T {
|
||||
return t < u ? min(t, forward<P>(p)...) : min(u, forward<P>(p)...);
|
||||
return t < u ? min(t, std::forward<P>(p)...) : min(u, std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
template<typename T, typename U> constexpr auto max(const T& t, const U& u) -> T {
|
||||
|
@ -20,7 +20,7 @@ template<typename T, typename U> constexpr auto max(const T& t, const U& u) -> T
|
|||
}
|
||||
|
||||
template<typename T, typename U, typename... P> constexpr auto max(const T& t, const U& u, P&&... p) -> T {
|
||||
return t > u ? max(t, forward<P>(p)...) : max(u, forward<P>(p)...);
|
||||
return t > u ? max(t, std::forward<P>(p)...) : max(u, std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace nall {
|
|||
struct any {
|
||||
any() = default;
|
||||
any(const any& source) { operator=(source); }
|
||||
any(any&& source) { operator=(move(source)); }
|
||||
any(any&& source) { operator=(std::move(source)); }
|
||||
template<typename T> any(const T& value) { operator=(value); }
|
||||
~any() { reset(); }
|
||||
|
||||
|
@ -52,12 +52,14 @@ struct any {
|
|||
}
|
||||
|
||||
auto operator=(const any& source) -> any& {
|
||||
if(this == &source) return *this;
|
||||
if(container) { delete container; container = nullptr; }
|
||||
if(source.container) container = source.container->copy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator=(any&& source) -> any& {
|
||||
if(this == &source) return *this;
|
||||
if(container) delete container;
|
||||
container = source.container;
|
||||
source.container = nullptr;
|
||||
|
|
|
@ -16,12 +16,13 @@ struct ODBC {
|
|||
auto operator=(const Statement& source) -> Statement& = delete;
|
||||
|
||||
Statement(SQLHANDLE statement) : _statement(statement) {}
|
||||
Statement(Statement&& source) { operator=(move(source)); }
|
||||
Statement(Statement&& source) { operator=(std::move(source)); }
|
||||
|
||||
auto operator=(Statement&& source) -> Statement& {
|
||||
if(this == &source) return *this;
|
||||
_statement = source._statement;
|
||||
_output = source._output;
|
||||
_values = move(source._values);
|
||||
_values = std::move(source._values);
|
||||
source._statement = nullptr;
|
||||
source._output = 0;
|
||||
return *this;
|
||||
|
@ -98,7 +99,7 @@ struct ODBC {
|
|||
auto operator=(const Query& source) -> Query& = delete;
|
||||
|
||||
Query(SQLHANDLE statement) : Statement(statement) {}
|
||||
Query(Query&& source) : Statement(source._statement) { operator=(move(source)); }
|
||||
Query(Query&& source) : Statement(source._statement) { operator=(std::move(source)); }
|
||||
|
||||
~Query() {
|
||||
if(statement()) {
|
||||
|
@ -108,8 +109,10 @@ struct ODBC {
|
|||
}
|
||||
|
||||
auto operator=(Query&& source) -> Query& {
|
||||
Statement::operator=(move(source));
|
||||
_bindings = move(source._bindings);
|
||||
if(this == &source) return *this;
|
||||
if(statement()) SQLFreeHandle(SQL_HANDLE_STMT, _statement);
|
||||
Statement::operator=(std::move(source));
|
||||
_bindings = std::move(source._bindings);
|
||||
_result = source._result;
|
||||
_input = source._input;
|
||||
_stepped = source._stepped;
|
||||
|
@ -278,7 +281,7 @@ struct ODBC {
|
|||
_result = SQLPrepareA(_statement, (SQLCHAR*)statement.data(), SQL_NTS);
|
||||
if(!success()) return {nullptr};
|
||||
|
||||
bind(query, forward<P>(p)...);
|
||||
bind(query, std::forward<P>(p)...);
|
||||
return query;
|
||||
}
|
||||
|
||||
|
@ -288,7 +291,7 @@ private:
|
|||
auto bind(Query&) -> void {}
|
||||
template<typename T, typename... P> auto bind(Query& query, const T& value, P&&... p) -> void {
|
||||
query.bind(value);
|
||||
bind(query, forward<P>(p)...);
|
||||
bind(query, std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
SQLHANDLE _environment = nullptr;
|
||||
|
|
|
@ -16,9 +16,10 @@ struct SQLite3 {
|
|||
auto operator=(const Statement& source) -> Statement& = delete;
|
||||
|
||||
Statement(sqlite3_stmt* statement) : _statement(statement) {}
|
||||
Statement(Statement&& source) { operator=(move(source)); }
|
||||
Statement(Statement&& source) { operator=(std::move(source)); }
|
||||
|
||||
auto operator=(Statement&& source) -> Statement& {
|
||||
if(this == &source) return *this;
|
||||
_statement = source._statement;
|
||||
_response = source._response;
|
||||
_output = source._output;
|
||||
|
@ -90,7 +91,7 @@ struct SQLite3 {
|
|||
auto operator=(const Query& source) -> Query& = delete;
|
||||
|
||||
Query(sqlite3_stmt* statement) : Statement(statement) {}
|
||||
Query(Query&& source) : Statement(source._statement) { operator=(move(source)); }
|
||||
Query(Query&& source) : Statement(source._statement) { operator=(std::move(source)); }
|
||||
|
||||
~Query() {
|
||||
sqlite3_finalize(statement());
|
||||
|
@ -98,6 +99,8 @@ struct SQLite3 {
|
|||
}
|
||||
|
||||
auto operator=(Query&& source) -> Query& {
|
||||
if(this == &source) return *this;
|
||||
sqlite3_finalize(statement());
|
||||
_statement = source._statement;
|
||||
_input = source._input;
|
||||
source._statement = nullptr;
|
||||
|
@ -192,7 +195,7 @@ struct SQLite3 {
|
|||
}
|
||||
|
||||
Query query{_statement};
|
||||
bind(query, forward<P>(p)...);
|
||||
bind(query, std::forward<P>(p)...);
|
||||
return query;
|
||||
}
|
||||
|
||||
|
@ -208,7 +211,7 @@ protected:
|
|||
auto bind(Query&) -> void {}
|
||||
template<typename T, typename... P> auto bind(Query& query, const T& value, P&&... p) -> void {
|
||||
query.bind(value);
|
||||
bind(query, forward<P>(p)...);
|
||||
bind(query, std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
bool _debug = false;
|
||||
|
|
|
@ -57,17 +57,17 @@ private:
|
|||
|
||||
template<typename... P> auto input(Hash::SHA512& hash, u256 value, P&&... p) const -> void {
|
||||
for(u32 byte : range(32)) hash.input(u8(value >> byte * 8));
|
||||
input(hash, forward<P>(p)...);
|
||||
input(hash, std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
template<typename... P> auto input(Hash::SHA512& hash, array_view<u8> value, P&&... p) const -> void {
|
||||
hash.input(value);
|
||||
input(hash, forward<P>(p)...);
|
||||
input(hash, std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
template<typename... P> auto hash(P&&... p) const -> u512 {
|
||||
Hash::SHA512 hash;
|
||||
input(hash, forward<P>(p)...);
|
||||
input(hash, std::forward<P>(p)...);
|
||||
u512 result;
|
||||
for(auto byte : reverse(hash.output())) result = result << 8 | byte;
|
||||
return result;
|
||||
|
|
|
@ -29,11 +29,14 @@ struct file_buffer {
|
|||
file_buffer() = default;
|
||||
file_buffer(const string& filename, u32 mode) { open(filename, mode); }
|
||||
|
||||
file_buffer(file_buffer&& source) { operator=(move(source)); }
|
||||
file_buffer(file_buffer&& source) { operator=(std::move(source)); }
|
||||
|
||||
~file_buffer() { close(); }
|
||||
|
||||
auto operator=(file_buffer&& source) -> file_buffer& {
|
||||
if(this == &source) return *this;
|
||||
close();
|
||||
|
||||
buffer = source.buffer;
|
||||
bufferOffset = source.bufferOffset;
|
||||
bufferDirty = source.bufferDirty;
|
||||
|
@ -122,7 +125,7 @@ struct file_buffer {
|
|||
}
|
||||
|
||||
template<typename... P> auto print(P&&... p) -> void {
|
||||
string s{forward<P>(p)...};
|
||||
string s{std::forward<P>(p)...};
|
||||
for(auto& byte : s) write(byte);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ struct file_map {
|
|||
auto operator=(const file_map&) = delete;
|
||||
|
||||
file_map() = default;
|
||||
file_map(file_map&& source) { operator=(move(source)); }
|
||||
file_map(file_map&& source) { operator=(std::move(source)); }
|
||||
file_map(const string& filename, u32 mode) { open(filename, mode); }
|
||||
|
||||
~file_map() { close(); }
|
||||
|
@ -55,6 +55,9 @@ private:
|
|||
|
||||
public:
|
||||
auto operator=(file_map&& source) -> file_map& {
|
||||
if(this == &source) return *this;
|
||||
close();
|
||||
|
||||
_open = source._open;
|
||||
_data = source._data;
|
||||
_size = source._size;
|
||||
|
@ -147,6 +150,9 @@ public:
|
|||
|
||||
public:
|
||||
auto operator=(file_map&& source) -> file_map& {
|
||||
if(this == &source) return *this;
|
||||
close();
|
||||
|
||||
_open = source._open;
|
||||
_data = source._data;
|
||||
_size = source._size;
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
#pragma once
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
|
||||
//the c/c++ standard library fenv.h has numerous design and implementation flaws:
|
||||
//- forces updates to both x87 and sse state on amd64
|
||||
//- 'set' operations require a register read + modify + write
|
||||
//- some implementations define api flags differently from native registers (msvc)
|
||||
//- some implementations are so buggy they don't even use the correct registers (mingw/arm64)
|
||||
|
||||
//here we provide our own thin abstraction that falls back on fenv.h as a last resort.
|
||||
//note: the state of the control register is cached, so changes made via external means
|
||||
//will be reverted the next time it is modified by this wrapper.
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct float_env {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
static constexpr u32 allExcept = _MM_EXCEPT_MASK;
|
||||
static constexpr u32 denormal = _MM_EXCEPT_DENORM;
|
||||
static constexpr u32 inexact = _MM_EXCEPT_INEXACT;
|
||||
static constexpr u32 underflow = _MM_EXCEPT_UNDERFLOW;
|
||||
static constexpr u32 overflow = _MM_EXCEPT_OVERFLOW;
|
||||
static constexpr u32 divByZero = _MM_EXCEPT_DIV_ZERO;
|
||||
static constexpr u32 invalid = _MM_EXCEPT_INVALID;
|
||||
static constexpr u32 downward = _MM_ROUND_DOWN;
|
||||
static constexpr u32 toNearest = _MM_ROUND_NEAREST;
|
||||
static constexpr u32 towardZero = _MM_ROUND_TOWARD_ZERO;
|
||||
static constexpr u32 upward = _MM_ROUND_UP;
|
||||
#elif defined(ARCHITECTURE_ARM64)
|
||||
static constexpr u32 allExcept = 0x3f;
|
||||
static constexpr u32 denormal = 0x20;
|
||||
static constexpr u32 inexact = 0x10;
|
||||
static constexpr u32 underflow = 0x08;
|
||||
static constexpr u32 overflow = 0x04;
|
||||
static constexpr u32 divByZero = 0x02;
|
||||
static constexpr u32 invalid = 0x01;
|
||||
static constexpr u32 downward = 2 << 22;
|
||||
static constexpr u32 toNearest = 0 << 22;
|
||||
static constexpr u32 towardZero = 3 << 22;
|
||||
static constexpr u32 upward = 1 << 22;
|
||||
#else
|
||||
static constexpr u32 allExcept = FE_ALL_EXCEPT;
|
||||
#if defined(FE_DENORMAL)
|
||||
static constexpr u32 denormal = FE_DENORMAL;
|
||||
#else
|
||||
static constexpr u32 denormal = 0;
|
||||
#endif
|
||||
static constexpr u32 inexact = FE_INEXACT;
|
||||
static constexpr u32 underflow = FE_UNDERFLOW;
|
||||
static constexpr u32 overflow = FE_OVERFLOW;
|
||||
static constexpr u32 divByZero = FE_DIVBYZERO;
|
||||
static constexpr u32 invalid = FE_INVALID;
|
||||
static constexpr u32 downward = FE_DOWNWARD;
|
||||
static constexpr u32 toNearest = FE_TONEAREST;
|
||||
static constexpr u32 towardZero = FE_TOWARDZERO;
|
||||
static constexpr u32 upward = FE_UPWARD;
|
||||
#endif
|
||||
static constexpr u32 roundMask = downward | toNearest | towardZero | upward;
|
||||
|
||||
u32 control = 0;
|
||||
|
||||
float_env() {
|
||||
control = getControl();
|
||||
}
|
||||
|
||||
auto setRound(u32 mode) -> void {
|
||||
control &= ~roundMask;
|
||||
control |= mode & roundMask;
|
||||
setControl();
|
||||
}
|
||||
|
||||
auto getRound() -> u32 {
|
||||
return control & roundMask;
|
||||
}
|
||||
|
||||
auto testExcept(u32 mask) -> u32 {
|
||||
return getStatus() & mask & allExcept;
|
||||
}
|
||||
|
||||
auto clearExcept() -> void {
|
||||
clearStatus();
|
||||
}
|
||||
|
||||
private:
|
||||
auto getControl() -> u32 {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
return _mm_getcsr() & ~allExcept;
|
||||
#elif defined(ARCHITECTURE_ARM64)
|
||||
#if defined(COMPILER_MICROSOFT)
|
||||
return _ReadStatusReg(ARM64_FPCR);
|
||||
#else
|
||||
u64 value;
|
||||
__asm__ __volatile__("mrs %0, FPCR" : "=r"(value));
|
||||
return value;
|
||||
#endif
|
||||
#else
|
||||
return fegetround();
|
||||
#endif
|
||||
}
|
||||
|
||||
auto getStatus() -> u32 {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
return _mm_getcsr() & allExcept;
|
||||
#elif defined(ARCHITECTURE_ARM64)
|
||||
#if defined(COMPILER_MICROSOFT)
|
||||
return _ReadStatusReg(ARM64_FPSR);
|
||||
#else
|
||||
u64 value;
|
||||
__asm__ __volatile__("mrs %0, FPSR" : "=r"(value));
|
||||
return value;
|
||||
#endif
|
||||
#else
|
||||
return fetestexcept(allExcept);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto setControl() -> void {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
_mm_setcsr(control | getStatus());
|
||||
#elif defined(ARCHITECTURE_ARM64)
|
||||
#if defined(COMPILER_MICROSOFT)
|
||||
_WriteStatusReg(ARM64_FPCR, control);
|
||||
#else
|
||||
u64 value = control;
|
||||
__asm__ __volatile__("msr FPCR, %0" : : "r"(value));
|
||||
#endif
|
||||
#else
|
||||
fesetround(control & roundMask);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto clearStatus() -> void {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
_mm_setcsr(control);
|
||||
#elif defined(ARCHITECTURE_ARM64)
|
||||
#if defined(COMPILER_MICROSOFT)
|
||||
_WriteStatusReg(ARM64_FPSR, 0);
|
||||
#else
|
||||
u64 value = 0;
|
||||
__asm__ __volatile__("msr FPSR, %0" : : "r"(value));
|
||||
#endif
|
||||
#else
|
||||
feclearexcept(allExcept);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -26,7 +26,7 @@ template<typename R, typename... P> struct function<auto (P...) -> R> {
|
|||
~function() { if(callback) delete callback; }
|
||||
|
||||
explicit operator bool() const { return callback; }
|
||||
auto operator()(P... p) const -> R { return (*callback)(forward<P>(p)...); }
|
||||
auto operator()(P... p) const -> R { return (*callback)(std::forward<P>(p)...); }
|
||||
auto reset() -> void { if(callback) { delete callback; callback = nullptr; } }
|
||||
|
||||
auto operator=(const function& source) -> function& {
|
||||
|
@ -54,7 +54,7 @@ private:
|
|||
|
||||
struct global : container {
|
||||
auto (*function)(P...) -> R;
|
||||
auto operator()(P... p) const -> R { return function(forward<P>(p)...); }
|
||||
auto operator()(P... p) const -> R { return function(std::forward<P>(p)...); }
|
||||
auto copy() const -> container* { return new global(function); }
|
||||
global(auto (*function)(P...) -> R) : function(function) {}
|
||||
};
|
||||
|
@ -62,14 +62,14 @@ private:
|
|||
template<typename C> struct member : container {
|
||||
auto (C::*function)(P...) -> R;
|
||||
C* object;
|
||||
auto operator()(P... p) const -> R { return (object->*function)(forward<P>(p)...); }
|
||||
auto operator()(P... p) const -> R { return (object->*function)(std::forward<P>(p)...); }
|
||||
auto copy() const -> container* { return new member(function, object); }
|
||||
member(auto (C::*function)(P...) -> R, C* object) : function(function), object(object) {}
|
||||
};
|
||||
|
||||
template<typename L> struct lambda : container {
|
||||
mutable L object;
|
||||
auto operator()(P... p) const -> R { return object(forward<P>(p)...); }
|
||||
auto operator()(P... p) const -> R { return object(std::forward<P>(p)...); }
|
||||
auto copy() const -> container* { return new lambda(object); }
|
||||
lambda(const L& object) : object(object) {}
|
||||
};
|
||||
|
|
|
@ -17,20 +17,22 @@ struct hashset {
|
|||
hashset() = default;
|
||||
hashset(u32 length) : length(bit::round(length)) {}
|
||||
hashset(const hashset& source) { operator=(source); }
|
||||
hashset(hashset&& source) { operator=(move(source)); }
|
||||
hashset(hashset&& source) { operator=(std::move(source)); }
|
||||
~hashset() { reset(); }
|
||||
|
||||
auto operator=(const hashset& source) -> hashset& {
|
||||
if(this == &source) return *this;
|
||||
reset();
|
||||
if(source.pool) {
|
||||
for(u32 n : range(source.count)) {
|
||||
insert(*source.pool[n]);
|
||||
for(u32 n : range(source.length)) {
|
||||
if(source.pool[n]) insert(*source.pool[n]);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator=(hashset&& source) -> hashset& {
|
||||
if(this == &source) return *this;
|
||||
reset();
|
||||
pool = source.pool;
|
||||
length = source.length;
|
||||
|
@ -53,7 +55,7 @@ struct hashset {
|
|||
pool[n] = nullptr;
|
||||
}
|
||||
}
|
||||
delete pool;
|
||||
delete[] pool;
|
||||
pool = nullptr;
|
||||
}
|
||||
length = 8;
|
||||
|
@ -61,6 +63,8 @@ struct hashset {
|
|||
}
|
||||
|
||||
auto reserve(u32 size) -> void {
|
||||
if(length >= size) return;
|
||||
|
||||
//ensure all items will fit into pool (with <= 50% load) and amortize growth
|
||||
size = bit::round(max(size, count << 1));
|
||||
T** copy = new T*[size]();
|
||||
|
@ -76,7 +80,7 @@ struct hashset {
|
|||
}
|
||||
}
|
||||
|
||||
delete pool;
|
||||
delete[] pool;
|
||||
pool = copy;
|
||||
length = size;
|
||||
}
|
||||
|
@ -98,15 +102,20 @@ struct hashset {
|
|||
|
||||
//double pool size when load is >= 50%
|
||||
if(count >= (length >> 1)) reserve(length << 1);
|
||||
count++;
|
||||
|
||||
u32 hash = value.hash() & (length - 1);
|
||||
while(pool[hash]) if(++hash >= length) hash = 0;
|
||||
while(pool[hash]) {
|
||||
if(value == *pool[hash]) return nothing;
|
||||
if(++hash >= length) hash = 0;
|
||||
}
|
||||
count++;
|
||||
pool[hash] = new T(value);
|
||||
|
||||
return *pool[hash];
|
||||
}
|
||||
|
||||
#if 0
|
||||
//does not work! todo: implement tombstones or rehashing to fill gaps.
|
||||
auto remove(const T& value) -> bool {
|
||||
if(!pool) return false;
|
||||
|
||||
|
@ -123,6 +132,7 @@ struct hashset {
|
|||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
T** pool = nullptr;
|
||||
|
|
|
@ -7,7 +7,7 @@ inline image::image(const image& source) {
|
|||
}
|
||||
|
||||
inline image::image(image&& source) {
|
||||
operator=(forward<image>(source));
|
||||
operator=(std::forward<image>(source));
|
||||
}
|
||||
|
||||
inline image::image(bool endian, u32 depth, u64 alphaMask, u64 redMask, u64 greenMask, u64 blueMask) {
|
||||
|
|
|
@ -7,7 +7,7 @@ inline multiFactorImage::multiFactorImage(const multiFactorImage& source) {
|
|||
}
|
||||
|
||||
inline multiFactorImage::multiFactorImage(multiFactorImage&& source) {
|
||||
operator=(forward<multiFactorImage>(source));
|
||||
operator=(std::forward<multiFactorImage>(source));
|
||||
}
|
||||
|
||||
inline multiFactorImage::multiFactorImage(const image& lowDPI, const image& highDPI) {
|
||||
|
@ -20,7 +20,7 @@ inline multiFactorImage::multiFactorImage(const image& source) {
|
|||
}
|
||||
|
||||
inline multiFactorImage::multiFactorImage(image&& source) {
|
||||
operator=(forward<multiFactorImage>(source));
|
||||
operator=(std::forward<multiFactorImage>(source));
|
||||
}
|
||||
|
||||
inline multiFactorImage::multiFactorImage() {
|
||||
|
|
|
@ -173,7 +173,7 @@ inline auto image::transform(bool outputEndian, u32 outputDepth, u64 outputAlpha
|
|||
}
|
||||
}
|
||||
|
||||
operator=(move(output));
|
||||
operator=(std::move(output));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ struct Instance {
|
|||
auto construct(P&&... p) {
|
||||
if(constructed) return;
|
||||
constructed = true;
|
||||
new((void*)(&instance.object)) T(forward<P>(p)...);
|
||||
new((void*)(&instance.object)) T(std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
auto destruct() -> void {
|
||||
|
|
|
@ -173,7 +173,6 @@ namespace nall {
|
|||
struct Architecture {
|
||||
static constexpr bool x86 = 1;
|
||||
static constexpr bool amd64 = 0;
|
||||
static constexpr bool sse41 = 0;
|
||||
static constexpr bool arm64 = 0;
|
||||
static constexpr bool arm32 = 0;
|
||||
static constexpr bool ppc64 = 0;
|
||||
|
@ -181,14 +180,12 @@ namespace nall {
|
|||
};
|
||||
#elif defined(__amd64__) || defined(_M_AMD64)
|
||||
#define ARCHITECTURE_AMD64
|
||||
#if defined(__SSE4_1__)
|
||||
#define ARCHITECTURE_SUPPORTS_SSE4_1 1
|
||||
#endif
|
||||
struct Architecture {
|
||||
static constexpr bool x86 = 0;
|
||||
static constexpr bool amd64 = 1;
|
||||
#ifdef __SSE4_1__
|
||||
static constexpr bool sse41 = 1;
|
||||
#else
|
||||
static constexpr bool sse41 = 0;
|
||||
#endif
|
||||
static constexpr bool arm64 = 0;
|
||||
static constexpr bool arm32 = 0;
|
||||
static constexpr bool ppc64 = 0;
|
||||
|
@ -196,10 +193,10 @@ namespace nall {
|
|||
};
|
||||
#elif defined(__aarch64__)
|
||||
#define ARCHITECTURE_ARM64
|
||||
#define ARCHITECTURE_SUPPORTS_SSE4_1 1 // simulated via sse2neon.h
|
||||
struct Architecture {
|
||||
static constexpr bool x86 = 0;
|
||||
static constexpr bool amd64 = 0;
|
||||
static constexpr bool sse41 = 1; // simulated via sse2neon.h
|
||||
static constexpr bool arm64 = 1;
|
||||
static constexpr bool arm32 = 0;
|
||||
static constexpr bool ppc64 = 0;
|
||||
|
@ -210,7 +207,6 @@ namespace nall {
|
|||
struct Architecture {
|
||||
static constexpr bool x86 = 0;
|
||||
static constexpr bool amd64 = 0;
|
||||
static constexpr bool sse41 = 0;
|
||||
static constexpr bool arm64 = 0;
|
||||
static constexpr bool arm32 = 1;
|
||||
static constexpr bool ppc64 = 0;
|
||||
|
@ -221,7 +217,6 @@ namespace nall {
|
|||
struct Architecture {
|
||||
static constexpr bool x86 = 0;
|
||||
static constexpr bool amd64 = 0;
|
||||
static constexpr bool sse41 = 0;
|
||||
static constexpr bool arm64 = 0;
|
||||
static constexpr bool arm32 = 0;
|
||||
static constexpr bool ppc64 = 1;
|
||||
|
@ -232,7 +227,6 @@ namespace nall {
|
|||
struct Architecture {
|
||||
static constexpr bool x86 = 0;
|
||||
static constexpr bool amd64 = 0;
|
||||
static constexpr bool sse41 = 0;
|
||||
static constexpr bool arm64 = 0;
|
||||
static constexpr bool arm32 = 0;
|
||||
static constexpr bool ppc64 = 0;
|
||||
|
@ -242,6 +236,10 @@ namespace nall {
|
|||
#error "unable to detect architecture"
|
||||
#endif
|
||||
|
||||
#if !defined(ARCHITECTURE_SUPPORTS_SSE4_1)
|
||||
#define ARCHITECTURE_SUPPORTS_SSE4_1 0
|
||||
#endif
|
||||
|
||||
/* Endian detection */
|
||||
|
||||
#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) || defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64)
|
||||
|
|
|
@ -42,7 +42,7 @@ struct Locale {
|
|||
|
||||
template<typename... P>
|
||||
auto operator()(string ns, string input, P&&... p) const -> string {
|
||||
vector<string> arguments{forward<P>(p)...};
|
||||
vector<string> arguments{std::forward<P>(p)...};
|
||||
if(selected) {
|
||||
for(auto node : selected().document) {
|
||||
if(node.name() == "namespace" && node.text() == ns) {
|
||||
|
@ -66,12 +66,12 @@ struct Locale {
|
|||
|
||||
template<typename... P>
|
||||
auto operator()(string input, P&&... p) const -> string {
|
||||
return _locale(_namespace, input, forward<P>(p)...);
|
||||
return _locale(_namespace, input, std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
template<typename... P>
|
||||
auto tr(string input, P&&... p) const -> string {
|
||||
return _locale(_namespace, input, forward<P>(p)...);
|
||||
return _locale(_namespace, input, std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -13,14 +13,14 @@ struct maybe {
|
|||
maybe() {}
|
||||
maybe(nothing_t) {}
|
||||
maybe(const T& source) { operator=(source); }
|
||||
maybe(T&& source) { operator=(move(source)); }
|
||||
maybe(T&& source) { operator=(std::move(source)); }
|
||||
maybe(const maybe& source) { operator=(source); }
|
||||
maybe(maybe&& source) { operator=(move(source)); }
|
||||
maybe(maybe&& source) { operator=(std::move(source)); }
|
||||
~maybe() { reset(); }
|
||||
|
||||
auto operator=(nothing_t) -> maybe& { reset(); return *this; }
|
||||
auto operator=(const T& source) -> maybe& { reset(); _valid = true; new(&_value.t) T(source); return *this; }
|
||||
auto operator=(T&& source) -> maybe& { reset(); _valid = true; new(&_value.t) T(move(source)); return *this; }
|
||||
auto operator=(T&& source) -> maybe& { reset(); _valid = true; new(&_value.t) T(std::move(source)); return *this; }
|
||||
|
||||
auto operator=(const maybe& source) -> maybe& {
|
||||
if(this == &source) return *this;
|
||||
|
@ -32,7 +32,7 @@ struct maybe {
|
|||
auto operator=(maybe&& source) -> maybe& {
|
||||
if(this == &source) return *this;
|
||||
reset();
|
||||
if(_valid = source._valid) new(&_value.t) T(move(source.get()));
|
||||
if(_valid = source._valid) new(&_value.t) T(std::move(source.get()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ template<typename T> auto fill(void* target, u32 capacity, const T& value) -> T*
|
|||
|
||||
template<typename T, typename U, typename... P> auto assign(T* target, const U& value, P&&... p) -> void {
|
||||
*target++ = value;
|
||||
assign(target, forward<P>(p)...);
|
||||
assign(target, std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
template<u32 size, typename T> auto readl(const void* source) -> T {
|
||||
|
@ -204,7 +204,7 @@ inline auto map(u32 size, bool executable) -> void* {
|
|||
int flags = MAP_ANON | MAP_PRIVATE;
|
||||
if(executable) {
|
||||
prot |= PROT_EXEC;
|
||||
#if defined(PLATFORM_MACOS) && defined(ARCHITECTURE_ARM64)
|
||||
#if defined(PLATFORM_MACOS)
|
||||
flags |= MAP_JIT;
|
||||
#endif
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ inline auto protect(void* target, u32 size, bool executable) -> void {
|
|||
}
|
||||
|
||||
inline auto jitprotect(bool executable) -> void {
|
||||
#if defined(PLATFORM_MACOS) && defined(ARCHITECTURE_ARM64)
|
||||
#if defined(PLATFORM_MACOS)
|
||||
if(__builtin_available(macOS 11.0, *)) {
|
||||
pthread_jit_write_protect_np(executable);
|
||||
}
|
||||
|
|
|
@ -29,12 +29,12 @@ template<typename T, typename Comparator> auto sort(T list[], u32 size, const Co
|
|||
//insertion sort requires a copy (via move construction)
|
||||
#if defined(NALL_MERGE_SORT_INSERTION)
|
||||
for(s32 i = 1, j; i < size; i++) {
|
||||
T copy(move(list[i]));
|
||||
T copy(std::move(list[i]));
|
||||
for(j = i - 1; j >= 0; j--) {
|
||||
if(!lessthan(copy, list[j])) break;
|
||||
list[j + 1] = move(list[j]);
|
||||
list[j + 1] = std::move(list[j]);
|
||||
}
|
||||
list[j + 1] = move(copy);
|
||||
list[j + 1] = std::move(copy);
|
||||
}
|
||||
//selection sort requires a swap
|
||||
#elif defined(NALL_MERGE_SORT_SELECTION)
|
||||
|
@ -60,16 +60,16 @@ template<typename T, typename Comparator> auto sort(T list[], u32 size, const Co
|
|||
u32 offset = 0, left = 0, right = middle;
|
||||
while(left < middle && right < size) {
|
||||
if(!lessthan(list[right], list[left])) {
|
||||
new(buffer + offset++) T(move(list[left++]));
|
||||
new(buffer + offset++) T(std::move(list[left++]));
|
||||
} else {
|
||||
new(buffer + offset++) T(move(list[right++]));
|
||||
new(buffer + offset++) T(std::move(list[right++]));
|
||||
}
|
||||
}
|
||||
while(left < middle) new(buffer + offset++) T(move(list[left++]));
|
||||
while(right < size ) new(buffer + offset++) T(move(list[right++]));
|
||||
while(left < middle) new(buffer + offset++) T(std::move(list[left++]));
|
||||
while(right < size ) new(buffer + offset++) T(std::move(list[right++]));
|
||||
|
||||
for(u32 i = 0; i < size; i++) {
|
||||
list[i] = move(buffer[i]);
|
||||
list[i] = std::move(buffer[i]);
|
||||
buffer[i].~T();
|
||||
}
|
||||
memory::free(buffer);
|
||||
|
|
|
@ -83,7 +83,7 @@ template<typename T>
|
|||
struct queue {
|
||||
queue() = default;
|
||||
queue(const queue& source) { operator=(source); }
|
||||
queue(queue&& source) { operator=(move(source)); }
|
||||
queue(queue&& source) { operator=(std::move(source)); }
|
||||
~queue() { reset(); }
|
||||
|
||||
auto operator=(const queue& source) -> queue& {
|
||||
|
@ -100,6 +100,7 @@ struct queue {
|
|||
|
||||
auto operator=(queue&& source) -> queue& {
|
||||
if(this == &source) return *this;
|
||||
delete[] _data;
|
||||
_data = source._data;
|
||||
_capacity = source._capacity;
|
||||
_size = source._size;
|
||||
|
|
|
@ -7,7 +7,7 @@ struct emitter {
|
|||
template<typename... P>
|
||||
alwaysinline auto byte(u8 data, P&&... p) {
|
||||
span.write(data);
|
||||
byte(forward<P>(p)...);
|
||||
byte(std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
alwaysinline auto word(u16 data) {
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
flag_sle = SLJIT_SIG_LESS_EQUAL,
|
||||
flag_o = SLJIT_OVERFLOW,
|
||||
flag_no = SLJIT_NOT_OVERFLOW,
|
||||
flag_c = SLJIT_CARRY,
|
||||
flag_nc = SLJIT_NOT_CARRY,
|
||||
};
|
||||
|
||||
struct op_base {
|
||||
|
@ -53,4 +55,8 @@
|
|||
struct mem : public op_base {
|
||||
mem(sreg base, sljit_sw offset) : op_base(SLJIT_MEM1(base.fst), offset) {}
|
||||
};
|
||||
|
||||
struct unused {
|
||||
unused() {}
|
||||
};
|
||||
//};
|
||||
|
|
|
@ -44,6 +44,13 @@
|
|||
x.fst, x.snd, \
|
||||
y.fst, y.snd, \
|
||||
z.fst, z.snd); \
|
||||
} \
|
||||
template<typename U, typename V> \
|
||||
auto name(unused, U y, V z, sljit_s32 flags = 0) { \
|
||||
sljit_emit_op2u(compiler, \
|
||||
SLJIT_##op | flags, \
|
||||
y.fst, y.snd, \
|
||||
z.fst, z.snd); \
|
||||
}
|
||||
|
||||
OP2(add32, ADD32)
|
||||
|
@ -118,34 +125,6 @@
|
|||
|
||||
//meta instructions
|
||||
|
||||
auto mov32_to_c(mem m, int sign) {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
cmp32(imm(0), m, set_c);
|
||||
#elif defined(ARCHITECTURE_ARM64)
|
||||
if(sign < 0) {
|
||||
cmp32(imm(0), m, set_c);
|
||||
} else {
|
||||
cmp32(m, imm(1), set_c);
|
||||
}
|
||||
#else
|
||||
#error "Unimplemented architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
auto mov32_from_c(reg r, int sign) {
|
||||
#if defined(ARCHITECTURE_AMD64)
|
||||
mov32(r, imm(0));
|
||||
addc32(r, r, r);
|
||||
#elif defined(ARCHITECTURE_ARM64)
|
||||
mov32(r, imm(0));
|
||||
addc32(r, r, r);
|
||||
if(sign < 0) {
|
||||
xor32(r, r, imm(1));
|
||||
}
|
||||
#else
|
||||
#error "Unimplemented architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
auto lea(reg r, sreg base, sljit_sw offset) {
|
||||
add64(r, base, imm(offset));
|
||||
|
|
|
@ -8,9 +8,11 @@ namespace nall::recompiler {
|
|||
sljit_label* epilogue = nullptr;
|
||||
|
||||
generic(bump_allocator& alloc) : allocator(alloc) {}
|
||||
~generic() { /*resetCompiler();*/ }
|
||||
|
||||
auto beginFunction(int args) {
|
||||
auto beginFunction(int args) -> void {
|
||||
assert(args <= 3);
|
||||
resetCompiler();
|
||||
compiler = sljit_create_compiler(nullptr, &allocator);
|
||||
|
||||
sljit_s32 options = 0;
|
||||
|
@ -27,21 +29,25 @@ namespace nall::recompiler {
|
|||
|
||||
auto endFunction() -> u8* {
|
||||
u8* code = (u8*)sljit_generate_code(compiler);
|
||||
sljit_free_compiler(compiler);
|
||||
compiler = nullptr;
|
||||
epilogue = nullptr;
|
||||
resetCompiler();
|
||||
return code;
|
||||
}
|
||||
|
||||
auto testJumpEpilog() {
|
||||
auto resetCompiler() -> void {
|
||||
if(compiler) sljit_free_compiler(compiler);
|
||||
compiler = nullptr;
|
||||
epilogue = nullptr;
|
||||
}
|
||||
|
||||
auto testJumpEpilog() -> void {
|
||||
sljit_set_label(sljit_emit_cmp(compiler, SLJIT_NOT_EQUAL | SLJIT_32, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0), epilogue);
|
||||
}
|
||||
|
||||
auto jumpEpilog() {
|
||||
auto jumpEpilog() -> void {
|
||||
sljit_set_label(sljit_emit_jump(compiler, SLJIT_JUMP), epilogue);
|
||||
}
|
||||
|
||||
auto setLabel(sljit_jump* jump) {
|
||||
auto setLabel(sljit_jump* jump) -> void {
|
||||
sljit_set_label(jump, sljit_emit_label(compiler));
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ template<typename... P> inline auto execute(const string& name, P&&... p) -> exe
|
|||
if(pid == 0) {
|
||||
const char* argv[1 + sizeof...(p) + 1];
|
||||
const char** argp = argv;
|
||||
vector<string> argl(forward<P>(p)...);
|
||||
vector<string> argl(std::forward<P>(p)...);
|
||||
*argp++ = (const char*)name;
|
||||
for(auto& arg : argl) *argp++ = (const char*)arg;
|
||||
*argp++ = nullptr;
|
||||
|
@ -92,7 +92,7 @@ template<typename... P> inline auto invoke(const string& name, P&&... p) -> void
|
|||
if(pid == 0) {
|
||||
const char* argv[1 + sizeof...(p) + 1];
|
||||
const char** argp = argv;
|
||||
vector<string> argl(forward<P>(p)...);
|
||||
vector<string> argl(std::forward<P>(p)...);
|
||||
*argp++ = (const char*)name;
|
||||
for(auto& arg : argl) *argp++ = (const char*)arg;
|
||||
*argp++ = nullptr;
|
||||
|
@ -111,7 +111,7 @@ template<typename... P> inline auto invoke(const string& name, P&&... p) -> void
|
|||
#elif defined(PLATFORM_WINDOWS)
|
||||
|
||||
template<typename... P> inline auto execute(const string& name, P&&... p) -> execute_result_t {
|
||||
vector<string> argl(name, forward<P>(p)...);
|
||||
vector<string> argl(name, std::forward<P>(p)...);
|
||||
for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""};
|
||||
string arguments = argl.merge(" ");
|
||||
|
||||
|
@ -193,7 +193,7 @@ template<typename... P> inline auto execute(const string& name, P&&... p) -> exe
|
|||
}
|
||||
|
||||
template<typename... P> inline auto invoke(const string& name, P&&... p) -> void {
|
||||
vector<string> argl(forward<P>(p)...);
|
||||
vector<string> argl(std::forward<P>(p)...);
|
||||
for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""};
|
||||
string arguments = argl.merge(" ");
|
||||
string directory = Path::program().replace("/", "\\");
|
||||
|
|
|
@ -98,6 +98,7 @@ struct serializer {
|
|||
}
|
||||
|
||||
auto operator=(const serializer& s) -> serializer& {
|
||||
if(this == &s) return *this;
|
||||
if(_data) delete[] _data;
|
||||
|
||||
_mode = s._mode;
|
||||
|
@ -110,6 +111,7 @@ struct serializer {
|
|||
}
|
||||
|
||||
auto operator=(serializer&& s) -> serializer& {
|
||||
if(this == &s) return *this;
|
||||
if(_data) delete[] _data;
|
||||
|
||||
_mode = s._mode;
|
||||
|
@ -122,7 +124,7 @@ struct serializer {
|
|||
}
|
||||
|
||||
serializer(const serializer& s) { operator=(s); }
|
||||
serializer(serializer&& s) { operator=(move(s)); }
|
||||
serializer(serializer&& s) { operator=(std::move(s)); }
|
||||
|
||||
serializer() {
|
||||
setWriting();
|
||||
|
|
|
@ -30,7 +30,7 @@ template<typename T> struct set {
|
|||
|
||||
set() = default;
|
||||
set(const set& source) { operator=(source); }
|
||||
set(set&& source) { operator=(move(source)); }
|
||||
set(set&& source) { operator=(std::move(source)); }
|
||||
set(std::initializer_list<T> list) { for(auto& value : list) insert(value); }
|
||||
~set() { reset(); }
|
||||
|
||||
|
@ -44,6 +44,7 @@ template<typename T> struct set {
|
|||
|
||||
auto operator=(set&& source) -> set& {
|
||||
if(this == &source) return *this;
|
||||
reset();
|
||||
root = source.root;
|
||||
nodes = source.nodes;
|
||||
source.root = nullptr;
|
||||
|
@ -79,7 +80,7 @@ template<typename T> struct set {
|
|||
|
||||
template<typename... P> auto insert(const T& value, P&&... p) -> bool {
|
||||
bool result = insert(value);
|
||||
insert(forward<P>(p)...) | result;
|
||||
insert(std::forward<P>(p)...) | result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -93,7 +94,7 @@ template<typename T> struct set {
|
|||
|
||||
template<typename... P> auto remove(const T& value, P&&... p) -> bool {
|
||||
bool result = remove(value);
|
||||
return remove(forward<P>(p)...) | result;
|
||||
return remove(std::forward<P>(p)...) | result;
|
||||
}
|
||||
|
||||
struct base_iterator {
|
||||
|
|
|
@ -27,7 +27,7 @@ struct shared_pointer_this_base{};
|
|||
template<typename T>
|
||||
struct shared_pointer {
|
||||
template<typename... P> static auto create(P&&... p) {
|
||||
return shared_pointer<T>{new T{forward<P>(p)...}};
|
||||
return shared_pointer<T>{new T{std::forward<P>(p)...}};
|
||||
}
|
||||
|
||||
using type = T;
|
||||
|
@ -57,7 +57,7 @@ struct shared_pointer {
|
|||
}
|
||||
|
||||
shared_pointer(shared_pointer&& source) {
|
||||
operator=(move(source));
|
||||
operator=(std::move(source));
|
||||
}
|
||||
|
||||
template<typename U, typename = enable_if_t<is_compatible<U>::value>>
|
||||
|
@ -67,7 +67,7 @@ struct shared_pointer {
|
|||
|
||||
template<typename U, typename = enable_if_t<is_compatible<U>::value>>
|
||||
shared_pointer(shared_pointer<U>&& source) {
|
||||
operator=<U>(move(source));
|
||||
operator=<U>(std::move(source));
|
||||
}
|
||||
|
||||
template<typename U, typename = enable_if_t<is_compatible<U>::value>>
|
||||
|
@ -279,13 +279,13 @@ struct shared_pointer_this : shared_pointer_this_base {
|
|||
|
||||
template<typename T, typename... P>
|
||||
auto shared_pointer_make(P&&... p) -> shared_pointer<T> {
|
||||
return shared_pointer<T>{new T{forward<P>(p)...}};
|
||||
return shared_pointer<T>{new T{std::forward<P>(p)...}};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct shared_pointer_new : shared_pointer<T> {
|
||||
shared_pointer_new(const shared_pointer<T>& source) : shared_pointer<T>(source) {}
|
||||
template<typename... P> shared_pointer_new(P&&... p) : shared_pointer<T>(new T(forward<P>(p)...)) {}
|
||||
template<typename... P> shared_pointer_new(P&&... p) : shared_pointer<T>(new T(std::forward<P>(p)...)) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ public:
|
|||
string();
|
||||
string(string& source) : string() { operator=(source); }
|
||||
string(const string& source) : string() { operator=(source); }
|
||||
string(string&& source) : string() { operator=(move(source)); }
|
||||
string(string&& source) : string() { operator=(std::move(source)); }
|
||||
template<typename T = char> auto get() -> T*;
|
||||
template<typename T = char> auto data() const -> const T*;
|
||||
template<typename T = char> auto size() const -> u32 { return _size / sizeof(T); }
|
||||
|
@ -153,7 +153,7 @@ public:
|
|||
auto operator=(string&&) -> type&;
|
||||
|
||||
template<typename T, typename... P> string(T&& s, P&&... p) : string() {
|
||||
append(forward<T>(s), forward<P>(p)...);
|
||||
append(std::forward<T>(s), std::forward<P>(p)...);
|
||||
}
|
||||
~string() { reset(); }
|
||||
|
||||
|
@ -304,12 +304,12 @@ template<> struct vector<string> : vector_base<string> {
|
|||
|
||||
vector(const vector& source) { vector_base::operator=(source); }
|
||||
vector(vector& source) { vector_base::operator=(source); }
|
||||
vector(vector&& source) { vector_base::operator=(move(source)); }
|
||||
template<typename... P> vector(P&&... p) { append(forward<P>(p)...); }
|
||||
vector(vector&& source) { vector_base::operator=(std::move(source)); }
|
||||
template<typename... P> vector(P&&... p) { append(std::forward<P>(p)...); }
|
||||
|
||||
auto operator=(const vector& source) -> type& { return vector_base::operator=(source), *this; }
|
||||
auto operator=(vector& source) -> type& { return vector_base::operator=(source), *this; }
|
||||
auto operator=(vector&& source) -> type& { return vector_base::operator=(move(source)), *this; }
|
||||
auto operator=(vector&& source) -> type& { return vector_base::operator=(std::move(source)), *this; }
|
||||
|
||||
//vector.hpp
|
||||
template<typename... P> auto append(const string&, P&&...) -> type&;
|
||||
|
@ -329,7 +329,7 @@ template<> struct vector<string> : vector_base<string> {
|
|||
struct string_format : vector<string> {
|
||||
using type = string_format;
|
||||
|
||||
template<typename... P> string_format(P&&... p) { reserve(sizeof...(p)); append(forward<P>(p)...); }
|
||||
template<typename... P> string_format(P&&... p) { reserve(sizeof...(p)); append(std::forward<P>(p)...); }
|
||||
template<typename T, typename... P> auto append(const T&, P&&... p) -> type&;
|
||||
auto append() -> type&;
|
||||
};
|
||||
|
|
|
@ -183,16 +183,6 @@ template<u32 Bits> struct stringify<Real<Bits>> {
|
|||
//arrays
|
||||
|
||||
template<> struct stringify<vector<u8>> {
|
||||
stringify(vector<u8> source) {
|
||||
_text.resize(source.size());
|
||||
memory::copy(_text.data(), source.data(), source.size());
|
||||
}
|
||||
auto data() const -> const char* { return _text.data(); }
|
||||
auto size() const -> u32 { return _text.size(); }
|
||||
vector<char> _text;
|
||||
};
|
||||
|
||||
template<> struct stringify<const vector<u8>&> {
|
||||
stringify(const vector<u8>& source) {
|
||||
_text.resize(source.size());
|
||||
memory::copy(_text.data(), source.data(), source.size());
|
||||
|
@ -205,7 +195,7 @@ template<> struct stringify<const vector<u8>&> {
|
|||
//char arrays
|
||||
|
||||
template<> struct stringify<char*> {
|
||||
stringify(char* source) : _data(source ? source : "") {}
|
||||
stringify(const char* source) : _data(source ? source : "") {}
|
||||
auto data() const -> const char* { return _data; }
|
||||
auto size() const -> u32 { return strlen(_data); }
|
||||
const char* _data;
|
||||
|
@ -227,13 +217,6 @@ template<> struct stringify<string> {
|
|||
const string& _text;
|
||||
};
|
||||
|
||||
template<> struct stringify<const string&> {
|
||||
stringify(const string& source) : _text(source) {}
|
||||
auto data() const -> const char* { return _text.data(); }
|
||||
auto size() const -> u32 { return _text.size(); }
|
||||
const string& _text;
|
||||
};
|
||||
|
||||
template<> struct stringify<string_view> {
|
||||
stringify(const string_view& source) : _view(source) {}
|
||||
auto data() const -> const char* { return _view.data(); }
|
||||
|
@ -241,13 +224,6 @@ template<> struct stringify<string_view> {
|
|||
const string_view& _view;
|
||||
};
|
||||
|
||||
template<> struct stringify<const string_view&> {
|
||||
stringify(const string_view& source) : _view(source) {}
|
||||
auto data() const -> const char* { return _view.data(); }
|
||||
auto size() const -> u32 { return _view.size(); }
|
||||
const string_view& _view;
|
||||
};
|
||||
|
||||
template<> struct stringify<array_view<u8>> {
|
||||
stringify(const array_view<u8>& source) : _view(source) {}
|
||||
auto data() const -> const char* { return _view.data<const char>(); }
|
||||
|
@ -255,13 +231,6 @@ template<> struct stringify<array_view<u8>> {
|
|||
const array_view<u8>& _view;
|
||||
};
|
||||
|
||||
template<> struct stringify<const array_view<u8>&> {
|
||||
stringify(const array_view<u8>& source) : _view(source) {}
|
||||
auto data() const -> const char* { return _view.data<const char>(); }
|
||||
auto size() const -> u32 { return _view.size(); }
|
||||
const array_view<u8>& _view;
|
||||
};
|
||||
|
||||
template<> struct stringify<string_pascal> {
|
||||
stringify(const string_pascal& source) : _text(source) {}
|
||||
auto data() const -> const char* { return _text.data(); }
|
||||
|
@ -269,13 +238,6 @@ template<> struct stringify<string_pascal> {
|
|||
const string_pascal& _text;
|
||||
};
|
||||
|
||||
template<> struct stringify<const string_pascal&> {
|
||||
stringify(const string_pascal& source) : _text(source) {}
|
||||
auto data() const -> const char* { return _text.data(); }
|
||||
auto size() const -> u32 { return _text.size(); }
|
||||
const string_pascal& _text;
|
||||
};
|
||||
|
||||
//pointers
|
||||
|
||||
//note: T = char* is matched by stringify<string_view>
|
||||
|
@ -295,8 +257,8 @@ template<typename T> struct stringify<T*> {
|
|||
|
||||
//
|
||||
|
||||
template<typename T> inline auto make_string(T value) -> stringify<T> {
|
||||
return stringify<T>(forward<T>(value));
|
||||
template<typename T> inline auto make_string(const T& value) {
|
||||
return stringify<std::decay_t<T>>(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,16 +30,16 @@ inline auto string::operator()(u32 position, char fallback) const -> char {
|
|||
|
||||
template<typename... P> inline auto string::assign(P&&... p) -> string& {
|
||||
resize(0);
|
||||
return append(forward<P>(p)...);
|
||||
return append(std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
template<typename T, typename... P> inline auto string::prepend(const T& value, P&&... p) -> string& {
|
||||
if constexpr(sizeof...(p)) prepend(forward<P>(p)...);
|
||||
if constexpr(sizeof...(p)) prepend(std::forward<P>(p)...);
|
||||
return _prepend(make_string(value));
|
||||
}
|
||||
|
||||
template<typename... P> inline auto string::prepend(const nall::string_format& value, P&&... p) -> string& {
|
||||
if constexpr(sizeof...(p)) prepend(forward<P>(p)...);
|
||||
if constexpr(sizeof...(p)) prepend(std::forward<P>(p)...);
|
||||
return format(value);
|
||||
}
|
||||
|
||||
|
@ -52,13 +52,13 @@ template<typename T> inline auto string::_prepend(const stringify<T>& source) ->
|
|||
|
||||
template<typename T, typename... P> inline auto string::append(const T& value, P&&... p) -> string& {
|
||||
_append(make_string(value));
|
||||
if constexpr(sizeof...(p) > 0) append(forward<P>(p)...);
|
||||
if constexpr(sizeof...(p) > 0) append(std::forward<P>(p)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename... P> inline auto string::append(const nall::string_format& value, P&&... p) -> string& {
|
||||
format(value);
|
||||
if constexpr(sizeof...(p)) append(forward<P>(p)...);
|
||||
if constexpr(sizeof...(p)) append(std::forward<P>(p)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ inline auto string::format(const nall::string_format& params) -> type& {
|
|||
|
||||
template<typename T, typename... P> inline auto string_format::append(const T& value, P&&... p) -> string_format& {
|
||||
vector<string>::append(value);
|
||||
return append(forward<P>(p)...);
|
||||
return append(std::forward<P>(p)...);
|
||||
}
|
||||
|
||||
inline auto string_format::append() -> string_format& {
|
||||
|
@ -69,13 +69,13 @@ inline auto string_format::append() -> string_format& {
|
|||
}
|
||||
|
||||
template<typename... P> inline auto print(P&&... p) -> void {
|
||||
string s{forward<P>(p)...};
|
||||
string s{std::forward<P>(p)...};
|
||||
fwrite(s.data(), 1, s.size(), stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
template<typename... P> inline auto print(FILE* fp, P&&... p) -> void {
|
||||
string s{forward<P>(p)...};
|
||||
string s{std::forward<P>(p)...};
|
||||
fwrite(s.data(), 1, s.size(), fp);
|
||||
if(fp == stdout || fp == stderr) fflush(fp);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ struct string_pascal {
|
|||
}
|
||||
|
||||
string_pascal(const string_pascal& source) { operator=(source); }
|
||||
string_pascal(string_pascal&& source) { operator=(move(source)); }
|
||||
string_pascal(string_pascal&& source) { operator=(std::move(source)); }
|
||||
|
||||
~string_pascal() {
|
||||
if(_data) memory::free(_data);
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace nall {
|
|||
|
||||
template<typename... P> inline auto vector<string>::append(const string& data, P&&... p) -> type& {
|
||||
vector_base::append(data);
|
||||
append(forward<P>(p)...);
|
||||
append(std::forward<P>(p)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ inline string_view::string_view(const string& source) {
|
|||
|
||||
template<typename... P>
|
||||
inline string_view::string_view(P&&... p) {
|
||||
_string = new string{forward<P>(p)...};
|
||||
_string = new string{std::forward<P>(p)...};
|
||||
_data = _string->data();
|
||||
_size = _string->size();
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ inline string_view::~string_view() {
|
|||
|
||||
inline auto string_view::operator=(const string_view& source) -> type& {
|
||||
if(this == &source) return *this;
|
||||
if(_string) delete _string;
|
||||
_string = nullptr;
|
||||
_data = source._data;
|
||||
_size = source._size;
|
||||
|
@ -63,6 +64,7 @@ inline auto string_view::operator=(const string_view& source) -> type& {
|
|||
|
||||
inline auto string_view::operator=(string_view&& source) -> type& {
|
||||
if(this == &source) return *this;
|
||||
if(_string) delete _string;
|
||||
_string = source._string;
|
||||
_data = source._data;
|
||||
_size = source._size;
|
||||
|
|
|
@ -16,48 +16,48 @@ inline auto escapable() -> bool {
|
|||
namespace color {
|
||||
|
||||
template<typename... P> inline auto black(P&&... p) -> string {
|
||||
if(!escapable()) return string{forward<P>(p)...};
|
||||
return {"\e[30m", string{forward<P>(p)...}, "\e[0m"};
|
||||
if(!escapable()) return string{std::forward<P>(p)...};
|
||||
return {"\e[30m", string{std::forward<P>(p)...}, "\e[0m"};
|
||||
}
|
||||
|
||||
template<typename... P> inline auto blue(P&&... p) -> string {
|
||||
if(!escapable()) return string{forward<P>(p)...};
|
||||
return {"\e[94m", string{forward<P>(p)...}, "\e[0m"};
|
||||
if(!escapable()) return string{std::forward<P>(p)...};
|
||||
return {"\e[94m", string{std::forward<P>(p)...}, "\e[0m"};
|
||||
}
|
||||
|
||||
template<typename... P> inline auto green(P&&... p) -> string {
|
||||
if(!escapable()) return string{forward<P>(p)...};
|
||||
return {"\e[92m", string{forward<P>(p)...}, "\e[0m"};
|
||||
if(!escapable()) return string{std::forward<P>(p)...};
|
||||
return {"\e[92m", string{std::forward<P>(p)...}, "\e[0m"};
|
||||
}
|
||||
|
||||
template<typename... P> inline auto cyan(P&&... p) -> string {
|
||||
if(!escapable()) return string{forward<P>(p)...};
|
||||
return {"\e[96m", string{forward<P>(p)...}, "\e[0m"};
|
||||
if(!escapable()) return string{std::forward<P>(p)...};
|
||||
return {"\e[96m", string{std::forward<P>(p)...}, "\e[0m"};
|
||||
}
|
||||
|
||||
template<typename... P> inline auto red(P&&... p) -> string {
|
||||
if(!escapable()) return string{forward<P>(p)...};
|
||||
return {"\e[91m", string{forward<P>(p)...}, "\e[0m"};
|
||||
if(!escapable()) return string{std::forward<P>(p)...};
|
||||
return {"\e[91m", string{std::forward<P>(p)...}, "\e[0m"};
|
||||
}
|
||||
|
||||
template<typename... P> inline auto magenta(P&&... p) -> string {
|
||||
if(!escapable()) return string{forward<P>(p)...};
|
||||
return {"\e[95m", string{forward<P>(p)...}, "\e[0m"};
|
||||
if(!escapable()) return string{std::forward<P>(p)...};
|
||||
return {"\e[95m", string{std::forward<P>(p)...}, "\e[0m"};
|
||||
}
|
||||
|
||||
template<typename... P> inline auto yellow(P&&... p) -> string {
|
||||
if(!escapable()) return string{forward<P>(p)...};
|
||||
return {"\e[93m", string{forward<P>(p)...}, "\e[0m"};
|
||||
if(!escapable()) return string{std::forward<P>(p)...};
|
||||
return {"\e[93m", string{std::forward<P>(p)...}, "\e[0m"};
|
||||
}
|
||||
|
||||
template<typename... P> inline auto white(P&&... p) -> string {
|
||||
if(!escapable()) return string{forward<P>(p)...};
|
||||
return {"\e[97m", string{forward<P>(p)...}, "\e[0m"};
|
||||
if(!escapable()) return string{std::forward<P>(p)...};
|
||||
return {"\e[97m", string{std::forward<P>(p)...}, "\e[0m"};
|
||||
}
|
||||
|
||||
template<typename... P> inline auto gray(P&&... p) -> string {
|
||||
if(!escapable()) return string{forward<P>(p)...};
|
||||
return {"\e[37m", string{forward<P>(p)...}, "\e[0m"};
|
||||
if(!escapable()) return string{std::forward<P>(p)...};
|
||||
return {"\e[37m", string{std::forward<P>(p)...}, "\e[0m"};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue