merge latest ares, hook up its new N64DD support, make ares use AxisContraint (see #3453), some other cleanups here

This commit is contained in:
CasualPokePlayer 2022-11-27 05:44:00 -08:00
parent c23b063733
commit 9420c8b21c
119 changed files with 3801 additions and 1623 deletions

Binary file not shown.

View File

@ -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),

View File

@ -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());

View File

@ -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;

View File

@ -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)));

View File

@ -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;

View File

@ -89,6 +89,7 @@ namespace BizHawk.Client.EmuHawk
["ChannelF"] = "Channel F",
["VEC"] = "Vectrex",
["MSX"] = "MSX",
["N64DD"] = "N64 Disk Drive",
// ["PS2"] = "Sony PlayStation 2",
};

View File

@ -337,6 +337,7 @@ namespace BizHawk.Emulation.Common
case ".Z64":
case ".V64":
case ".N64":
case ".NDD":
game.System = VSystemID.Raw.N64;
break;

View File

@ -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"));

View File

@ -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);
}
}
}

View File

@ -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");

View File

@ -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);

View File

@ -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();

View File

@ -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 \

View File

@ -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
----------------------------------------------------------------------

View File

@ -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>;

View File

@ -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:

View File

@ -2,7 +2,7 @@
namespace ares::Memory {
#if defined(PLATFORM_MACOS) && defined(ARCHITECTURE_ARM64)
#if defined(PLATFORM_MACOS)
//stub for unsupported platforms
FixedAllocator::FixedAllocator() {
}

View File

@ -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);
}

View File

@ -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;
};

View File

@ -1,4 +1,5 @@
struct Input : Object {
DeclareClass(Input, "input")
using Object::Object;
u64 lastPoll;
};

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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;
};

View File

@ -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();

View File

@ -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;

View File

@ -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
}

View File

@ -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) {

View File

@ -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

View File

@ -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);
}
}

View File

@ -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)...};
}

View File

@ -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); }

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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",

View File

@ -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();
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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();

View File

@ -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 {

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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");
}

View File

@ -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)...};
}

View File

@ -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;

View File

@ -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
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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(); }

View File

@ -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
}
}

View File

@ -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]; }

View File

@ -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)...);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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
}
};
}

View File

@ -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) {}
};

View File

@ -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;

View File

@ -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) {

View File

@ -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() {

View File

@ -173,7 +173,7 @@ inline auto image::transform(bool outputEndian, u32 outputDepth, u64 outputAlpha
}
}
operator=(move(output));
operator=(std::move(output));
}
}

View File

@ -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 {

View File

@ -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)

View File

@ -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:

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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() {}
};
//};

View File

@ -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));

View File

@ -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));
}

View File

@ -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("/", "\\");

View File

@ -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();

View File

@ -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 {

View File

@ -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)...)) {}
};
}

View File

@ -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&;
};

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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