* ares64

* build, currently cherry picking c9af34027b to build this

* some work

* commit the frontend work. crashes on some NR_SCHED_GETAFFINITY syscall

* delete this

* fix oopsie, add debugging shit

* getting closer...

* what the fuck is this mame shit doing???

* fuck

* it's one of these isn't it

* fucking hell

* at least it's not crashing on init now

* bleh

* let's see if this works?

* audio i think?

* oh right, need to disable threading here since that's not wbx supported

* testing

* testing

* testing...

* testing

* wtf?

* lol

* it's doing something now i guess?

* let's try this

* maybe this will catch this bullshit?

* lol

* the fuck?

* what

* meh

* bleh

* let's have some fun

* bleh

* bleh

* bleh

* maybe this works better

* let's try this?

* meh

* this probably will break

* lol

* meh

* a

* a

* a

* a

* a

* meh

* a

* a

* b

* alloc invisible these video buffers

* a

* actually build

* bleh

* well uhhhhhhhh let's see if this blows up for fun i guess?

* bleh

* yeah that when as well as expected

* push this

* bleh

* bleh

* bleh

* a

* dirty hackery

* fuck space/tabs

* hook up input

* hook up input frontend side

* reset/power

* going to have to commit to dumb workaround here i guess?

* fuck this bullshit crash

* cic stuff

* oops

* testing

* hook up save detection

* memory domains and saveram shit

* oops

* someday i'll get macros right

* let's get this kinda reproducable

* build

* bleh

* frontend shit

* bleh

* testing

* a

* fucking

* a

* a

* a

* workaround C# bullshit marshalling maybe?

* revert that, let's hack it on c# side instead

* disgusting hack

* fuck c#

* fix oopsies for eeprom/flash and fuck c#

* apparently explicit layout just dont work, thanks c#!

* correct orientation

* actually fix orientation

* testing

* bleh

* b

* a

* fix crash here

* rumble getter

* hook up rumble with frontend, cache readonly controller settings on core init

* remove old experiment

* make clean make install
This commit is contained in:
CasualPokePlayer 2022-02-11 18:22:16 -08:00 committed by GitHub
parent 86e939b6d4
commit 682111f377
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
541 changed files with 117173 additions and 8 deletions

BIN
Assets/dll/ares64.wbx.gz Normal file

Binary file not shown.

View File

@ -26,6 +26,8 @@ namespace BizHawk.Client.Common
new[] { CoreNames.QuickNes, CoreNames.NesHawk, CoreNames.SubNesHawk }),
(new[] { VSystemID.Raw.SNES },
new[] { CoreNames.Faust, CoreNames.Snes9X, CoreNames.Bsnes, CoreNames.Bsnes115 }),
(new[] { VSystemID.Raw.N64 },
new[] { CoreNames.Mupen64Plus, CoreNames.Ares64, }),
(new[] { VSystemID.Raw.SGB },
new[] { CoreNames.Gambatte, CoreNames.Bsnes, CoreNames.Bsnes115}),
(new[] { VSystemID.Raw.GB, VSystemID.Raw.GBC },
@ -316,6 +318,7 @@ namespace BizHawk.Client.Common
{
[VSystemID.Raw.NES] = CoreNames.QuickNes,
[VSystemID.Raw.SNES] = CoreNames.Snes9X,
[VSystemID.Raw.N64] = CoreNames.Mupen64Plus,
[VSystemID.Raw.GB] = CoreNames.Gambatte,
[VSystemID.Raw.GBC] = CoreNames.Gambatte,
[VSystemID.Raw.GBL] = CoreNames.GambatteLink,

View File

@ -1980,7 +1980,7 @@ namespace BizHawk.Client.EmuHawk
case VSystemID.Raw.INTV:
IntvSubMenu.Visible = true;
break;
case VSystemID.Raw.N64:
case VSystemID.Raw.N64 when Emulator is N64:
N64SubMenu.Visible = true;
break;
case VSystemID.Raw.NES:

View File

@ -49,6 +49,7 @@
<Compile Update="Consoles/Nintendo/GBHawkLink/GBHawkLink.*.cs" DependentUpon="GBHawkLink.cs" />
<Compile Update="Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.*.cs" DependentUpon="GBHawkLink3x.cs" />
<Compile Update="Consoles/Nintendo/GBHawkLink4x/GBHawkLink4x.*.cs" DependentUpon="GBHawkLink4x.cs" />
<Compile Update="Consoles/Nintendo/Ares64/Ares64.*.cs" DependentUpon="Ares64.cs" />
<Compile Update="Consoles/Nintendo/N64/N64.*.cs" DependentUpon="N64.cs" />
<Compile Update="Consoles/Nintendo/NES/Boards/AxROM.cs" SubType="Code" />
<Compile Update="Consoles/Nintendo/NES/Boards/CPROM.cs" SubType="Code" />

View File

@ -0,0 +1,54 @@
using System.ComponentModel;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
{
public partial class Ares64 : ISettable<object, Ares64.Ares64SyncSettings>
{
private Ares64SyncSettings _syncSettings;
public object GetSettings() => null;
public Ares64SyncSettings GetSyncSettings() => _syncSettings.Clone();
public PutSettingsDirtyBits PutSettings(object o) => PutSettingsDirtyBits.None;
public PutSettingsDirtyBits PutSyncSettings(Ares64SyncSettings o)
{
var ret = Ares64SyncSettings.NeedsReboot(_syncSettings, o);
_syncSettings = o;
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
}
public class Ares64SyncSettings
{
[DisplayName("Player 1 Controller")]
[Description("")]
[DefaultValue(LibAres64.ControllerType.Mempak)]
public LibAres64.ControllerType P1Controller { get; set; }
[DisplayName("Player 2 Controller")]
[Description("")]
[DefaultValue(LibAres64.ControllerType.Unplugged)]
public LibAres64.ControllerType P2Controller { get; set; }
[DisplayName("Player 3 Controller")]
[Description("")]
[DefaultValue(LibAres64.ControllerType.Unplugged)]
public LibAres64.ControllerType P3Controller { get; set; }
[DisplayName("Player 4 Controller")]
[Description("")]
[DefaultValue(LibAres64.ControllerType.Unplugged)]
public LibAres64.ControllerType P4Controller { get; set; }
public Ares64SyncSettings() => SettingsUtil.SetDefaultValues(this);
public Ares64SyncSettings Clone() => MemberwiseClone() as Ares64SyncSettings;
public static bool NeedsReboot(Ares64SyncSettings x, Ares64SyncSettings y) => !DeepEquality.DeepEquals(x, y);
}
}
}

View File

@ -0,0 +1,210 @@
using System;
using System.IO;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Properties;
using BizHawk.Emulation.Cores.Waterbox;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
{
[PortedCore(CoreNames.Ares64, "ares team, Near", "v126", "https://ares-emulator.github.io/", isReleased: false)]
[ServiceNotApplicable(new[] { typeof(IDriveLight), })]
public partial class Ares64 : WaterboxCore, IRegionable
{
private readonly LibAres64 _core;
[CoreConstructor(VSystemID.Raw.N64)]
public Ares64(CoreLoadParameters<object, Ares64SyncSettings> lp)
: base(lp.Comm, new Configuration
{
DefaultWidth = 640,
DefaultHeight = 480,
MaxWidth = 640,
MaxHeight = 576,
MaxSamples = 2048,
DefaultFpsNumerator = 60000,
DefaultFpsDenominator = 1001,
SystemId = VSystemID.Raw.N64,
})
{
_syncSettings = lp.SyncSettings ?? new();
ControllerSettings = new[]
{
_syncSettings.P1Controller,
_syncSettings.P2Controller,
_syncSettings.P3Controller,
_syncSettings.P4Controller,
};
N64Controller = CreateControllerDefinition(ControllerSettings);
_core = PreInit<LibAres64>(new WaterboxOptions
{
Filename = "ares64.wbx",
SbrkHeapSizeKB = 2 * 1024,
SealedHeapSizeKB = 4,
InvisibleHeapSizeKB = 6 * 1024,
PlainHeapSizeKB = 4,
MmapHeapSizeKB = 512 * 1024,
SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
});
var rom = lp.Roms[0].RomData;
Region = rom[0x3E] switch
{
0x44 or 0x46 or 0x49 or 0x50 or 0x53 or 0x55 or 0x58 or 0x59 => DisplayType.PAL,
_ => DisplayType.NTSC,
};
var pal = Region == DisplayType.PAL;
if (pal)
{
VsyncNumerator = 50;
VsyncDenominator = 1;
}
var pif = Util.DecompressGzipFile(new MemoryStream(pal ? Resources.PIF_PAL_ROM.Value : Resources.PIF_NTSC_ROM.Value));
_exe.AddReadonlyFile(pif, pal ? "pif.pal.rom" : "pif.ntsc.rom");
_exe.AddReadonlyFile(rom, "program.rom");
if (!_core.Init(ControllerSettings, pal))
{
throw new InvalidOperationException("Init returned false!");
}
_exe.RemoveReadonlyFile(pal ? "pif.pal.rom" : "pif.ntsc.rom");
_exe.RemoveReadonlyFile("program.rom");
PostInit();
DeterministicEmulation = true;
}
public DisplayType Region { get; }
public override ControllerDefinition ControllerDefinition => N64Controller;
private ControllerDefinition N64Controller { get; }
public LibAres64.ControllerType[] ControllerSettings { get; }
private static ControllerDefinition CreateControllerDefinition(LibAres64.ControllerType[] controllerSettings)
{
var ret = new ControllerDefinition("Nintendo 64 Controller");
for (int i = 0; i < 4; i++)
{
if (controllerSettings[i] != LibAres64.ControllerType.Unplugged)
{
ret.BoolButtons.Add($"P{i + 1} DPad U");
ret.BoolButtons.Add($"P{i + 1} DPad D");
ret.BoolButtons.Add($"P{i + 1} DPad L");
ret.BoolButtons.Add($"P{i + 1} DPad R");
ret.BoolButtons.Add($"P{i + 1} Start");
ret.BoolButtons.Add($"P{i + 1} Z");
ret.BoolButtons.Add($"P{i + 1} B");
ret.BoolButtons.Add($"P{i + 1} A");
ret.BoolButtons.Add($"P{i + 1} C Up");
ret.BoolButtons.Add($"P{i + 1} C Down");
ret.BoolButtons.Add($"P{i + 1} C Left");
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.RightAndDown, (-32768).RangeTo(32767), 0);
if (controllerSettings[i] == LibAres64.ControllerType.Rumblepak)
{
ret.HapticsChannels.Add($"P{i + 1} Rumble Pak");
}
}
}
ret.BoolButtons.Add("Reset");
ret.BoolButtons.Add("Power");
return ret.MakeImmutable();
}
private static LibAres64.Buttons GetButtons(IController controller, int num)
{
LibAres64.Buttons ret = 0;
if (controller.IsPressed($"P{num} DPad U"))
ret |= LibAres64.Buttons.UP;
if (controller.IsPressed($"P{num} DPad D"))
ret |= LibAres64.Buttons.DOWN;
if (controller.IsPressed($"P{num} DPad L"))
ret |= LibAres64.Buttons.LEFT;
if (controller.IsPressed($"P{num} DPad R"))
ret |= LibAres64.Buttons.RIGHT;
if (controller.IsPressed($"P{num} B"))
ret |= LibAres64.Buttons.B;
if (controller.IsPressed($"P{num} A"))
ret |= LibAres64.Buttons.A;
if (controller.IsPressed($"P{num} C Up"))
ret |= LibAres64.Buttons.C_UP;
if (controller.IsPressed($"P{num} C Down"))
ret |= LibAres64.Buttons.C_DOWN;
if (controller.IsPressed($"P{num} C Left"))
ret |= LibAres64.Buttons.C_LEFT;
if (controller.IsPressed($"P{num} C Right"))
ret |= LibAres64.Buttons.C_RIGHT;
if (controller.IsPressed($"P{num} L"))
ret |= LibAres64.Buttons.L;
if (controller.IsPressed($"P{num} R"))
ret |= LibAres64.Buttons.R;
if (controller.IsPressed($"P{num} Z"))
ret |= LibAres64.Buttons.Z;
if (controller.IsPressed($"P{num} Start"))
ret |= LibAres64.Buttons.START;
return ret;
}
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
{
for (int i = 0; i < 4; i++)
{
if (ControllerSettings[i] == LibAres64.ControllerType.Rumblepak)
{
controller.SetHapticChannelStrength($"P{i + 1} Rumble Pak", _core.GetRumbleStatus(i) ? int.MaxValue : 0);
}
}
return new LibAres64.FrameInfo
{
P1Buttons = GetButtons(controller, 1),
P1XAxis = (short)controller.AxisValue("P1 X Axis"),
P1YAxis = (short)controller.AxisValue("P1 Y Axis"),
P2Buttons = GetButtons(controller, 2),
P2XAxis = (short)controller.AxisValue("P2 X Axis"),
P2YAxis = (short)controller.AxisValue("P2 Y Axis"),
P3Buttons = GetButtons(controller, 3),
P3XAxis = (short)controller.AxisValue("P3 X Axis"),
P3YAxis = (short)controller.AxisValue("P3 Y Axis"),
P4Buttons = GetButtons(controller, 4),
P4XAxis = (short)controller.AxisValue("P4 X Axis"),
P4YAxis = (short)controller.AxisValue("P4 Y Axis"),
Reset = controller.IsPressed("Reset"),
Power = controller.IsPressed("Power"),
};
}
protected override void FrameAdvancePost()
{
if (BufferWidth == 0)
{
BufferWidth = BufferHeight == 239 ? 320 : 640;
}
}
public override int VirtualWidth => 640;
public override int VirtualHeight => 480;
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Runtime.InteropServices;
using BizHawk.BizInvoke;
using BizHawk.Emulation.Cores.Waterbox;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64
{
public abstract class LibAres64 : LibWaterboxCore
{
[Flags]
public enum Buttons : uint
{
UP = 1 << 0,
DOWN = 1 << 1,
LEFT = 1 << 2,
RIGHT = 1 << 3,
B = 1 << 4,
A = 1 << 5,
C_UP = 1 << 6,
C_DOWN = 1 << 7,
C_LEFT = 1 << 8,
C_RIGHT = 1 << 9,
L = 1 << 10,
R = 1 << 11,
Z = 1 << 12,
START = 1 << 13,
}
public enum ControllerType : uint
{
Unplugged,
Standard,
Mempak,
Rumblepak,
}
[StructLayout(LayoutKind.Sequential)]
public new class FrameInfo : LibWaterboxCore.FrameInfo
{
public Buttons P1Buttons;
public short P1XAxis;
public short P1YAxis;
public Buttons P2Buttons;
public short P2XAxis;
public short P2YAxis;
public Buttons P3Buttons;
public short P3XAxis;
public short P3YAxis;
public Buttons P4Buttons;
public short P4XAxis;
public short P4YAxis;
public bool Reset;
public bool Power;
}
[BizImport(CC)]
public abstract bool Init(ControllerType[] controllerSettings, bool pal);
[BizImport(CC)]
public abstract bool GetRumbleStatus(int num);
}
}

View File

@ -10,6 +10,7 @@ namespace BizHawk.Emulation.Cores
public static class CoreNames
{
public const string A7800Hawk = "A7800Hawk";
public const string Ares64 = "Ares64";
public const string Atari2600Hawk = "Atari2600Hawk";
public const string Bsnes = "BSNES";
public const string Bsnes115 = "BSNESv115+";

View File

@ -24,5 +24,7 @@ namespace BizHawk.Emulation.Cores.Properties {
internal static readonly Lazy<byte[]> ZX_plus2_rom = new Lazy<byte[]>(() => ReadEmbeddedByteArray("plus2.rom.gz"));
internal static readonly Lazy<byte[]> ZX_plus2a_rom = new Lazy<byte[]>(() => ReadEmbeddedByteArray("plus2a.rom.gz"));
internal static readonly Lazy<byte[]> TMDS = new Lazy<byte[]>(() => ReadEmbeddedByteArray("tmds.zip.gz"));
internal static readonly Lazy<byte[]> PIF_PAL_ROM = new Lazy<byte[]>(() => ReadEmbeddedByteArray("pif.pal.rom.gz"));
internal static readonly Lazy<byte[]> PIF_NTSC_ROM = new Lazy<byte[]>(() => ReadEmbeddedByteArray("pif.ntsc.rom.gz"));
}
}

Binary file not shown.

Binary file not shown.

View File

@ -4,6 +4,7 @@ using System.Drawing;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Consoles.Nintendo.Ares64;
using BizHawk.Emulation.Cores.Nintendo.N64;
namespace BizHawk.Emulation.Cores
@ -14,17 +15,33 @@ namespace BizHawk.Emulation.Cores
{
public IEnumerable<PadSchema> GetPadSchemas(IEmulator core, Action<string> showMessageBox)
{
var ss = ((N64)core).GetSyncSettings();
for (var i = 0; i < 4; i++)
if (core is N64 n64)
{
if (ss.Controllers[i].IsConnected)
var ss = n64.GetSyncSettings();
for (var i = 0; i < 4; i++)
{
yield return StandardController(i + 1);
if (ss.Controllers[i].IsConnected)
{
yield return StandardController(i + 1, MupenRange);
}
}
}
else if (core is Ares64 ares64)
{
for (var i = 0; i < 4; i++)
{
if (ares64.ControllerSettings[i] != LibAres64.ControllerType.Unplugged)
{
yield return StandardController(i + 1, AresRange);
}
}
}
}
private static PadSchema StandardController(int controller)
private static readonly Func<bool, AxisSpec> MupenRange = (bool isY) => new((-128).RangeTo(127), 0, false);
private static readonly Func<bool, AxisSpec> AresRange = (bool isY) => new((-32768).RangeTo(32767), 0, isY);
private static PadSchema StandardController(int controller, Func<bool, AxisSpec> makeRange)
{
return new PadSchema
{
@ -47,8 +64,8 @@ namespace BizHawk.Emulation.Cores
new ButtonSchema(194, 221, controller, "C Right") { Icon = VGamepadButtonImage.YellowArrE },
new AnalogSchema(6, 14, $"P{controller} X Axis")
{
Spec = new AxisSpec((-128).RangeTo(127), 0),
SecondarySpec = new AxisSpec((-128).RangeTo(127), 0)
Spec = makeRange(false),
SecondarySpec = makeRange(true)
}
}
};

View File

@ -0,0 +1,182 @@
From c9af34027bc9cb852a4e5e96154a7bd89531a6de Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper@intel.com>
Date: Wed, 16 Sep 2020 21:56:01 -0700
Subject: [PATCH] Add __divmodti4 to match libgcc.
gcc has used this on x86-64 since at least version 7.
Reviewed By: MaskRay
Differential Revision: https://reviews.llvm.org/D80506
---
compiler-rt/lib/builtins/CMakeLists.txt | 1 +
compiler-rt/lib/builtins/README.txt | 2 +
compiler-rt/lib/builtins/divmodti4.c | 32 +++++++
.../test/builtins/Unit/divmodti4_test.c | 91 +++++++++++++++++++
4 files changed, 126 insertions(+)
create mode 100644 compiler-rt/lib/builtins/divmodti4.c
create mode 100644 compiler-rt/test/builtins/Unit/divmodti4_test.c
diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt
index 8dbe15364ab8..3c50df179764 100644
--- a/compiler-rt/lib/builtins/CMakeLists.txt
+++ b/compiler-rt/lib/builtins/CMakeLists.txt
@@ -71,6 +71,7 @@ set(GENERIC_SOURCES
divdi3.c
divmoddi4.c
divmodsi4.c
+ divmodti4.c
divsc3.c
divsf3.c
divsi3.c
diff --git a/compiler-rt/lib/builtins/README.txt b/compiler-rt/lib/builtins/README.txt
index f9e1bc805092..d66d725e7ab5 100644
--- a/compiler-rt/lib/builtins/README.txt
+++ b/compiler-rt/lib/builtins/README.txt
@@ -87,6 +87,8 @@ du_int __udivmoddi4(du_int a, du_int b, du_int* rem); // a / b, *rem = a % b u
tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem); // a / b, *rem = a % b unsigned
su_int __udivmodsi4(su_int a, su_int b, su_int* rem); // a / b, *rem = a % b unsigned
si_int __divmodsi4(si_int a, si_int b, si_int* rem); // a / b, *rem = a % b signed
+di_int __divmoddi4(di_int a, di_int b, di_int* rem); // a / b, *rem = a % b signed
+ti_int __divmodti4(ti_int a, ti_int b, ti_int* rem); // a / b, *rem = a % b signed
diff --git a/compiler-rt/lib/builtins/divmodti4.c b/compiler-rt/lib/builtins/divmodti4.c
new file mode 100644
index 000000000000..b243ba4ef853
--- /dev/null
+++ b/compiler-rt/lib/builtins/divmodti4.c
@@ -0,0 +1,32 @@
+//===-- divmodti4.c - Implement __divmodti4 -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements __divmodti4 for the compiler_rt library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "int_lib.h"
+
+#ifdef CRT_HAS_128BIT
+
+// Returns: a / b, *rem = a % b
+
+COMPILER_RT_ABI ti_int __divmodti4(ti_int a, ti_int b, ti_int *rem) {
+ const int bits_in_tword_m1 = (int)(sizeof(ti_int) * CHAR_BIT) - 1;
+ ti_int s_a = a >> bits_in_tword_m1; // s_a = a < 0 ? -1 : 0
+ ti_int s_b = b >> bits_in_tword_m1; // s_b = b < 0 ? -1 : 0
+ a = (a ^ s_a) - s_a; // negate if s_a == -1
+ b = (b ^ s_b) - s_b; // negate if s_b == -1
+ s_b ^= s_a; // sign of quotient
+ tu_int r;
+ ti_int q = (__udivmodti4(a, b, &r) ^ s_b) - s_b; // negate if s_b == -1
+ *rem = (r ^ s_a) - s_a; // negate if s_a == -1
+ return q;
+}
+
+#endif // CRT_HAS_128BIT
diff --git a/compiler-rt/test/builtins/Unit/divmodti4_test.c b/compiler-rt/test/builtins/Unit/divmodti4_test.c
new file mode 100644
index 000000000000..a9f70dcf1c1e
--- /dev/null
+++ b/compiler-rt/test/builtins/Unit/divmodti4_test.c
@@ -0,0 +1,91 @@
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: librt_has_divmodti4
+// REQUIRES: int128
+//===-- divmodti4_test.c - Test __divmodti4 -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file tests __divmodti4 for the compiler_rt library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "int_lib.h"
+#include <stdio.h>
+
+#ifdef CRT_HAS_128BIT
+
+// Effects: if rem != 0, *rem = a % b
+// Returns: a / b
+
+COMPILER_RT_ABI ti_int __divmodti4(ti_int a, ti_int b, ti_int* rem);
+
+int test__divmodti4(ti_int a, ti_int b, ti_int expected_q, ti_int expected_r) {
+ ti_int r;
+ ti_int q = __divmodti4(a, b, &r);
+ if (q != expected_q || r != expected_r)
+ {
+ utwords at;
+ at.all = a;
+ utwords bt;
+ bt.all = b;
+ utwords expected_qt;
+ expected_qt.all = expected_q;
+ utwords expected_rt;
+ expected_rt.all = expected_r;
+ utwords qt;
+ qt.all = q;
+ utwords rt;
+ rt.all = r;
+ printf("error in __divmodti4: 0x%.16llX%.16llX / 0x%.16llX%.16llX = "
+ "0x%.16llX%.16llX, R = 0x%.16llX%.16llX, expected 0x%.16llX%.16llX, "
+ "0x%.16llX%.16llX\n",
+ at.s.high, at.s.low, bt.s.high, bt.s.low, qt.s.high, qt.s.low,
+ rt.s.high, rt.s.low, expected_qt.s.high, expected_qt.s.low,
+ expected_rt.s.high, expected_rt.s.low);
+ }
+ return !(q == expected_q && r == expected_r);
+}
+
+char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0};
+
+tu_int tests[][4] =
+{
+{ (ti_int) 0, (ti_int) 1, (ti_int) 0, (ti_int) 0 },
+{ (ti_int) 0, (ti_int)-1, (ti_int) 0, (ti_int) 0 },
+{ (ti_int) 2, (ti_int) 1, (ti_int) 2, (ti_int) 0 },
+{ (ti_int) 2, (ti_int)-1, (ti_int)-2, (ti_int) 0 },
+{ (ti_int)-2, (ti_int) 1, (ti_int)-2, (ti_int) 0 },
+{ (ti_int)-2, (ti_int)-1, (ti_int) 2, (ti_int) 0 },
+{ (ti_int) 5, (ti_int) 3, (ti_int) 1, (ti_int) 2 },
+{ (ti_int) 5, (ti_int)-3, (ti_int)-1, (ti_int) 2 },
+{ (ti_int)-5, (ti_int) 3, (ti_int)-1, (ti_int)-2 },
+{ (ti_int)-5, (ti_int)-3, (ti_int) 1, (ti_int)-2 },
+{ (ti_int)0x8000000000000000LL << 64 | 0, (ti_int) 1, (ti_int)0x8000000000000000LL << 64 | 0, (ti_int)0x0LL },
+{ (ti_int)0x8000000000000000LL << 64 | 0, (ti_int)-1, (ti_int)0x8000000000000000LL << 64 | 0, (ti_int)0x0LL },
+{ (ti_int)0x8000000000000000LL << 64 | 0, (ti_int)-2, (ti_int)0x4000000000000000LL << 64 | 0, (ti_int)0x0LL },
+{ (ti_int)0x8000000000000000LL << 64 | 0, (ti_int) 2, (ti_int)0xC000000000000000LL << 64 | 0, (ti_int)0x0LL },
+{ (ti_int)0x8000000000000000LL << 64 | 0, (ti_int)-3, (ti_int)0x2AAAAAAAAAAAAAAALL << 64 | 0xAAAAAAAAAAAAAAAALL, (ti_int)-2 },
+{ (ti_int)0x8000000000000000LL << 64 | 0, (ti_int) 3, (ti_int)0xD555555555555555LL << 64 | 0x5555555555555556LL, (ti_int)-2 },
+};
+
+#endif
+
+int main()
+{
+#ifdef CRT_HAS_128BIT
+ const unsigned N = sizeof(tests) / sizeof(tests[0]);
+ unsigned i;
+ for (i = 0; i < N; ++i)
+ if (test__divmodti4(tests[i][0], tests[i][1], tests[i][2], tests[i][3]))
+ return 1;
+
+
+#else
+ printf("skipped\n");
+#endif
+ return 0;
+}
--
2.25.1

View File

@ -0,0 +1,578 @@
#include <n64/n64.hpp>
#include <emulibc.h>
#include <waterboxcore.h>
#define EXPORT extern "C" ECL_EXPORT
typedef enum
{
Unplugged,
Standard,
Mempak,
Rumblepak,
} ControllerType;
typedef enum
{
UP = 1 << 0,
DOWN = 1 << 1,
LEFT = 1 << 2,
RIGHT = 1 << 3,
B = 1 << 4,
A = 1 << 5,
C_UP = 1 << 6,
C_DOWN = 1 << 7,
C_LEFT = 1 << 8,
C_RIGHT = 1 << 9,
L = 1 << 10,
R = 1 << 11,
Z = 1 << 12,
START = 1 << 13,
} Buttons_t;
struct BizPlatform : ares::Platform
{
auto attach(ares::Node::Object) -> void override;
auto pak(ares::Node::Object) -> ares::VFS::Pak override;
auto video(ares::Node::Video::Screen, const u32*, u32, u32, u32) -> void override;
auto input(ares::Node::Input::Input) -> void override;
ares::VFS::Pak bizpak = new vfs::directory;
ares::Node::Audio::Stream stream = nullptr;
u32* videobuf = nullptr;
u32 pitch = 0;
u32 width = 0;
u32 height = 0;
bool newframe = false;
void (*inputcb)() = nullptr;
bool lagged = true;
};
auto BizPlatform::attach(ares::Node::Object node) -> void
{
if (auto stream = node->cast<ares::Node::Audio::Stream>())
{
stream->setResamplerFrequency(44100);
this->stream = stream;
}
}
auto BizPlatform::pak(ares::Node::Object) -> ares::VFS::Pak
{
return bizpak;
}
auto BizPlatform::video(ares::Node::Video::Screen screen, const u32* data, u32 pitch, u32 width, u32 height) -> void
{
videobuf = (u32*)data;
this->pitch = pitch >> 2;
this->width = width;
this->height = height;
newframe = true;
}
auto BizPlatform::input(ares::Node::Input::Input node) -> void
{
if (auto input = node->cast<ares::Node::Input::Button>())
{
if (input->name() == "Start")
{
lagged = false;
if (inputcb) inputcb();
}
}
};
static ares::Node::System root;
static BizPlatform platform;
static inline void HackeryDoo()
{
root->run();
root->run();
platform.newframe = false;
f64 buf[2];
while (platform.stream->pending()) platform.stream->read(buf);
}
typedef enum
{
NONE,
EEPROM512,
EEPROM2KB,
SRAM32KB,
SRAM96KB,
FLASH128KB,
} SaveType;
static inline SaveType DetectSaveType(u8* rom)
{
string id;
id.append((char)rom[0x3B]);
id.append((char)rom[0x3C]);
id.append((char)rom[0x3D]);
char region_code = rom[0x3E];
u8 revision = rom[0x3F];
SaveType ret = NONE;
if (id == "NTW") ret = EEPROM512;
if (id == "NHF") ret = EEPROM512;
if (id == "NOS") ret = EEPROM512;
if (id == "NTC") ret = EEPROM512;
if (id == "NER") ret = EEPROM512;
if (id == "NAG") ret = EEPROM512;
if (id == "NAB") ret = EEPROM512;
if (id == "NS3") ret = EEPROM512;
if (id == "NTN") ret = EEPROM512;
if (id == "NBN") ret = EEPROM512;
if (id == "NBK") ret = EEPROM512;
if (id == "NFH") ret = EEPROM512;
if (id == "NMU") ret = EEPROM512;
if (id == "NBC") ret = EEPROM512;
if (id == "NBH") ret = EEPROM512;
if (id == "NHA") ret = EEPROM512;
if (id == "NBM") ret = EEPROM512;
if (id == "NBV") ret = EEPROM512;
if (id == "NBD") ret = EEPROM512;
if (id == "NCT") ret = EEPROM512;
if (id == "NCH") ret = EEPROM512;
if (id == "NCG") ret = EEPROM512;
if (id == "NP2") ret = EEPROM512;
if (id == "NXO") ret = EEPROM512;
if (id == "NCU") ret = EEPROM512;
if (id == "NCX") ret = EEPROM512;
if (id == "NDY") ret = EEPROM512;
if (id == "NDQ") ret = EEPROM512;
if (id == "NDR") ret = EEPROM512;
if (id == "NN6") ret = EEPROM512;
if (id == "NDU") ret = EEPROM512;
if (id == "NJM") ret = EEPROM512;
if (id == "NFW") ret = EEPROM512;
if (id == "NF2") ret = EEPROM512;
if (id == "NKA") ret = EEPROM512;
if (id == "NFG") ret = EEPROM512;
if (id == "NGL") ret = EEPROM512;
if (id == "NGV") ret = EEPROM512;
if (id == "NGE") ret = EEPROM512;
if (id == "NHP") ret = EEPROM512;
if (id == "NPG") ret = EEPROM512;
if (id == "NIJ") ret = EEPROM512;
if (id == "NIC") ret = EEPROM512;
if (id == "NFY") ret = EEPROM512;
if (id == "NKI") ret = EEPROM512;
if (id == "NLL") ret = EEPROM512;
if (id == "NLR") ret = EEPROM512;
if (id == "NKT") ret = EEPROM512;
if (id == "CLB") ret = EEPROM512;
if (id == "NLB") ret = EEPROM512;
if (id == "NMW") ret = EEPROM512;
if (id == "NML") ret = EEPROM512;
if (id == "NTM") ret = EEPROM512;
if (id == "NMI") ret = EEPROM512;
if (id == "NMG") ret = EEPROM512;
if (id == "NMO") ret = EEPROM512;
if (id == "NMS") ret = EEPROM512;
if (id == "NMR") ret = EEPROM512;
if (id == "NCR") ret = EEPROM512;
if (id == "NEA") ret = EEPROM512;
if (id == "NPW") ret = EEPROM512;
if (id == "NPM") ret = EEPROM512;
if (id == "NPY") ret = EEPROM512;
if (id == "NPT") ret = EEPROM512;
if (id == "NRA") ret = EEPROM512;
if (id == "NWQ") ret = EEPROM512;
if (id == "NSU") ret = EEPROM512;
if (id == "NSN") ret = EEPROM512;
if (id == "NK2") ret = EEPROM512;
if (id == "NSV") ret = EEPROM512;
if (id == "NFX") ret = EEPROM512;
if (id == "NFP") ret = EEPROM512;
if (id == "NS6") ret = EEPROM512;
if (id == "NNA") ret = EEPROM512;
if (id == "NRS") ret = EEPROM512;
if (id == "NSW") ret = EEPROM512;
if (id == "NSC") ret = EEPROM512;
if (id == "NSA") ret = EEPROM512;
if (id == "NB6") ret = EEPROM512;
if (id == "NSM") ret = EEPROM512;
if (id == "NSS") ret = EEPROM512;
if (id == "NTX") ret = EEPROM512;
if (id == "NT6") ret = EEPROM512;
if (id == "NTP") ret = EEPROM512;
if (id == "NTJ") ret = EEPROM512;
if (id == "NRC") ret = EEPROM512;
if (id == "NTR") ret = EEPROM512;
if (id == "NTB") ret = EEPROM512;
if (id == "NGU") ret = EEPROM512;
if (id == "NIR") ret = EEPROM512;
if (id == "NVL") ret = EEPROM512;
if (id == "NVY") ret = EEPROM512;
if (id == "NWR") ret = EEPROM512;
if (id == "NWC") ret = EEPROM512;
if (id == "NAD") ret = EEPROM512;
if (id == "NWU") ret = EEPROM512;
if (id == "NYK") ret = EEPROM512;
if (id == "NMZ") ret = EEPROM512;
if (id == "NDK" && region_code == 'J') ret = EEPROM512;
if (id == "NWT" && region_code == 'J') ret = EEPROM512;
if (id == "NB7") ret = EEPROM2KB;
if (id == "NGT") ret = EEPROM2KB;
if (id == "NFU") ret = EEPROM2KB;
if (id == "NCW") ret = EEPROM2KB;
if (id == "NCZ") ret = EEPROM2KB;
if (id == "ND6") ret = EEPROM2KB;
if (id == "NDO") ret = EEPROM2KB;
if (id == "ND2") ret = EEPROM2KB;
if (id == "N3D") ret = EEPROM2KB;
if (id == "NMX") ret = EEPROM2KB;
if (id == "NGC") ret = EEPROM2KB;
if (id == "NIM") ret = EEPROM2KB;
if (id == "NK4") ret = EEPROM2KB;
if (id == "NNB") ret = EEPROM2KB;
if (id == "NMV") ret = EEPROM2KB;
if (id == "NM8") ret = EEPROM2KB;
if (id == "NEV") ret = EEPROM2KB;
if (id == "NPP") ret = EEPROM2KB;
if (id == "NUB") ret = EEPROM2KB;
if (id == "NPD") ret = EEPROM2KB;
if (id == "NRZ") ret = EEPROM2KB;
if (id == "NR7") ret = EEPROM2KB;
if (id == "NEP") ret = EEPROM2KB;
if (id == "NYS") ret = EEPROM2KB;
if (id == "ND3" && region_code == 'J') ret = EEPROM2KB;
if (id == "ND4" && region_code == 'J') ret = EEPROM2KB;
if (id == "NTE") ret = SRAM32KB;
if (id == "NVB") ret = SRAM32KB;
if (id == "CFZ") ret = SRAM32KB;
if (id == "NFZ") ret = SRAM32KB;
if (id == "NSI") ret = SRAM32KB;
if (id == "NG6") ret = SRAM32KB;
if (id == "N3H") ret = SRAM32KB;
if (id == "NGP") ret = SRAM32KB;
if (id == "NYW") ret = SRAM32KB;
if (id == "NHY") ret = SRAM32KB;
if (id == "NIB") ret = SRAM32KB;
if (id == "NPS") ret = SRAM32KB;
if (id == "NPA") ret = SRAM32KB;
if (id == "NP4") ret = SRAM32KB;
if (id == "NJ5") ret = SRAM32KB;
if (id == "NP6") ret = SRAM32KB;
if (id == "NPE") ret = SRAM32KB;
if (id == "NJG") ret = SRAM32KB;
if (id == "CZL") ret = SRAM32KB;
if (id == "NZL") ret = SRAM32KB;
if (id == "NKG") ret = SRAM32KB;
if (id == "NMF") ret = SRAM32KB;
if (id == "NRI") ret = SRAM32KB;
if (id == "NUT") ret = SRAM32KB;
if (id == "NUM") ret = SRAM32KB;
if (id == "NOB") ret = SRAM32KB;
if (id == "CPS") ret = SRAM32KB;
if (id == "NB5") ret = SRAM32KB;
if (id == "NRE") ret = SRAM32KB;
if (id == "NAL") ret = SRAM32KB;
if (id == "NT3") ret = SRAM32KB;
if (id == "NS4") ret = SRAM32KB;
if (id == "NA2") ret = SRAM32KB;
if (id == "NVP") ret = SRAM32KB;
if (id == "NWL") ret = SRAM32KB;
if (id == "NW2") ret = SRAM32KB;
if (id == "NWX") ret = SRAM32KB;
if (id == "NK4" && region_code == 'J' && revision < 2) ret = SRAM32KB;
if (id == "CDZ") ret = SRAM96KB;
if (id == "NCC") ret = FLASH128KB;
if (id == "NDA") ret = FLASH128KB;
if (id == "NAF") ret = FLASH128KB;
if (id == "NJF") ret = FLASH128KB;
if (id == "NKJ") ret = FLASH128KB;
if (id == "NZS") ret = FLASH128KB;
if (id == "NM6") ret = FLASH128KB;
if (id == "NCK") ret = FLASH128KB;
if (id == "NMQ") ret = FLASH128KB;
if (id == "NPN") ret = FLASH128KB;
if (id == "NPF") ret = FLASH128KB;
if (id == "NPO") ret = FLASH128KB;
if (id == "CP2") ret = FLASH128KB;
if (id == "NP3") ret = FLASH128KB;
if (id == "NRH") ret = FLASH128KB;
if (id == "NSQ") ret = FLASH128KB;
if (id == "NT9") ret = FLASH128KB;
if (id == "NW4") ret = FLASH128KB;
if (id == "NDP") ret = FLASH128KB;
return ret;
}
EXPORT bool Init(ControllerType* controllers, bool pal)
{
FILE* f;
array_view<u8>* data;
u32 len;
string name;
name = pal ? "pif.pal.rom" : "pif.ntsc.rom";
f = fopen(name, "rb");
fseek(f, 0, SEEK_END);
len = ftell(f);
data = new array_view<u8>(new u8[len], len);
fseek(f, 0, SEEK_SET);
fread((void*)data->data(), 1, len, f);
fclose(f);
platform.bizpak->append(name, *data);
name = "program.rom";
f = fopen(name, "rb");
fseek(f, 0, SEEK_END);
len = ftell(f);
data = new array_view<u8>(new u8[len], len);
fseek(f, 0, SEEK_SET);
fread((void*)data->data(), 1, len, f);
fclose(f);
platform.bizpak->append(name, *data);
string region = pal ? "PAL" : "NTSC";
platform.bizpak->setAttribute("region", region);
string cic = pal ? "CIC-NUS-7101" : "CIC-NUS-6102";
u32 crc32 = Hash::CRC32({&((u8*)data->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";
platform.bizpak->setAttribute("cic", cic);
SaveType save = DetectSaveType((u8*)data->data());
if (save != NONE)
{
switch (save)
{
case EEPROM512: len = 512; name = "save.eeprom"; break;
case EEPROM2KB: len = 2 * 1024; name = "save.eeprom"; break;
case SRAM32KB: len = 32 * 1024; name = "save.ram"; break;
case SRAM96KB: len = 96 * 1024; name = "save.ram"; break;
case FLASH128KB: len = 128 * 1024; name = "save.flash"; break;
default: return false;
}
data = new array_view<u8>(new u8[len], len);
memset((void*)data->data(), 0xFF, len);
platform.bizpak->append(name, *data);
}
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; i < 4; i++)
{
if (auto port = root->find<ares::Node::Port>({"Controller Port ", 1 + i}))
{
if (controllers[i] == Unplugged) continue;
auto peripheral = port->allocate("Gamepad");
port->connect();
string name;
switch (controllers[i])
{
case Mempak: name = "Controller Pak"; break;
case Rumblepak: name = "Rumble Pak"; break;
default: continue;
}
if (auto port = peripheral->find<ares::Node::Port>("Pak"))
{
port->allocate(name);
port->connect();
}
else
{
return false;
}
}
else
{
return false;
}
}
root->power(false);
HackeryDoo();
return true;
}
EXPORT bool GetRumbleStatus(u32 num)
{
ares::Nintendo64::Gamepad* c = nullptr;
switch (num)
{
case 0: c = (ares::Nintendo64::Gamepad*)ares::Nintendo64::controllerPort1.device.data(); break;
case 1: c = (ares::Nintendo64::Gamepad*)ares::Nintendo64::controllerPort2.device.data(); break;
case 2: c = (ares::Nintendo64::Gamepad*)ares::Nintendo64::controllerPort3.device.data(); break;
case 3: c = (ares::Nintendo64::Gamepad*)ares::Nintendo64::controllerPort4.device.data(); break;
}
return c ? c->motor->enable() : false;
}
#define MAYBE_ADD_MEMORY_DOMAIN(mem, name, flags) do { \
if (ares::Nintendo64::mem.data) \
{ \
m[i].Data = ares::Nintendo64::mem.data; \
m[i].Name = name; \
m[i].Size = ares::Nintendo64::mem.size; \
m[i].Flags = flags | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE; \
i++; \
} \
} while (0) \
#define MAYBE_ADD_MEMPAK_DOMAIN(num) do { \
if (auto c = (ares::Nintendo64::Gamepad*)ares::Nintendo64::controllerPort##num.device.data()) \
{ \
if (c->ram.data) \
{ \
m[i].Data = c->ram.data; \
m[i].Name = "MEMPAK " #num; \
m[i].Size = c->ram.size; \
m[i].Flags = MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_SAVERAMMABLE | MEMORYAREA_FLAGS_YUGEENDIAN | MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE; \
i++; \
} \
} \
} while (0) \
EXPORT void GetMemoryAreas(MemoryArea *m)
{
int i = 0;
MAYBE_ADD_MEMORY_DOMAIN(rdram.ram, "RDRAM", MEMORYAREA_FLAGS_PRIMARY);
MAYBE_ADD_MEMORY_DOMAIN(cartridge.rom, "ROM", 0);
MAYBE_ADD_MEMORY_DOMAIN(pi.rom, "PI ROM", 0);
MAYBE_ADD_MEMORY_DOMAIN(pi.ram, "PI RAM", 0);
MAYBE_ADD_MEMORY_DOMAIN(rsp.dmem, "RSP DMEM", 0);
MAYBE_ADD_MEMORY_DOMAIN(rsp.imem, "RSP IMEM", 0);
MAYBE_ADD_MEMORY_DOMAIN(cartridge.ram, "SRAM", MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_SAVERAMMABLE);
MAYBE_ADD_MEMORY_DOMAIN(cartridge.eeprom, "EEPROM", MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_SAVERAMMABLE);
MAYBE_ADD_MEMORY_DOMAIN(cartridge.flash, "FLASH", MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_SAVERAMMABLE);
MAYBE_ADD_MEMPAK_DOMAIN(1);
MAYBE_ADD_MEMPAK_DOMAIN(2);
MAYBE_ADD_MEMPAK_DOMAIN(3);
MAYBE_ADD_MEMPAK_DOMAIN(4);
}
// fixme: this mismatches the c# side due to some re-ordering c# is doing for some reason
struct MyFrameInfo : public FrameInfo
{
Buttons_t P1Buttons;
Buttons_t P2Buttons;
Buttons_t P3Buttons;
Buttons_t P4Buttons;
s16 P1XAxis;
s16 P1YAxis;
s16 P2XAxis;
s16 P2YAxis;
s16 P3XAxis;
s16 P3YAxis;
s16 P4XAxis;
s16 P4YAxis;
bool Reset;
bool Power;
};
#define UPDATE_CONTROLLER(NUM) do { \
if (auto c = (ares::Nintendo64::Gamepad*)ares::Nintendo64::controllerPort##NUM.device.data()) \
{ \
c->x->setValue(f->P##NUM##XAxis); \
c->y->setValue(f->P##NUM##YAxis); \
c->up->setValue(f->P##NUM##Buttons & UP); \
c->down->setValue(f->P##NUM##Buttons & DOWN); \
c->left->setValue(f->P##NUM##Buttons & LEFT); \
c->right->setValue(f->P##NUM##Buttons & RIGHT); \
c->b->setValue(f->P##NUM##Buttons & B); \
c->a->setValue(f->P##NUM##Buttons & A); \
c->cameraUp->setValue(f->P##NUM##Buttons & C_UP); \
c->cameraDown->setValue(f->P##NUM##Buttons & C_DOWN); \
c->cameraLeft->setValue(f->P##NUM##Buttons & C_LEFT); \
c->cameraRight->setValue(f->P##NUM##Buttons & C_RIGHT); \
c->l->setValue(f->P##NUM##Buttons & L); \
c->r->setValue(f->P##NUM##Buttons & R); \
c->z->setValue(f->P##NUM##Buttons & Z); \
c->start->setValue(f->P##NUM##Buttons & START); \
} \
} while (0)
EXPORT void FrameAdvance(MyFrameInfo* f)
{
if (f->Power)
{
root->power(false);
HackeryDoo();
}
else if (f->Reset)
{
root->power(true);
HackeryDoo();
}
UPDATE_CONTROLLER(1);
UPDATE_CONTROLLER(2);
UPDATE_CONTROLLER(3);
UPDATE_CONTROLLER(4);
platform.lagged = true;
root->run();
f->Width = platform.width;
f->Height = platform.height;
if (platform.newframe)
{
u32* src = platform.videobuf;
u32* dst = f->VideoBuffer;
for (int i = 0; i < f->Height; i++)
{
memcpy(dst, src, f->Width * 4);
dst += f->Width;
src += platform.pitch;
}
platform.newframe = false;
}
s16* soundbuf = f->SoundBuffer;
while (platform.stream->pending())
{
f64 buf[2];
platform.stream->read(buf);
*soundbuf++ = (s16)std::clamp(buf[0] * 32768, -32768.0, 32767.0);
*soundbuf++ = (s16)std::clamp(buf[1] * 32768, -32768.0, 32767.0);
f->Samples++;
}
f->Lagged = platform.lagged;
}
EXPORT void SetInputCallback(void (*callback)())
{
platform.inputcb = callback;
}

57
waterbox/ares64/Makefile Normal file
View File

@ -0,0 +1,57 @@
NEED_LIBCO := 1
ARES_PATH = $(ROOT_DIR)/ares/ares
MAME_PATH = $(ROOT_DIR)/ares/thirdparty/mame
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-parentheses -Wno-reorder -Wno-unused-variable \
-Wno-sign-compare -Wno-switch -Wno-unused-local-typedefs \
-fno-strict-aliasing -fwrapv -fno-operator-names \
-I.$(MAME_PATH)/devices -I.$(MAME_PATH)/emu \
-I.$(MAME_PATH)/lib/util -I.$(MAME_PATH)/mame \
-I.$(MAME_PATH)/osd -DMAME_RDP -DLSB_FIRST -DPTR64 -DSDLMAME_EMSCRIPTEN
TARGET = ares64.wbx
SRCS_PROCESSORS = \
$(ARES_PATH)/component/processor/sm5k/sm5k.cpp
SRCS_ARES = \
$(ARES_PATH)/ares/ares.cpp \
$(ARES_PATH)/ares/memory/fixed-allocator.cpp
SRCS_N64 = \
$(ARES_PATH)/n64/memory/memory.cpp \
$(ARES_PATH)/n64/system/system.cpp \
$(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 \
$(ARES_PATH)/n64/pi/pi.cpp \
$(ARES_PATH)/n64/ri/ri.cpp \
$(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
SRCS_MAME = \
$(MAME_PATH)/emu/emucore.cpp \
$(MAME_PATH)/lib/util/delegate.cpp \
$(MAME_PATH)/lib/util/strformat.cpp \
$(MAME_PATH)/mame/video/n64.cpp \
$(MAME_PATH)/mame/video/pin64.cpp \
$(MAME_PATH)/mame/video/rdpblend.cpp \
$(MAME_PATH)/mame/video/rdptpipe.cpp \
$(MAME_PATH)/osd/osdcore.cpp \
$(MAME_PATH)/osd/osdsync.cpp
SRCS = $(SRCS_PROCESSORS) $(SRCS_ARES) $(SRCS_N64) $(SRCS_MAME) BizInterface.cpp
include ../common.mak

View File

@ -0,0 +1,78 @@
----------------------------------------------------------------------
ares
Copyright (c) 2004-2021 ares team, Near et al
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
----------------------------------------------------------------------
----------------------------------------------------------------------
Stack-less Just-In-Time compiler
Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
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.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) 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(S) 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.
----------------------------------------------------------------------
----------------------------------------------------------------------
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.
----------------------------------------------------------------------

View File

@ -0,0 +1,11 @@
#include <ares/ares.hpp>
#include <ares/debug/debug.cpp>
#include <ares/node/node.cpp>
#include <ares/resource/resource.cpp>
namespace ares {
Platform* platform = nullptr;
bool _runAhead = false;
}

View File

@ -0,0 +1,83 @@
#pragma once
#include <emulibc.h>
#include <libco.h>
#include <sljit.h>
#include <nall/platform.hpp>
#include <nall/adaptive-array.hpp>
#include <nall/any.hpp>
#include <nall/array.hpp>
#include <nall/bump-allocator.hpp>
#include <nall/chrono.hpp>
#include <nall/directory.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>
#include <nall/image.hpp>
#include <nall/literals.hpp>
#include <nall/priority-queue.hpp>
#include <nall/queue.hpp>
#include <nall/random.hpp>
#include <nall/serializer.hpp>
#include <nall/set.hpp>
#include <nall/shared-pointer.hpp>
#include <nall/string.hpp>
#include <nall/terminal.hpp>
#include <nall/thread.hpp>
#include <nall/traits.hpp>
#include <nall/unique-pointer.hpp>
#include <nall/variant.hpp>
#include <nall/vector.hpp>
#include <nall/vfs.hpp>
#include <nall/cd.hpp>
#include <nall/dsp/iir/one-pole.hpp>
#include <nall/dsp/iir/biquad.hpp>
#include <nall/dsp/resampler/cubic.hpp>
#include <nall/hash/crc32.hpp>
#include <nall/hash/sha256.hpp>
using namespace nall;
namespace ares {
static const string Name = "ares";
static const string Version = "126";
static const string Copyright = "ares team, Near";
static const string License = "ISC";
static const string LicenseURI = "https://opensource.org/licenses/ISC";
static const string Website = "ares-emulator.github.io";
static const string WebsiteURI = "https://ares-emulator.github.io";
//incremented only when serialization format changes
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
static const string SerializerVersion = "125";
namespace VFS {
using Pak = shared_pointer<vfs::directory>;
using File = shared_pointer<vfs::file>;
}
namespace Video {
static constexpr bool Threaded = false;
}
namespace Constants {
namespace Colorburst {
static constexpr f64 NTSC = 315.0 / 88.0 * 1'000'000.0;
static constexpr f64 PAL = 283.75 * 15'625.0 + 25.0;
}
}
extern bool _runAhead;
inline auto runAhead() -> bool { return _runAhead; }
inline auto setRunAhead(bool runAhead) -> void { _runAhead = runAhead; }
}
#include <ares/types.hpp>
#include <ares/random.hpp>
#include <ares/debug/debug.hpp>
#include <ares/node/node.hpp>
#include <ares/platform.hpp>
#include <ares/memory/fixed-allocator.hpp>
#include <ares/memory/readable.hpp>
#include <ares/memory/writable.hpp>
#include <ares/resource/resource.hpp>

View File

@ -0,0 +1,45 @@
namespace ares {
Debug _debug;
auto Debug::reset() -> void {
_totalNotices = 0;
_unhandledNotices.reset();
_unimplementedNotices.reset();
_unusualNotices.reset();
_unverifiedNotices.reset();
}
auto Debug::_unhandled(const string& text) -> void {
if(_unhandledNotices.find(text)) return;
if(_totalNotices++ > 256) return;
_unhandledNotices.append(text);
print(terminal::color::yellow("[unhandled] "), text, "\n");
}
auto Debug::_unimplemented(const string& text) -> void {
if(_unimplementedNotices.find(text)) return;
if(_totalNotices++ > 256) return;
_unimplementedNotices.append(text);
print(terminal::color::magenta("[unimplemented] "), text, "\n");
}
auto Debug::_unusual(const string& text) -> void {
if(_unusualNotices.find(text)) return;
if(_totalNotices++ > 256) return;
_unusualNotices.append(text);
print(terminal::color::cyan("[unusual] "), text, "\n");
}
auto Debug::_unverified(const string& text) -> void {
if(_unverifiedNotices.find(text)) return;
if(_totalNotices++ > 256) return;
_unverifiedNotices.append(text);
print(terminal::color::gray("[unverified] "), text, "\n");
}
}

View File

@ -0,0 +1,39 @@
namespace ares {
struct Debug {
auto reset() -> void;
template<typename... P> auto unhandled(P&&... p) -> void {
return _unhandled({forward<P>(p)...});
}
template<typename... P> auto unimplemented(P&&... p) -> void {
return _unimplemented({forward<P>(p)...});
}
template<typename... P> auto unusual(P&&... p) -> void {
return _unusual({forward<P>(p)...});
}
template<typename... P> auto unverified(P&&... p) -> void {
return _unverified({forward<P>(p)...});
}
private:
auto _unhandled(const string&) -> void;
auto _unimplemented(const string&) -> void;
auto _unusual(const string&) -> void;
auto _unverified(const string&) -> void;
u64 _totalNotices = 0;
vector<string> _unhandledNotices;
vector<string> _unimplementedNotices;
vector<string> _unusualNotices;
vector<string> _unverifiedNotices;
};
extern Debug _debug;
}
#define debug(function, ...) if constexpr(1) ares::_debug.function(__VA_ARGS__)

View File

@ -0,0 +1,4 @@
#include <ares/scheduler/thread.hpp>
#include <ares/scheduler/scheduler.hpp>
#include <ares/scheduler/thread.cpp>
#include <ares/scheduler/scheduler.cpp>

View File

@ -0,0 +1,22 @@
#include <ares/ares.hpp>
namespace ares::Memory {
#if defined(PLATFORM_MACOS) && defined(ARCHITECTURE_ARM64)
//stub for unsupported platforms
FixedAllocator::FixedAllocator() {
}
#else
alignas(4096) u8 fixedBuffer[128_MiB];
FixedAllocator::FixedAllocator() {
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
}
#endif
auto FixedAllocator::get() -> bump_allocator& {
static FixedAllocator allocator;
return allocator._allocator;
}
}

View File

@ -0,0 +1,14 @@
#pragma once
namespace ares::Memory {
struct FixedAllocator {
static auto get() -> bump_allocator&;
private:
FixedAllocator();
bump_allocator _allocator;
};
}

View File

@ -0,0 +1,30 @@
#pragma once
namespace ares::Memory {
inline auto mirror(u32 address, u32 size) -> u32 {
if(size == 0) return 0;
u32 base = 0;
u32 mask = 1 << 31;
while(address >= size) {
while(!(address & mask)) mask >>= 1;
address -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
return base + address;
}
inline auto reduce(u32 address, u32 mask) -> u32 {
while(mask) {
u32 bits = (mask & -mask) - 1;
address = address >> 1 & ~bits | address & bits;
mask = (mask & mask - 1) >> 1;
}
return address;
}
}

View File

@ -0,0 +1,73 @@
#pragma once
#include <ares/memory/memory.hpp>
namespace ares::Memory {
template<typename T>
struct Readable {
~Readable() { reset(); }
auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
auto allocate(u32 size, T fill = (T)~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
auto fill(T fill = ~0ull) -> void {
for(u32 address : range(self.size)) {
self.data[address] = fill;
}
}
auto load(VFS::File fp) -> void {
if(!self.size) allocate(fp->size());
fp->read({self.data, min(fp->size(), self.size * sizeof(T))});
for(u32 address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
auto save(VFS::File fp) -> void {
fp->write({self.data, min(fp->size(), self.size * sizeof(T))});
}
explicit operator bool() const { return (bool)self.data; }
auto data() const -> const T* { return self.data; }
auto size() const -> u32 { return self.size; }
auto mask() const -> u32 { return self.mask; }
auto operator[](u32 address) const -> T { return self.data[address & self.mask]; }
auto read(u32 address) const -> T { return self.data[address & self.mask]; }
auto write(u32 address, T data) const -> void {}
auto program(u32 address, T data) const -> void { self.data[address & self.mask] = data; }
auto begin() -> T* { return &self.data[0]; }
auto end() -> T* { return &self.data[self.size]; }
auto begin() const -> const T* { return &self.data[0]; }
auto end() const -> const T* { return &self.data[self.size]; }
auto serialize(serializer& s) -> void {
s(array_span<T>{self.data, self.size});
}
//private:
struct {
T* data = nullptr;
u32 size = 0;
u32 mask = 0;
} self;
};
}

View File

@ -0,0 +1,75 @@
#pragma once
#include <ares/memory/memory.hpp>
namespace ares::Memory {
template<typename T>
struct Writable {
~Writable() { reset(); }
auto reset() -> void {
delete[] self.data;
self.data = nullptr;
self.size = 0;
self.mask = 0;
}
auto allocate(u32 size, T fill = (T)~0ull) -> void {
if(!size) return reset();
delete[] self.data;
self.size = size;
self.mask = bit::round(self.size) - 1;
self.data = new T[self.mask + 1];
memory::fill<T>(self.data, self.mask + 1, fill);
}
auto fill(T fill = ~0ull) -> void {
for(u32 address : range(self.size)) {
self.data[address] = fill;
}
}
auto load(VFS::File fp) -> void {
if(!self.size) allocate(fp->size());
fp->read({self.data, min(fp->size(), self.size * sizeof(T))});
for(u32 address = self.size; address <= self.mask; address++) {
self.data[address] = self.data[mirror(address, self.size)];
}
}
auto save(VFS::File fp) -> void {
fp->write({self.data, min(fp->size(), self.size * sizeof(T))});
}
explicit operator bool() const { return (bool)self.data; }
auto data() -> T* { return self.data; }
auto data() const -> const T* { return self.data; }
auto size() const -> u32 { return self.size; }
auto mask() const -> u32 { return self.mask; }
auto operator[](u32 address) -> T& { return self.data[address & self.mask]; }
auto operator[](u32 address) const -> T { return self.data[address & self.mask]; }
auto read(u32 address) const -> T { return self.data[address & self.mask]; }
auto write(u32 address, T data) -> void { self.data[address & self.mask] = data; }
auto program(u32 address, T data) -> void { self.data[address & self.mask] = data; }
auto begin() -> T* { return &self.data[0]; }
auto end() -> T* { return &self.data[self.size]; }
auto begin() const -> const T* { return &self.data[0]; }
auto end() const -> const T* { return &self.data[self.size]; }
auto serialize(serializer& s) -> void {
s(array_span<T>{self.data, self.size});
}
private:
struct {
T* data = nullptr;
u32 size = 0;
u32 mask = 0;
} self;
};
}

View File

@ -0,0 +1,8 @@
struct Attribute {
Attribute(const string& name, const any& value = {}) : name(name), value(value) {}
auto operator==(const Attribute& source) const -> bool { return name == source.name; }
auto operator< (const Attribute& source) const -> bool { return name < source.name; }
string name;
any value;
};

View File

@ -0,0 +1,4 @@
struct Audio : Object {
DeclareClass(Audio, "audio")
using Object::Object;
};

View File

@ -0,0 +1,131 @@
auto Stream::setChannels(u32 channels) -> void {
_channels.reset();
_channels.resize(channels);
}
auto Stream::setFrequency(f64 frequency) -> void {
_frequency = frequency;
setResamplerFrequency(_resamplerFrequency);
}
auto Stream::setResamplerFrequency(f64 resamplerFrequency) -> void {
_resamplerFrequency = resamplerFrequency;
for(auto& channel : _channels) {
channel.nyquist.reset();
channel.resampler.reset(_frequency, _resamplerFrequency);
}
if(_frequency >= _resamplerFrequency * 2) {
//add a low-pass filter to prevent aliasing during resampling
f64 cutoffFrequency = min(25000.0, _resamplerFrequency / 2.0 - 2000.0);
for(auto& channel : _channels) {
u32 passes = 3;
for(u32 pass : range(passes)) {
DSP::IIR::Biquad filter;
f64 q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, _frequency, q);
channel.nyquist.append(filter);
}
}
}
}
auto Stream::setMuted(bool muted) -> void {
_muted = muted;
}
auto Stream::resetFilters() -> void {
for(auto& channel : _channels) {
channel.filters.reset();
}
}
auto Stream::addLowPassFilter(f64 cutoffFrequency, u32 order, u32 passes) -> void {
for(auto& channel : _channels) {
for(u32 pass : range(passes)) {
if(order == 1) {
Filter filter{Filter::Mode::OnePole, Filter::Type::LowPass, Filter::Order::First};
filter.onePole.reset(DSP::IIR::OnePole::Type::LowPass, cutoffFrequency, _frequency);
channel.filters.append(filter);
}
if(order == 2) {
Filter filter{Filter::Mode::Biquad, Filter::Type::LowPass, Filter::Order::Second};
f64 q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.biquad.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, _frequency, q);
channel.filters.append(filter);
}
}
}
}
auto Stream::addHighPassFilter(f64 cutoffFrequency, u32 order, u32 passes) -> void {
for(auto& channel : _channels) {
for(u32 pass : range(passes)) {
if(order == 1) {
Filter filter{Filter::Mode::OnePole, Filter::Type::HighPass, Filter::Order::First};
filter.onePole.reset(DSP::IIR::OnePole::Type::HighPass, cutoffFrequency, _frequency);
channel.filters.append(filter);
}
if(order == 2) {
Filter filter{Filter::Mode::Biquad, Filter::Type::HighPass, Filter::Order::Second};
f64 q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
filter.biquad.reset(DSP::IIR::Biquad::Type::HighPass, cutoffFrequency, _frequency, q);
channel.filters.append(filter);
}
}
}
}
auto Stream::addLowShelfFilter(f64 cutoffFrequency, u32 order, f64 gain, f64 slope) -> void {
for(auto& channel : _channels) {
if(order == 2) {
Filter filter{Filter::Mode::Biquad, Filter::Type::LowShelf, Filter::Order::Second};
f64 q = DSP::IIR::Biquad::shelf(gain, slope);
filter.biquad.reset(DSP::IIR::Biquad::Type::LowShelf, cutoffFrequency, _frequency, q);
channel.filters.append(filter);
}
}
}
auto Stream::addHighShelfFilter(f64 cutoffFrequency, u32 order, f64 gain, f64 slope) -> void {
for(auto& channel : _channels) {
if(order == 2) {
Filter filter{Filter::Mode::Biquad, Filter::Type::HighShelf, Filter::Order::Second};
f64 q = DSP::IIR::Biquad::shelf(gain, slope);
filter.biquad.reset(DSP::IIR::Biquad::Type::HighShelf, cutoffFrequency, _frequency, q);
channel.filters.append(filter);
}
}
}
auto Stream::pending() const -> bool {
return _channels && _channels[0].resampler.pending();
}
auto Stream::read(f64 samples[]) -> u32 {
for(u32 c : range(_channels.size())) {
samples[c] = _channels[c].resampler.read() * !muted();
}
return _channels.size();
}
auto Stream::write(const f64 samples[]) -> void {
for(u32 c : range(_channels.size())) {
f64 sample = samples[c] + 1e-25; //constant offset used to suppress denormals
for(auto& filter : _channels[c].filters) {
switch(filter.mode) {
case Filter::Mode::OnePole: sample = filter.onePole.process(sample); break;
case Filter::Mode::Biquad: sample = filter.biquad.process(sample); break;
}
}
for(auto& filter : _channels[c].nyquist) {
sample = filter.process(sample);
}
_channels[c].resampler.write(sample);
}
//if there are samples pending, then alert the frontend to possibly process them.
//this will generally happen when every audio stream has pending samples to be mixed.
if(pending()) platform->audio(shared());
}

View File

@ -0,0 +1,49 @@
struct Stream : Audio {
DeclareClass(Stream, "audio.stream")
using Audio::Audio;
auto channels() const -> u32 { return _channels.size(); }
auto frequency() const -> f64 { return _frequency; }
auto resamplerFrequency() const -> f64 { return _resamplerFrequency; }
auto muted() const -> bool { return _muted; }
auto setChannels(u32 channels) -> void;
auto setFrequency(f64 frequency) -> void;
auto setResamplerFrequency(f64 resamplerFrequency) -> void;
auto setMuted(bool muted) -> void;
auto resetFilters() -> void;
auto addLowPassFilter(f64 cutoffFrequency, u32 order, u32 passes = 1) -> void;
auto addHighPassFilter(f64 cutoffFrequency, u32 order, u32 passes = 1) -> void;
auto addLowShelfFilter(f64 cutoffFrequency, u32 order, f64 gain, f64 slope) -> void;
auto addHighShelfFilter(f64 cutoffFrequency, u32 order, f64 gain, f64 slope) -> void;
auto pending() const -> bool;
auto read(f64 samples[]) -> u32;
auto write(const f64 samples[]) -> void;
template<typename... P>
auto frame(P&&... p) -> void {
if(runAhead()) return;
f64 samples[sizeof...(p)] = {forward<P>(p)...};
write(samples);
}
protected:
struct Filter {
enum class Mode : u32 { OnePole, Biquad } mode;
enum class Type : u32 { None, LowPass, HighPass, LowShelf, HighShelf } type;
enum class Order : u32 { None, First, Second } order;
DSP::IIR::OnePole onePole;
DSP::IIR::Biquad biquad;
};
struct Channel {
vector<Filter> filters;
vector<DSP::IIR::Biquad> nyquist;
DSP::Resampler::Cubic resampler;
};
vector<Channel> _channels;
f64 _frequency = 48000.0;
f64 _resamplerFrequency = 48000.0;
bool _muted = false;
};

View File

@ -0,0 +1,34 @@
//horrible implementation of run-time introspection:
//allow converting a unique class string to a derived Node type.
struct Class {
struct Instance {
const string identifier;
const function<Node::Object ()> create;
};
static auto classes() -> vector<Instance>& {
static vector<Instance> classes;
return classes;
}
template<typename T> static auto register() -> void {
if(!classes().find([&](auto instance) { return instance.identifier == T::identifier(); })) {
classes().append({T::identifier(), &T::create});
} else {
throw;
}
}
static auto create(string identifier) -> Node::Object {
if(auto index = classes().find([&](auto instance) { return instance.identifier == identifier; })) {
return classes()[*index].create();
}
if(identifier == "Object") throw; //should never occur: detects unregistered classes
return create("Object");
}
template<typename T> struct Register {
Register() { Class::register<T>(); }
};
};

View File

@ -0,0 +1,4 @@
struct Component : Object {
DeclareClass(Component, "component");
using Object::Object;
};

View File

@ -0,0 +1,30 @@
struct RealTimeClock : Component {
DeclareClass(RealTimeClock, "component.real-time-clock")
using Component::Component;
auto update() -> void { if(_update) return _update(); }
auto timestamp() const -> u64 { return _timestamp; }
auto setUpdate(function<void ()> update) -> void { _update = update; }
auto setTimestamp(u64 timestamp) -> void { _timestamp = timestamp; }
auto synchronize(u64 timestamp = 0) -> void {
if(!timestamp) timestamp = chrono::timestamp();
_timestamp = timestamp;
update();
}
auto serialize(string& output, string depth) -> void override {
Component::serialize(output, depth);
output.append(depth, " timestamp: ", _timestamp, "\n");
}
auto unserialize(Markup::Node node) -> void override {
Component::unserialize(node);
_timestamp = node["timestamp"].natural();
}
protected:
function<void ()> _update;
u64 _timestamp;
};

View File

@ -0,0 +1,4 @@
struct Debugger : Object {
DeclareClass(Debugger, "debugger")
using Object::Object;
};

View File

@ -0,0 +1,26 @@
struct Graphics : Debugger {
DeclareClass(Graphics, "debugger.graphics")
Graphics(string name = {}) : Debugger(name) {
}
auto width() const -> u32 { return _width; }
auto height() const -> u32 { return _height; }
auto capture() const -> vector<u32> { if(_capture) return _capture(); return {}; }
auto setSize(u32 width, u32 height) -> void { _width = width, _height = height; }
auto setCapture(function<vector<u32> ()> capture) -> void { _capture = capture; }
auto serialize(string& output, string depth) -> void override {
Debugger::serialize(output, depth);
}
auto unserialize(Markup::Node node) -> void override {
Debugger::unserialize(node);
}
protected:
u32 _width = 0;
u32 _height = 0;
function<vector<u32> ()> _capture;
};

View File

@ -0,0 +1,27 @@
struct Memory : Debugger {
DeclareClass(Memory, "debugger.memory")
Memory(string name = {}) : Debugger(name) {
}
auto size() const -> u32 { return _size; }
auto read(u32 address) const -> n8 { if(_read) return _read(address); return 0; }
auto write(u32 address, u8 data) const -> void { if(_write) return _write(address, data); }
auto setSize(u32 size) -> void { _size = size; }
auto setRead(function<u8 (u32)> read) -> void { _read = read; }
auto setWrite(function<void (u32, u8)> write) -> void { _write = write; }
auto serialize(string& output, string depth) -> void override {
Debugger::serialize(output, depth);
}
auto unserialize(Markup::Node node) -> void override {
Debugger::unserialize(node);
}
protected:
u32 _size = 0;
function<u8 (u32)> _read;
function<void (u32, u8)> _write;
};

View File

@ -0,0 +1,21 @@
struct Properties : Debugger {
DeclareClass(Properties, "debugger.properties")
Properties(string name = {}) : Debugger(name) {
}
auto query() const -> string { if(_query) return _query(); return {}; }
auto setQuery(function<string ()> query) -> void { _query = query; }
auto serialize(string& output, string depth) -> void override {
Debugger::serialize(output, depth);
}
auto unserialize(Markup::Node node) -> void override {
Debugger::unserialize(node);
}
protected:
function<string ()> _query;
};

View File

@ -0,0 +1,125 @@
struct Instruction : Tracer {
DeclareClass(Instruction, "debugger.tracer.instruction")
Instruction(string name = {}, string component = {}) : Tracer(name, component) {
setMask(_mask);
setDepth(_depth);
}
auto addressBits() const -> u32 { return _addressBits; }
auto addressMask() const -> u32 { return _addressMask; }
auto mask() const -> bool { return _mask; }
auto depth() const -> u32 { return _depth; }
auto setAddressBits(u32 addressBits, u32 addressMask = 0) -> void {
_addressBits = addressBits;
_addressMask = addressMask;
}
auto setMask(bool mask) -> void {
_mask = mask;
}
auto setDepth(u32 depth) -> void {
_depth = depth;
_history.reset();
_history.resize(depth);
for(auto& history : _history) history = ~0;
}
auto address(u32 address) -> bool {
address &= (1ull << _addressBits) - 1; //mask upper bits of address
_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(_depth) {
for(auto history : _history) {
if(_address == history) {
_omitted++;
return false; //do not trace again if recently traced
}
}
for(auto index : range(_depth - 1)) {
_history[index] = _history[index + 1];
}
_history.last() = _address;
}
return true;
}
//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(u32 address) -> void {
if(unlikely(_mask && updateMasks())) {
address &= (1ull << _addressBits) - 1;
address >>= _addressMask;
_masks[address >> 3] &= ~(1 << (address & 7));
}
}
auto notify(const string& instruction, const string& context, const string& extra = {}) -> void {
if(!enabled()) return;
if(_omitted) {
PlatformLog({
"[Omitted: ", _omitted, "]\n"}
);
_omitted = 0;
}
string output{
_component, " ",
hex(_address, _addressBits + 3 >> 2), " ",
instruction, " ",
context, " ",
extra
};
PlatformLog({output.strip(), "\n"});
}
auto serialize(string& output, string depth) -> void override {
Tracer::serialize(output, depth);
output.append(depth, " addressBits: ", _addressBits, "\n");
output.append(depth, " addressMask: ", _addressMask, "\n");
output.append(depth, " mask: ", _mask, "\n");
output.append(depth, " depth: ", _depth, "\n");
}
auto unserialize(Markup::Node node) -> void override {
Tracer::unserialize(node);
_addressBits = node["addressBits"].natural();
_addressMask = node["addressMask"].natural();
_mask = node["mask"].boolean();
_depth = node["depth"].natural();
setMask(_mask);
setDepth(_depth);
}
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;
}
u32 _addressBits = 32;
u32 _addressMask = 0;
bool _mask = false;
u32 _depth = 4;
//unserialized:
n64 _address = 0;
n64 _omitted = 0;
vector<u32> _history;
vector<u08> _masks;
};

View File

@ -0,0 +1,18 @@
struct Notification : Tracer {
DeclareClass(Notification, "debugger.tracer.notification")
Notification(string name = {}, string component = {}) : Tracer(name, component) {
}
auto notify(const string& message = {}) -> void {
if(!enabled()) return;
if(message) {
PlatformLog({_component, " ", _name, ": ", message, "\n"});
} else {
PlatformLog({_component, " ", _name, "\n"});
}
}
protected:
};

View File

@ -0,0 +1,29 @@
struct Tracer : Debugger {
DeclareClass(Tracer, "debugger.tracer")
Tracer(string name = {}, string component = {}) : Debugger(name) {
_component = component;
}
auto component() const -> string { return _component; }
auto enabled() const -> bool { return _enabled; }
auto setComponent(string component) -> void { _component = component; }
auto setEnabled(bool enabled) -> void { _enabled = enabled; }
auto serialize(string& output, string depth) -> void override {
Debugger::serialize(output, depth);
output.append(depth, " component: ", _component, "\n");
output.append(depth, " enabled: ", _enabled, "\n");
}
auto unserialize(Markup::Node node) -> void override {
Debugger::unserialize(node);
_component = node["component"].string();
_enabled = node["enabled"].boolean();
}
protected:
string _component;
bool _enabled = false;
};

View File

@ -0,0 +1,12 @@
struct Axis : Input {
DeclareClass(Axis, "input.axis")
using Input::Input;
auto value() const -> s64 { return _value; }
auto setValue(s64 value) -> void { _value = value; }
protected:
s64 _value = 0;
s64 _minimum = -32768;
s64 _maximum = +32767;
};

View File

@ -0,0 +1,10 @@
struct Button : Input {
DeclareClass(Button, "input.button")
using Input::Input;
auto value() const -> bool { return _value; }
auto setValue(bool value) -> void { _value = value; }
protected:
bool _value = 0;
};

View File

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

View File

@ -0,0 +1,10 @@
struct Rumble : Input {
DeclareClass(Rumble, "input.rumble")
using Input::Input;
auto enable() const -> bool { return _enable; }
auto setEnable(bool enable) -> void { _enable = enable; }
protected:
bool _enable = 0;
};

View File

@ -0,0 +1,12 @@
struct Trigger : Input {
DeclareClass(Trigger, "input.trigger")
using Input::Input;
auto value() const -> s64 { return _value; }
auto setValue(s64 value) -> void { _value = value; }
protected:
s64 _value = 0;
s64 _minimum = 0;
s64 _maximum = +32767;
};

View File

@ -0,0 +1,9 @@
namespace ares::Core {
namespace Video {
#include <ares/node/video/sprite.cpp>
#include <ares/node/video/screen.cpp>
}
namespace Audio {
#include <ares/node/audio/stream.cpp>
}
}

View File

@ -0,0 +1,185 @@
namespace ares::Core {
struct Object;
struct System;
struct Peripheral;
struct Port;
namespace Component {
struct Component;
struct RealTimeClock;
}
namespace Video {
struct Video;
struct Sprite;
struct Screen;
}
namespace Audio {
struct Audio;
struct Stream;
}
namespace Input {
struct Input;
struct Button;
struct Axis;
struct Trigger;
struct Rumble;
}
namespace Setting {
struct Setting;
struct Boolean;
struct Natural;
struct Integer;
struct Real;
struct String;
}
namespace Debugger {
struct Debugger;
struct Memory;
struct Graphics;
struct Properties;
namespace Tracer {
struct Tracer;
struct Notification;
struct Instruction;
}
}
}
namespace ares::Node {
using Object = shared_pointer<Core::Object>;
using System = shared_pointer<Core::System>;
using Peripheral = shared_pointer<Core::Peripheral>;
using Port = shared_pointer<Core::Port>;
namespace Component {
using Component = shared_pointer<Core::Component::Component>;
using RealTimeClock = shared_pointer<Core::Component::RealTimeClock>;
}
namespace Video {
using Video = shared_pointer<Core::Video::Video>;
using Sprite = shared_pointer<Core::Video::Sprite>;
using Screen = shared_pointer<Core::Video::Screen>;
}
namespace Audio {
using Audio = shared_pointer<Core::Audio::Audio>;
using Stream = shared_pointer<Core::Audio::Stream>;
}
namespace Input {
using Input = shared_pointer<Core::Input::Input>;
using Button = shared_pointer<Core::Input::Button>;
using Axis = shared_pointer<Core::Input::Axis>;
using Trigger = shared_pointer<Core::Input::Trigger>;
using Rumble = shared_pointer<Core::Input::Rumble>;
}
namespace Setting {
using Setting = shared_pointer<Core::Setting::Setting>;
using Boolean = shared_pointer<Core::Setting::Boolean>;
using Natural = shared_pointer<Core::Setting::Natural>;
using Integer = shared_pointer<Core::Setting::Integer>;
using Real = shared_pointer<Core::Setting::Real>;
using String = shared_pointer<Core::Setting::String>;
}
namespace Debugger {
using Debugger = shared_pointer<Core::Debugger::Debugger>;
using Memory = shared_pointer<Core::Debugger::Memory>;
using Graphics = shared_pointer<Core::Debugger::Graphics>;
using Properties = shared_pointer<Core::Debugger::Properties>;
namespace Tracer {
using Tracer = shared_pointer<Core::Debugger::Tracer::Tracer>;
using Notification = shared_pointer<Core::Debugger::Tracer::Notification>;
using Instruction = shared_pointer<Core::Debugger::Tracer::Instruction>;
}
}
}
namespace ares::Core {
// <ares/platform.hpp> forward declarations
static auto PlatformAttach(Node::Object) -> void;
static auto PlatformDetach(Node::Object) -> void;
static auto PlatformLog(string_view) -> void;
#include <ares/node/attribute.hpp>
#include <ares/node/class.hpp>
#include <ares/node/object.hpp>
#include <ares/node/system.hpp>
#include <ares/node/peripheral.hpp>
#include <ares/node/port.hpp>
namespace Component {
#include <ares/node/component/component.hpp>
#include <ares/node/component/real-time-clock.hpp>
}
namespace Video {
#include <ares/node/video/video.hpp>
#include <ares/node/video/sprite.hpp>
#include <ares/node/video/screen.hpp>
}
namespace Audio {
#include <ares/node/audio/audio.hpp>
#include <ares/node/audio/stream.hpp>
}
namespace Input {
#include <ares/node/input/input.hpp>
#include <ares/node/input/button.hpp>
#include <ares/node/input/axis.hpp>
#include <ares/node/input/trigger.hpp>
#include <ares/node/input/rumble.hpp>
}
namespace Setting {
#include <ares/node/setting/setting.hpp>
#include <ares/node/setting/boolean.hpp>
#include <ares/node/setting/natural.hpp>
#include <ares/node/setting/integer.hpp>
#include <ares/node/setting/real.hpp>
#include <ares/node/setting/string.hpp>
}
namespace Debugger {
#include <ares/node/debugger/debugger.hpp>
#include <ares/node/debugger/memory.hpp>
#include <ares/node/debugger/graphics.hpp>
#include <ares/node/debugger/properties.hpp>
namespace Tracer {
#include <ares/node/debugger/tracer/tracer.hpp>
#include <ares/node/debugger/tracer/notification.hpp>
#include <ares/node/debugger/tracer/instruction.hpp>
}
}
}
namespace ares::Node {
static inline auto create(string identifier) -> Object {
return Core::Class::create(identifier);
}
static inline auto serialize(Object node) -> string {
if(!node) return {};
string result;
node->serialize(result, {});
return result;
}
static inline auto unserialize(string markup) -> Object {
auto document = BML::unserialize(markup);
if(!document) return {};
auto node = Core::Class::create(document["node"].string());
node->unserialize(document["node"]);
return node;
}
static inline auto parent(Object child) -> Object {
if(!child || !child->parent()) return {};
if(auto parent = child->parent().acquire()) return parent;
return {};
}
template<typename T>
static inline auto find(Object from, string name) -> Object {
if(!from) return {};
if(auto object = from->find<T>(name)) return object;
return {};
}
template<typename T>
static inline auto enumerate(Object from) -> vector<T> {
vector<T> objects;
if(from) from->enumerate<T>(objects);
return objects;
}
}

View File

@ -0,0 +1,235 @@
//identifier() is static, allowing template<typename T> to access via T::identifier()
//identity() is virtual, allowing T* to access via T->identity()
#define DeclareClass(Type, Name) \
static auto identifier() -> string { return Name; } \
static auto create() -> Node::Object { return new Type; } \
auto identity() const -> string override { return Name; } \
private: static inline Class::Register<Type> register; public: \
struct Object : shared_pointer_this<Object> {
static auto identifier() -> string { return "Object"; }
static auto create() -> Node::Object { return new Object; }
virtual auto identity() const -> string { return "Object"; }
private: static inline Class::Register<Object> register; public:
//DeclareClass(Object, "object")
Object(string name = {}) : _name(name) {}
virtual ~Object() = default;
auto name() const -> string { return _name; }
auto parent() const -> shared_pointer_weak<Object> { return _parent; }
auto setName(string_view name) -> void { _name = name; }
auto prepend(Node::Object node) -> Node::Object {
if(auto found = find(node)) return found;
_nodes.prepend(node);
node->_parent = shared();
PlatformAttach(node);
return node;
}
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)...));
}
auto append(Node::Object node) -> Node::Object {
if(auto found = find(node)) return found;
_nodes.append(node);
node->_parent = shared();
PlatformAttach(node);
return node;
}
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)...));
}
auto remove(Node::Object node) -> void {
if(auto index = _nodes.find(node)) {
PlatformDetach(node);
node->reset();
node->_parent.reset();
_nodes.remove(*index);
}
}
auto reset() -> void {
for(auto& node : _nodes) {
PlatformDetach(node);
node->reset();
node->_parent.reset();
}
_nodes.reset();
}
template<typename T>
auto cast() -> shared_pointer<typename T::type> {
if(dynamic_cast<typename T::type*>(this)) return shared();
return {};
}
template<typename T>
auto is() -> bool {
return (bool)cast<T>();
}
template<typename T>
auto find() -> vector<shared_pointer<typename T::type>> {
vector<shared_pointer<typename T::type>> result;
if(dynamic_cast<typename T::type*>(this)) {
if(auto instance = shared()) result.append(instance);
}
for(auto& node : _nodes) result.append(node->find<T>());
return result;
}
template<typename T>
auto find(u32 index) -> shared_pointer<typename T::type> {
auto result = find<T>();
if(index < result.size()) return result[index];
return {};
}
auto find(Node::Object source) -> Node::Object {
if(!source) return {};
for(auto& node : _nodes) {
if(node->identity() == source->identity() && node->_name == source->_name) return node;
}
return {};
}
template<typename T = Node::Object>
auto find(string name) -> T {
using Type = typename T::type;
auto path = name.split("/");
name = path.takeFirst();
for(auto& node : _nodes) {
if(node->_name != name) continue;
if(path) return node->find<T>(path.merge("/"));
if(node->identity() == Type::identifier()) return node;
}
return {};
}
template<typename T = Node::Object>
auto scan(string name) -> T {
using Type = typename T::type;
for(auto& node : _nodes) {
if(node->identity() == Type::identifier() && node->_name == name) return node;
if(auto result = node->scan<T>(name)) return result;
}
return {};
}
template<typename T>
auto enumerate(vector<T>& objects) -> void {
using Type = typename T::type;
if(auto instance = cast<T>()) objects.append(instance);
for(auto& node : _nodes) node->enumerate<T>(objects);
}
auto pak() -> VFS::Pak {
return _pak;
}
auto setPak(VFS::Pak pak) -> bool {
_pak = pak;
return (bool)_pak;
}
template<typename T = string>
auto attribute(const string& name) const -> T {
if(auto attribute = _attributes.find(name)) {
if(attribute->value.is<T>()) return attribute->value.get<T>();
}
return {};
}
template<typename T = string>
auto hasAttribute(const string& name) const -> bool {
if(auto attribute = _attributes.find(name)) {
if(attribute->value.is<T>()) return true;
}
return false;
}
template<typename T = string, typename U = string>
auto setAttribute(const string& name, const U& value = {}) -> void {
if constexpr(is_same_v<T, string> && !is_same_v<U, string>) return setAttribute(name, string{value});
if(auto attribute = _attributes.find(name)) {
if((const T&)value) attribute->value = (const T&)value;
else _attributes.remove(*attribute);
} else {
if((const T&)value) _attributes.insert({name, (const T&)value});
}
}
virtual auto load(Node::Object source) -> bool {
if(!source || identity() != source->identity() || _name != source->_name) return false;
_attributes = source->_attributes;
return true;
}
auto save() -> string {
string markup;
serialize(markup, {});
return markup;
}
virtual auto serialize(string& output, string depth) -> void {
output.append(depth, "node: ", identity(), "\n");
output.append(depth, " name: ", _name, "\n");
for(auto& attribute : _attributes) {
if(!attribute.value.is<string>()) continue;
output.append(depth, " attribute\n");
output.append(depth, " name: ", attribute.name, "\n");
output.append(depth, " value: ", attribute.value.get<string>(), "\n");
}
depth.append(" ");
for(auto& node : _nodes) {
node->serialize(output, depth);
}
}
virtual auto unserialize(Markup::Node markup) -> void {
if(!markup) return;
_name = markup["name"].string();
_attributes.reset();
for(auto& attribute : markup.find("attribute")) {
_attributes.insert({attribute["name"].string(), attribute["value"].string()});
}
for(auto& leaf : markup.find("node")) {
auto node = Class::create(leaf.string());
append(node);
node->unserialize(leaf);
}
}
virtual auto copy(Node::Object source) -> void {
_attributes = source->_attributes;
for(auto& from : source->_nodes) {
for(auto& to : _nodes) {
if(from->identity() != to->identity()) continue;
if(from->name() != to->name()) continue;
to->copy(from);
break;
}
}
}
auto begin() { return _nodes.begin(); }
auto end() { return _nodes.end(); }
protected:
string _name;
VFS::Pak _pak;
set<Attribute> _attributes;
shared_pointer_weak<Object> _parent;
vector<Node::Object> _nodes;
};

View File

@ -0,0 +1,4 @@
struct Peripheral : Object {
DeclareClass(Peripheral, "peripheral")
using Object::Object;
};

View File

@ -0,0 +1,74 @@
struct Port : Object {
DeclareClass(Port, "port")
using Object::Object;
auto type() const -> string { return _type; }
auto family() const -> string { return _family; }
auto hotSwappable() const -> bool { return _hotSwappable; }
auto supported() const -> vector<string> { return _supported; }
auto setAllocate(function<Node::Peripheral (string)> allocate) -> void { _allocate = allocate; }
auto setConnect(function<void ()> connect) -> void { _connect = connect; }
auto setDisconnect(function<void ()> disconnect) -> void { _disconnect = disconnect; }
auto setType(string type) -> void { _type = type; }
auto setFamily(string family) -> void { _family = family; }
auto setHotSwappable(bool hotSwappable) -> void { _hotSwappable = hotSwappable; }
auto setSupported(vector<string> supported) -> void { _supported = supported; }
auto connected() -> Node::Peripheral {
return find<Node::Peripheral>(0);
}
auto allocate(string name = {}) -> Node::Peripheral {
disconnect();
if(_allocate) return _allocate(name);
return {};
}
auto connect() -> void {
if(_connect) _connect();
}
auto disconnect() -> void {
if(auto peripheral = connected()) {
if(_disconnect) _disconnect();
remove(peripheral);
}
}
auto serialize(string& output, string depth) -> void override {
Object::serialize(output, depth);
output.append(depth, " type: ", _type, "\n");
output.append(depth, " family: ", _family, "\n");
output.append(depth, " hotSwappable: ", _hotSwappable, "\n");
}
auto unserialize(Markup::Node node) -> void override {
Object::unserialize(node);
_type = node["type"].string();
_family = node["family"].string();
_hotSwappable = node["hotSwappable"].boolean();
}
auto copy(Node::Object object) -> void override {
if(auto source = object->cast<Node::Port>()) {
Object::copy(source);
if(auto peripheral = source->find<Node::Peripheral>(0)) {
if(auto node = allocate(peripheral->name())) {
node->copy(peripheral);
connect();
node->copy(peripheral);
}
}
}
}
protected:
function<Node::Peripheral (string)> _allocate;
function<void ()> _connect;
function<void ()> _disconnect;
string _type;
string _family;
bool _hotSwappable = false;
vector<string> _supported;
};

View File

@ -0,0 +1,36 @@
struct Boolean : Setting {
DeclareClass(Boolean, "setting.boolean")
Boolean(string name = {}, bool value = {}, function<void (bool)> modify = {}) : Setting(name) {
_currentValue = value;
_latchedValue = value;
_modify = modify;
}
auto modify(bool value) const -> void { if(_modify) return _modify(value); }
auto value() const -> bool { return _currentValue; }
auto latch() const -> bool { return _latchedValue; }
auto setModify(function<void (bool)> modify) { _modify = modify; }
auto setValue(bool value) -> void {
_currentValue = value;
if(_dynamic) setLatch();
}
auto setLatch() -> void override {
if(_latchedValue == _currentValue) return;
_latchedValue = _currentValue;
modify(_latchedValue);
}
auto readValue() const -> string override { return value(); }
auto readLatch() const -> string override { return latch(); }
auto readAllowedValues() const -> vector<string> override { return {"false", "true"}; }
auto writeValue(string value) -> void override { setValue(value.boolean()); }
protected:
function<void (bool)> _modify;
bool _currentValue = {};
bool _latchedValue = {};
};

View File

@ -0,0 +1,47 @@
struct Integer : Setting {
DeclareClass(Integer, "setting.integer")
Integer(string name = {}, s64 value = {}, function<void (s64)> modify = {}) : Setting(name) {
_currentValue = value;
_latchedValue = value;
_modify = modify;
}
auto modify(s64 value) const -> void { if(_modify) return _modify(value); }
auto value() const -> s64 { return _currentValue; }
auto latch() const -> s64 { return _latchedValue; }
auto setModify(function<void (s64)> modify) { _modify = modify; }
auto setValue(s64 value) -> void {
if(_allowedValues && !_allowedValues.find(value)) return;
_currentValue = value;
if(_dynamic) setLatch();
}
auto setLatch() -> void override {
if(_latchedValue == _currentValue) return;
_latchedValue = _currentValue;
modify(_latchedValue);
}
auto setAllowedValues(vector<s64> allowedValues) -> void {
_allowedValues = allowedValues;
if(_allowedValues && !_allowedValues.find(_currentValue)) setValue(_allowedValues.first());
}
auto readValue() const -> string override { return value(); }
auto readLatch() const -> string override { return latch(); }
auto readAllowedValues() const -> vector<string> override {
vector<string> values;
for(auto value : _allowedValues) values.append(value);
return values;
}
auto writeValue(string value) -> void override { setValue(value.integer()); }
protected:
function<void (s64)> _modify;
s64 _currentValue = {};
s64 _latchedValue = {};
vector<s64> _allowedValues;
};

View File

@ -0,0 +1,47 @@
struct Natural : Setting {
DeclareClass(Natural, "setting.natural")
Natural(string name = {}, u64 value = {}, function<void (u64)> modify = {}) : Setting(name) {
_currentValue = value;
_latchedValue = value;
_modify = modify;
}
auto modify(u64 value) const -> void { if(_modify) return _modify(value); }
auto value() const -> u64 { return _currentValue; }
auto latch() const -> u64 { return _latchedValue; }
auto setModify(function<void (u64)> modify) { _modify = modify; }
auto setValue(u64 value) -> void {
if(_allowedValues && !_allowedValues.find(value)) return;
_currentValue = value;
if(_dynamic) setLatch();
}
auto setLatch() -> void override {
if(_latchedValue == _currentValue) return;
_latchedValue = _currentValue;
modify(_latchedValue);
}
auto setAllowedValues(vector<u64> allowedValues) -> void {
_allowedValues = allowedValues;
if(_allowedValues && !_allowedValues.find(_currentValue)) setValue(_allowedValues.first());
}
auto readValue() const -> string override { return value(); }
auto readLatch() const -> string override { return latch(); }
auto readAllowedValues() const -> vector<string> override {
vector<string> values;
for(auto value : _allowedValues) values.append(value);
return values;
}
auto writeValue(string value) -> void override { setValue(value.natural()); }
protected:
function<void (u64)> _modify;
u64 _currentValue = {};
u64 _latchedValue = {};
vector<u64> _allowedValues;
};

View File

@ -0,0 +1,47 @@
struct Real : Setting {
DeclareClass(Real, "setting.real")
Real(string name = {}, f64 value = {}, function<void (f64)> modify = {}) : Setting(name) {
_currentValue = value;
_latchedValue = value;
_modify = modify;
}
auto modify(f64 value) const -> void { if(_modify) return _modify(value); }
auto value() const -> f64 { return _currentValue; }
auto latch() const -> f64 { return _latchedValue; }
auto setModify(function<void (f64)> modify) { _modify = modify; }
auto setValue(f64 value) -> void {
if(_allowedValues && !_allowedValues.find(value)) return;
_currentValue = value;
if(_dynamic) setLatch();
}
auto setLatch() -> void override {
if(_latchedValue == _currentValue) return;
_latchedValue = _currentValue;
modify(_latchedValue);
}
auto setAllowedValues(vector<f64> allowedValues) -> void {
_allowedValues = allowedValues;
if(_allowedValues && !_allowedValues.find(_currentValue)) setValue(_allowedValues.first());
}
auto readValue() const -> string override { return value(); }
auto readLatch() const -> string override { return latch(); }
auto readAllowedValues() const -> vector<string> override {
vector<string> values;
for(auto value : _allowedValues) values.append(value);
return values;
}
auto writeValue(string value) -> void override { setValue(value.real()); }
protected:
function<void (f64)> _modify;
f64 _currentValue = {};
f64 _latchedValue = {};
vector<f64> _allowedValues;
};

View File

@ -0,0 +1,46 @@
struct Setting : Object {
DeclareClass(Setting, "setting")
using Object::Object;
auto dynamic() const -> bool { return _dynamic; }
auto setDynamic(bool dynamic) -> void {
_dynamic = dynamic;
}
virtual auto setLatch() -> void {}
virtual auto readValue() const -> string { return {}; }
virtual auto readLatch() const -> string { return {}; }
virtual auto readAllowedValues() const -> vector<string> { return {}; }
virtual auto writeValue(string value) -> void {}
auto load(Node::Object source) -> bool override {
if(!Object::load(source)) return false;
if(auto setting = source->cast<shared_pointer<Setting>>()) writeValue(setting->readValue());
return true;
}
auto copy(Node::Object object) -> void override {
if(auto source = object->cast<Node::Setting::Setting>()) {
Object::copy(source);
writeValue(source->readValue());
setLatch();
}
}
auto serialize(string& output, string depth) -> void override {
Object::serialize(output, depth);
output.append(depth, " dynamic: ", _dynamic, "\n");
output.append(depth, " value: ", readValue(), "\n");
}
auto unserialize(Markup::Node node) -> void override {
Object::unserialize(node);
_dynamic = node["dynamic"].boolean();
writeValue(node["value"].string());
}
protected:
bool _dynamic = false;
};

View File

@ -0,0 +1,43 @@
struct String : Setting {
DeclareClass(String, "setting.string")
String(string name = {}, string value = {}, function<void (string)> modify = {}) : Setting(name) {
_currentValue = value;
_latchedValue = value;
_modify = modify;
}
auto modify(string value) const -> void { if(_modify) return _modify(value); }
auto value() const -> string { return _currentValue; }
auto latch() const -> string { return _latchedValue; }
auto setModify(function<void (string)> modify) { _modify = modify; }
auto setValue(string value) -> void {
if(_allowedValues && !_allowedValues.find(value)) return;
_currentValue = value;
if(_dynamic) setLatch();
}
auto setLatch() -> void override {
if(_latchedValue == _currentValue) return;
_latchedValue = _currentValue;
modify(_latchedValue);
}
auto setAllowedValues(vector<string> allowedValues) -> void {
_allowedValues = allowedValues;
if(_allowedValues && !_allowedValues.find(_currentValue)) setValue(_allowedValues.first());
}
auto readValue() const -> string override { return value(); }
auto readLatch() const -> string override { return latch(); }
auto readAllowedValues() const -> vector<string> override { return _allowedValues; }
auto writeValue(string value) -> void override { setValue(value); }
protected:
function<void (string)> _modify;
string _currentValue = {};
string _latchedValue = {};
vector<string> _allowedValues;
};

View File

@ -0,0 +1,29 @@
struct System : Object {
DeclareClass(System, "system")
using Object::Object;
auto game() -> string { if(_game) return _game(); return {}; }
auto run() -> void { if(_run) return _run(); }
auto power(bool reset = false) -> void { if(_power) return _power(reset); }
auto save() -> void { if(_save) return _save(); }
auto unload() -> void { if(_unload) return _unload(); }
auto serialize(bool synchronize = true) -> serializer { if(_serialize) return _serialize(synchronize); return {}; }
auto unserialize(serializer& s) -> bool { if(_unserialize) return _unserialize(s); return false; }
auto setGame(function<string ()> game) -> void { _game = game; }
auto setRun(function<void ()> run) -> void { _run = run; }
auto setPower(function<void (bool)> power) -> void { _power = power; }
auto setSave(function<void ()> save) -> void { _save = save; }
auto setUnload(function<void ()> unload) -> void { _unload = unload; }
auto setSerialize(function<serializer (bool)> serialize) -> void { _serialize = serialize; }
auto setUnserialize(function<bool (serializer&)> unserialize) -> void { _unserialize = unserialize; }
protected:
function<string ()> _game;
function<void ()> _run;
function<void (bool)> _power;
function<void ()> _save;
function<void ()> _unload;
function<serializer (bool)> _serialize;
function<bool (serializer&)> _unserialize;
};

View File

@ -0,0 +1,388 @@
Screen::Screen(string name, u32 width, u32 height) : Video(name) {
_canvasWidth = width;
_canvasHeight = height;
if(width && height) {
_inputA = alloc_invisible<u32>(width * height);
_inputB = alloc_invisible<u32>(width * height);
_output = alloc_invisible<u32>(width * height);
_rotate = alloc_invisible<u32>(width * height);
if constexpr(ares::Video::Threaded) {
_thread = nall::thread::create({&Screen::main, this});
}
}
}
Screen::~Screen() {
if constexpr(ares::Video::Threaded) {
if(_canvasWidth && _canvasHeight) {
_kill = true;
_thread.join();
}
}
}
auto Screen::main(uintptr_t) -> void {
while(!_kill) {
usleep(1);
if(_frame) {
refresh();
_frame = false;
}
}
}
auto Screen::quit() -> void {
_kill = true;
_thread.join();
_sprites.reset();
}
auto Screen::power() -> void {
lock_guard<recursive_mutex> lock(_mutex);
memory::fill<u32>(_inputA.data(), _canvasWidth * _canvasHeight, _fillColor);
memory::fill<u32>(_inputB.data(), _canvasWidth * _canvasHeight, _fillColor);
memory::fill<u32>(_output.data(), _canvasWidth * _canvasHeight, _fillColor);
memory::fill<u32>(_rotate.data(), _canvasWidth * _canvasHeight, _fillColor);
}
auto Screen::pixels(bool frame) -> array_span<u32> {
if(frame == 0) return {_inputA.data(), _canvasWidth * _canvasHeight};
if(frame == 1) return {_inputB.data(), _canvasWidth * _canvasHeight};
return {};
}
auto Screen::resetPalette() -> void {
lock_guard<recursive_mutex> lock(_mutex);
_palette.reset();
}
auto Screen::resetSprites() -> void {
lock_guard<recursive_mutex> lock(_mutex);
_sprites.reset();
}
auto Screen::setRefresh(function<void ()> refresh) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_refresh = refresh;
}
auto Screen::setViewport(u32 x, u32 y, u32 width, u32 height) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_viewportX = x;
_viewportY = y;
_viewportWidth = width;
_viewportHeight = height;
}
auto Screen::setSize(u32 width, u32 height) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_width = width;
_height = height;
}
auto Screen::setScale(f64 scaleX, f64 scaleY) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_scaleX = scaleX;
_scaleY = scaleY;
}
auto Screen::setAspect(f64 aspectX, f64 aspectY) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_aspectX = aspectX;
_aspectY = aspectY;
}
auto Screen::setSaturation(f64 saturation) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_saturation = saturation;
_palette.reset();
}
auto Screen::setGamma(f64 gamma) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_gamma = gamma;
_palette.reset();
}
auto Screen::setLuminance(f64 luminance) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_luminance = luminance;
_palette.reset();
}
auto Screen::setFillColor(u32 fillColor) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_fillColor = fillColor;
}
auto Screen::setColorBleed(bool colorBleed) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_colorBleed = colorBleed;
}
auto Screen::setInterframeBlending(bool interframeBlending) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_interframeBlending = interframeBlending;
}
auto Screen::setRotation(u32 rotation) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_rotation = rotation;
}
auto Screen::setProgressive(bool progressiveDouble) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_interlace = false;
_progressive = true;
_progressiveDouble = progressiveDouble;
}
auto Screen::setInterlace(bool interlaceField) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_progressive = false;
_interlace = true;
_interlaceField = interlaceField;
}
auto Screen::attach(Node::Video::Sprite sprite) -> void {
lock_guard<recursive_mutex> lock(_mutex);
if(_sprites.find(sprite)) return;
_sprites.append(sprite);
}
auto Screen::detach(Node::Video::Sprite sprite) -> void {
lock_guard<recursive_mutex> lock(_mutex);
if(!_sprites.find(sprite)) return;
_sprites.removeByValue(sprite);
}
auto Screen::colors(u32 colors, function<n64 (n32)> color) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_colors = colors;
_color = color;
_palette.reset();
}
auto Screen::frame() -> void {
if(runAhead()) return;
while(_frame) spinloop();
lock_guard<recursive_mutex> lock(_mutex);
_inputA.swap(_inputB);
_frame = true;
if constexpr(!ares::Video::Threaded) {
refresh();
_frame = false;
}
}
auto Screen::refresh() -> void {
lock_guard<recursive_mutex> lock(_mutex);
if(runAhead()) return;
refreshPalette();
if(_refresh) _refresh();
auto viewX = _viewportX;
auto viewY = _viewportY;
auto viewWidth = _viewportWidth;
auto viewHeight = _viewportHeight;
auto pitch = _canvasWidth;
auto width = _canvasWidth;
auto height = _canvasHeight;
auto input = _inputB.data();
auto output = _output.data();
for(u32 y : range(height)) {
auto source = input + y * pitch;
auto target = output + y * width;
if(_interlace) {
if((_interlaceField & 1) == (y & 1)) {
for(u32 x : range(width)) {
auto color = _palette[*source++];
*target++ = color;
}
}
} else if(_progressive && _progressiveDouble) {
source = input + (y & ~1) * pitch;
for(u32 x : range(width)) {
auto color = _palette[*source++];
*target++ = color;
}
} else if(_interframeBlending) {
n32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
for(u32 x : range(width)) {
auto a = *target;
auto b = _palette[*source++];
*target++ = (a + b - ((a ^ b) & mask)) >> 1;
}
} else {
for(u32 x : range(width)) {
auto color = _palette[*source++];
*target++ = color;
}
}
}
if(_colorBleed) {
n32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
for(u32 y : range(height)) {
auto target = output + y * width;
for(u32 x : range(width)) {
auto a = target[x];
auto b = target[x + (x != width - 1)];
target[x] = (a + b - ((a ^ b) & mask)) >> 1;
}
}
}
for(auto& sprite : _sprites) {
if(!sprite->visible()) continue;
n32 alpha = 255u << 24;
for(int y : range(sprite->height())) {
s32 pixelY = sprite->y() + y;
if(pixelY < 0 || pixelY >= height) continue;
auto source = sprite->image().data() + y * sprite->width();
auto target = &output[pixelY * width];
for(s32 x : range(sprite->width())) {
s32 pixelX = sprite->x() + x;
if(pixelX < 0 || pixelX >= width) continue;
auto pixel = source[x];
if(pixel >> 24) target[pixelX] = alpha | pixel;
}
}
}
if(_rotation == 90) {
//rotate left
for(u32 y : range(height)) {
auto source = output + y * width;
for(u32 x : range(width)) {
auto target = _rotate.data() + (width - 1 - x) * height + y;
*target = *source++;
}
}
output = _rotate.data();
swap(width, height);
swap(viewWidth, viewHeight);
}
if(_rotation == 180) {
//rotate upside down
for(u32 y : range(height)) {
auto source = output + y * width;
for(u32 x : range(width)) {
auto target = _rotate.data() + (height - 1 - y) * width + (width - 1 - x);
*target = *source++;
}
}
output = _rotate.data();
}
if(_rotation == 270) {
//rotate right
for(u32 y : range(height)) {
auto source = output + y * width;
for(u32 x : range(width)) {
auto target = _rotate.data() + x * height + (height - 1 - y);
*target = *source++;
}
}
output = _rotate.data();
swap(width, height);
swap(viewWidth, viewHeight);
}
platform->video(shared(), output + viewX + viewY * width, width * sizeof(u32), viewWidth, viewHeight);
memory::fill<u32>(_inputB.data(), width * height, _fillColor);
}
auto Screen::refreshPalette() -> void {
lock_guard<recursive_mutex> lock(_mutex);
if(_palette) return;
//generate the color lookup palettes to convert native colors to ARGB8888
_palette = new u32[_colors];
for(u32 index : range(_colors)) {
n64 color = _color(index);
n16 b = color.bit( 0,15);
n16 g = color.bit(16,31);
n16 r = color.bit(32,47);
n16 a = 65535;
if(_saturation != 1.0) {
n16 grayscale = uclamp<16>((r + g + b) / 3);
f64 inverse = max(0.0, 1.0 - _saturation);
r = uclamp<16>(r * _saturation + grayscale * inverse);
g = uclamp<16>(g * _saturation + grayscale * inverse);
b = uclamp<16>(b * _saturation + grayscale * inverse);
}
if(_gamma != 1.0) {
f64 reciprocal = 1.0 / 32767.0;
r = r > 32767 ? r : n16(32767 * pow(r * reciprocal, _gamma));
g = g > 32767 ? g : n16(32767 * pow(g * reciprocal, _gamma));
b = b > 32767 ? b : n16(32767 * pow(b * reciprocal, _gamma));
}
if(_luminance != 1.0) {
r = uclamp<16>(r * _luminance);
g = uclamp<16>(g * _luminance);
b = uclamp<16>(b * _luminance);
}
a >>= 8;
r >>= 8;
g >>= 8;
b >>= 8;
_palette[index] = a << 24 | r << 16 | g << 8 | b << 0;
}
}
auto Screen::serialize(string& output, string depth) -> void {
Video::serialize(output, depth);
output.append(depth, " width: ", _width, "\n");
output.append(depth, " height: ", _height, "\n");
output.append(depth, " scaleX: ", _scaleX, "\n");
output.append(depth, " scaleY: ", _scaleY, "\n");
output.append(depth, " aspectX: ", _aspectX, "\n");
output.append(depth, " aspectY: ", _aspectY, "\n");
output.append(depth, " colors: ", _colors, "\n");
output.append(depth, " saturation: ", _saturation, "\n");
output.append(depth, " gamma: ", _gamma, "\n");
output.append(depth, " luminance: ", _luminance, "\n");
output.append(depth, " fillColor: ", _fillColor, "\n");
output.append(depth, " colorBleed: ", _colorBleed, "\n");
output.append(depth, " interlace: ", _interlace, "\n");
output.append(depth, " interframeBlending: ", _interframeBlending, "\n");
output.append(depth, " rotation: ", _rotation, "\n");
}
auto Screen::unserialize(Markup::Node node) -> void {
Video::unserialize(node);
_width = node["width"].natural();
_height = node["height"].natural();
_scaleX = node["scaleX"].real();
_scaleY = node["scaleY"].real();
_aspectX = node["aspectX"].real();
_aspectY = node["aspectY"].real();
_colors = node["colors"].natural();
_saturation = node["saturation"].real();
_gamma = node["gamma"].real();
_luminance = node["luminance"].real();
_fillColor = node["fillColor"].natural();
_colorBleed = node["colorBleed"].boolean();
_interlace = node["interlace"].natural();
_interframeBlending = node["interframeBlending"].boolean();
_rotation = node["rotation"].natural();
resetPalette();
resetSprites();
}

View File

@ -0,0 +1,106 @@
struct Screen : Video {
DeclareClass(Screen, "video.screen")
using Video::Video;
Screen(string name = {}, u32 width = 0, u32 height = 0);
~Screen();
auto main(uintptr_t) -> void;
auto quit() -> void;
auto power() -> void;
auto canvasWidth() const -> u32 { return _canvasWidth; }
auto canvasHeight() const -> u32 { return _canvasHeight; }
auto width() const -> u32 { return _width; }
auto height() const -> u32 { return _height; }
auto scaleX() const -> f64 { return _scaleX; }
auto scaleY() const -> f64 { return _scaleY; }
auto aspectX() const -> f64 { return _aspectX; }
auto aspectY() const -> f64 { return _aspectY; }
auto colors() const -> u32 { return _colors; }
auto pixels(bool frame = 0) -> array_span<u32>;
auto saturation() const -> double { return _saturation; }
auto gamma() const -> double { return _gamma; }
auto luminance() const -> double { return _luminance; }
auto fillColor() const -> u32 { return _fillColor; }
auto colorBleed() const -> bool { return _colorBleed; }
auto interframeBlending() const -> bool { return _interframeBlending; }
auto rotation() const -> u32 { return _rotation; }
auto resetPalette() -> void;
auto resetSprites() -> void;
auto setRefresh(function<void ()> refresh) -> void;
auto setViewport(u32 x, u32 y, u32 width, u32 height) -> void;
auto setSize(u32 width, u32 height) -> void;
auto setScale(f64 scaleX, f64 scaleY) -> void;
auto setAspect(f64 aspectX, f64 aspectY) -> void;
auto setSaturation(f64 saturation) -> void;
auto setGamma(f64 gamma) -> void;
auto setLuminance(f64 luminance) -> void;
auto setFillColor(u32 fillColor) -> void;
auto setColorBleed(bool colorBleed) -> void;
auto setInterframeBlending(bool interframeBlending) -> void;
auto setRotation(u32 rotation) -> void;
auto setProgressive(bool progressiveDouble = false) -> void;
auto setInterlace(bool interlaceField) -> void;
auto attach(Node::Video::Sprite) -> void;
auto detach(Node::Video::Sprite) -> void;
auto colors(u32 colors, function<n64 (n32)> color) -> void;
auto frame() -> void;
auto refresh() -> void;
auto serialize(string& output, string depth) -> void override;
auto unserialize(Markup::Node node) -> void override;
private:
auto refreshPalette() -> void;
protected:
u32 _canvasWidth = 0;
u32 _canvasHeight = 0;
u32 _width = 0;
u32 _height = 0;
f64 _scaleX = 1.0;
f64 _scaleY = 1.0;
f64 _aspectX = 1.0;
f64 _aspectY = 1.0;
u32 _colors = 0;
f64 _saturation = 1.0;
f64 _gamma = 1.0;
f64 _luminance = 1.0;
u32 _fillColor = 0;
bool _colorBleed = false;
bool _interframeBlending = false;
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[]> _palette;
vector<Node::Video::Sprite> _sprites;
//unserialized:
nall::thread _thread;
recursive_mutex _mutex;
atomic<bool> _kill = false;
atomic<bool> _frame = false;
function<void ()> _refresh;
bool _progressive = false;
bool _progressiveDouble = false;
bool _interlace = false;
bool _interlaceField = false;
u32 _viewportX = 0;
u32 _viewportY = 0;
u32 _viewportWidth = 0;
u32 _viewportHeight = 0;
};

View File

@ -0,0 +1,26 @@
auto Sprite::setVisible(bool visible) -> void {
_visible = visible;
}
auto Sprite::setPosition(u32 x, u32 y) -> void {
_x = x;
_y = y;
}
auto Sprite::setImage(nall::image image, bool invert) -> void {
_width = image.width();
_height = image.height();
_pixels = new u32[_width * _height];
image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
memory::copy(_pixels.data(), _width * _height * sizeof(u32), image.data(), image.size());
if(!invert) return;
for(u32 y : range(_height)) {
auto data = _pixels.data() + y * _width;
for(u32 x : range(_width)) {
auto pixel = data[x];
pixel ^= 0xffffff;
data[x] = pixel;
}
}
}

View File

@ -0,0 +1,23 @@
struct Sprite : Video {
DeclareClass(Sprite, "video.sprite")
using Video::Video;
auto visible() const -> bool { return _visible; }
auto x() const -> u32 { return _x; }
auto y() const -> u32 { return _y; }
auto width() const -> u32 { return _width; }
auto height() const -> u32 { return _height; }
auto image() const -> array_view<u32> { return {_pixels.data(), _width * _height}; }
auto setVisible(bool visible) -> void;
auto setPosition(u32 x, u32 y) -> void;
auto setImage(nall::image, bool invert = false) -> void;
protected:
bool _visible = false;
u32 _x = 0;
u32 _y = 0;
u32 _width = 0;
u32 _height = 0;
unique_pointer<u32[]> _pixels;
};

View File

@ -0,0 +1,4 @@
struct Video : Object {
DeclareClass(Video, "video");
using Object::Object;
};

View File

@ -0,0 +1,33 @@
#pragma once
namespace ares {
enum class Event : u32 {
None,
Step,
Frame,
Power,
Synchronize,
};
struct Platform {
virtual auto attach(Node::Object) -> void {}
virtual auto detach(Node::Object) -> void {}
virtual auto pak(Node::Object) -> shared_pointer<vfs::directory> { return {}; }
virtual auto event(Event) -> void {}
virtual auto log(string_view message) -> void {}
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
virtual auto audio(Node::Audio::Stream) -> void {}
virtual auto input(Node::Input::Input) -> void {}
};
extern Platform* platform;
}
namespace ares::Core {
// <ares/node/node.hpp> forward declarations
auto PlatformAttach(Node::Object node) -> void { if(platform && node->name()) platform->attach(node); }
auto PlatformDetach(Node::Object node) -> void { if(platform && node->name()) platform->detach(node); }
auto PlatformLog(string_view text) -> void { if(platform) platform->log(text); }
}

View File

@ -0,0 +1,96 @@
#pragma once
namespace ares {
struct Random {
enum class Entropy : u32 { None, Low, High };
auto operator()() -> n64 {
return random();
}
auto entropy(Entropy entropy) -> void {
_entropy = entropy;
seed();
}
auto seed(maybe<n32> seed = nothing, maybe<n32> sequence = nothing) -> void {
if(!seed) seed = (n32)clock();
if(!sequence) sequence = 0;
_state = 0;
_increment = sequence() << 1 | 1;
step();
_state += seed();
step();
}
auto random() -> n64 {
if(_entropy == Entropy::None) return 0;
return (n64)step() << 32 | (n64)step() << 0;
}
auto bias(n64 bias) -> n64 {
if(_entropy == Entropy::None) return bias;
return random();
}
auto bound(n64 bound) -> n64 {
n64 threshold = -bound % bound;
while(true) {
n64 result = random();
if(result >= threshold) return result % bound;
}
}
auto array(array_span<n8> buffer) -> void {
if(_entropy == Entropy::None) {
memory::fill(buffer.data(), buffer.size());
return;
}
if(_entropy == Entropy::High) {
for(n32 address : range(buffer.size())) {
buffer[address] = random();
}
return;
}
//Entropy::Low
u32 lobit = random() & 3;
u32 hibit = (lobit + 8 + (random() & 3)) & 15;
u32 lovalue = random() & 255;
u32 hivalue = random() & 255;
if((random() & 3) == 0) lovalue = 0;
if((random() & 1) == 0) hivalue = ~lovalue;
for(n32 address : range(buffer.size())) {
n8 value = address.bit(lobit) ? lovalue : hivalue;
if(address.bit(hibit)) value = ~value;
if((random() & 511) == 0) value.bit(random() & 7) ^= 1;
if((random() & 2047) == 0) value.bit(random() & 7) ^= 1;
buffer[address] = value;
}
}
auto serialize(serializer& s) -> void {
s((u32&)_entropy);
s(_state);
s(_increment);
}
private:
auto step() -> n32 {
n64 state = _state;
_state = state * 6364136223846793005ull + _increment;
n32 xorshift = (state >> 18 ^ state) >> 27;
n32 rotate = state >> 59;
return xorshift >> rotate | xorshift << (-rotate & 31);
}
Entropy _entropy = Entropy::High;
n64 _state;
n64 _increment;
};
}

View File

@ -0,0 +1,6 @@
all:
sourcery resource.bml resource.cpp resource.hpp
clean:
rm resource.cpp
rm resource.hpp

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,29 @@
namespace name=Resource
namespace name=Ares
binary name=Icon1x file=icon.png
binary name=Icon2x file=icon@2x.png
binary name=Logo1x file=logo.png
binary name=Logo2x file=logo@2x.png
namespace name=Sprite
namespace name=SuperFamicom
binary name=CrosshairBlue file=sprite/sfc/crosshair-blue.png
binary name=CrosshairGreen file=sprite/sfc/crosshair-green.png
binary name=CrosshairRed file=sprite/sfc/crosshair-red.png
namespace name=WonderSwan
binary name=Auxiliary0 file=sprite/ws/auxiliary-0.png
binary name=Auxiliary1 file=sprite/ws/auxiliary-1.png
binary name=Auxiliary2 file=sprite/ws/auxiliary-2.png
binary name=Headphones file=sprite/ws/headphones.png
binary name=Initialized file=sprite/ws/initialized.png
binary name=LowBattery file=sprite/ws/low-battery.png
binary name=Orientation0 file=sprite/ws/orientation-0.png
binary name=Orientation1 file=sprite/ws/orientation-1.png
binary name=PoweredOn file=sprite/ws/powered-on.png
binary name=Sleeping file=sprite/ws/sleeping.png
binary name=VolumeA0 file=sprite/ws/volume-a0.png
binary name=VolumeA1 file=sprite/ws/volume-a1.png
binary name=VolumeA2 file=sprite/ws/volume-a2.png
binary name=VolumeB0 file=sprite/ws/volume-b0.png
binary name=VolumeB1 file=sprite/ws/volume-b1.png
binary name=VolumeB2 file=sprite/ws/volume-b2.png
binary name=VolumeB3 file=sprite/ws/volume-b3.png

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
namespace Resource {
namespace Ares {
extern const unsigned char Icon1x[6562];
extern const unsigned char Icon2x[9662];
extern const unsigned char Logo1x[11533];
extern const unsigned char Logo2x[18991];
}
namespace Sprite {
namespace SuperFamicom {
extern const unsigned char CrosshairBlue[332];
extern const unsigned char CrosshairGreen[329];
extern const unsigned char CrosshairRed[342];
}
namespace WonderSwan {
extern const unsigned char Auxiliary0[117];
extern const unsigned char Auxiliary1[134];
extern const unsigned char Auxiliary2[136];
extern const unsigned char Headphones[167];
extern const unsigned char Initialized[136];
extern const unsigned char LowBattery[144];
extern const unsigned char Orientation0[148];
extern const unsigned char Orientation1[150];
extern const unsigned char PoweredOn[155];
extern const unsigned char Sleeping[154];
extern const unsigned char VolumeA0[129];
extern const unsigned char VolumeA1[142];
extern const unsigned char VolumeA2[146];
extern const unsigned char VolumeB0[125];
extern const unsigned char VolumeB1[137];
extern const unsigned char VolumeB2[148];
extern const unsigned char VolumeB3[153];
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

View File

@ -0,0 +1,137 @@
inline auto Scheduler::reset() -> void {
_threads.reset();
}
inline auto Scheduler::threads() const -> u32 {
return _threads.size();
}
inline auto Scheduler::thread(u32 uniqueID) const -> maybe<Thread&> {
for(auto& thread : _threads) {
if(thread->_uniqueID == uniqueID) return *thread;
}
return {};
}
//if threads A and B both have a clock value of 0, it is ambiguous which should run first.
//to resolve this, a uniqueID is assigned to each thread when appended to the scheduler.
//the first unused ID is selected, to avoid the uniqueID growing in an unbounded fashion.
inline auto Scheduler::uniqueID() const -> u32 {
u32 uniqueID = 0;
while(thread(uniqueID)) uniqueID++;
return uniqueID;
}
//find the clock time of the furthest behind thread.
inline auto Scheduler::minimum() const -> u64 {
u64 minimum = (u64)-1;
for(auto& thread : _threads) {
minimum = min(minimum, thread->_clock - thread->_uniqueID);
}
return minimum;
}
//find the clock time of the furthest ahead thread.
inline auto Scheduler::maximum() const -> u64 {
u64 maximum = 0;
for(auto& thread : _threads) {
maximum = max(maximum, thread->_clock - thread->_uniqueID);
}
return maximum;
}
inline auto Scheduler::append(Thread& thread) -> bool {
if(_threads.find(&thread)) return false;
thread._uniqueID = uniqueID();
thread._clock = maximum() + thread._uniqueID;
_threads.append(&thread);
return true;
}
inline auto Scheduler::remove(Thread& thread) -> void {
_threads.removeByValue(&thread);
}
//power cycle and soft reset events: assigns the primary thread and resets all thread clocks.
inline auto Scheduler::power(Thread& thread) -> void {
_primary = _resume = thread.handle();
for(auto& thread : _threads) {
thread->_clock = thread->_uniqueID;
}
}
inline auto Scheduler::enter(Mode mode) -> Event {
if(mode == Mode::Run) {
_mode = mode;
_host = co_active();
co_switch(_resume);
platform->event(_event);
return _event;
}
if(mode == Mode::Synchronize) {
//run all threads to safe points, starting with the primary thread.
for(auto& thread : _threads) {
if(thread->handle() == _primary) {
_mode = Mode::SynchronizePrimary;
_host = co_active();
do {
co_switch(_resume);
platform->event(_event);
} while(_event != Event::Synchronize);
}
}
for(auto& thread : _threads) {
if(thread->handle() != _primary) {
_mode = Mode::SynchronizeAuxiliary;
_host = co_active();
_resume = thread->handle();
do {
co_switch(_resume);
platform->event(_event);
} while(_event != Event::Synchronize);
}
}
return Event::Synchronize;
}
return Event::None;
}
inline auto Scheduler::exit(Event event) -> void {
//subtract the minimum time from all threads to prevent clock overflow.
auto reduce = minimum();
for(auto& thread : _threads) {
thread->_clock -= reduce;
}
//return to the thread that entered the scheduler originally.
_event = event;
_resume = co_active();
co_switch(_host);
}
//used to prevent auxiliary threads from blocking during synchronization.
//for instance, a secondary CPU waiting on an interrupt from the primary CPU.
//as other threads are not run during synchronization, this would otherwise cause a deadlock.
inline auto Scheduler::synchronizing() const -> bool {
return _mode == Mode::SynchronizeAuxiliary;
}
//marks a safe point (typically the beginning of the entry point) of a thread.
//the scheduler may exit at these points for the purpose of synchronization.
inline auto Scheduler::synchronize() -> void {
if(co_active() == _primary) {
if(_mode == Mode::SynchronizePrimary) return exit(Event::Synchronize);
} else {
if(_mode == Mode::SynchronizeAuxiliary) return exit(Event::Synchronize);
}
}
inline auto Scheduler::getSynchronize() -> bool {
return _synchronize;
}
inline auto Scheduler::setSynchronize(bool synchronize) -> void {
_synchronize = synchronize;
}

View File

@ -0,0 +1,47 @@
struct Thread;
struct Scheduler {
enum class Mode : u32 {
Run,
Synchronize,
SynchronizePrimary,
SynchronizeAuxiliary,
};
Scheduler() = default;
Scheduler(const Scheduler&) = delete;
auto operator=(const Scheduler&) = delete;
auto reset() -> void;
auto threads() const -> u32;
auto thread(u32 threadID) const -> maybe<Thread&>;
auto uniqueID() const -> u32;
auto minimum() const -> u64;
auto maximum() const -> u64;
auto append(Thread& thread) -> bool;
auto remove(Thread& thread) -> void;
auto power(Thread& thread) -> void;
auto enter(Mode mode = Mode::Run) -> Event;
auto exit(Event event) -> void;
auto synchronizing() const -> bool;
auto synchronize() -> void;
auto getSynchronize() -> bool;
auto setSynchronize(bool) -> void;
private:
cothread_t _host = nullptr; //program thread (used to exit scheduler)
cothread_t _resume = nullptr; //resume thread (used to enter scheduler)
cothread_t _primary = nullptr; //primary thread (used to synchronize components)
Mode _mode = Mode::Run;
Event _event = Event::Step;
vector<Thread*> _threads;
bool _synchronize = false;
friend class Thread;
};
extern Scheduler scheduler;

View File

@ -0,0 +1,119 @@
inline auto Thread::EntryPoints() -> vector<EntryPoint>& {
static vector<EntryPoint> entryPoints;
return entryPoints;
}
inline auto Thread::Enter() -> void {
for(u32 index : range(EntryPoints().size())) {
if(co_active() == EntryPoints()[index].handle) {
auto entryPoint = EntryPoints()[index].entryPoint;
EntryPoints().remove(index);
while(true) {
scheduler.synchronize();
entryPoint();
}
}
}
struct ThreadNotFound{};
throw ThreadNotFound{};
}
inline Thread::~Thread() {
destroy();
}
inline auto Thread::active() const -> bool { return co_active() == _handle; }
inline auto Thread::handle() const -> cothread_t { return _handle; }
inline auto Thread::frequency() const -> u64 { return _frequency; }
inline auto Thread::scalar() const -> u64 { return _scalar; }
inline auto Thread::clock() const -> u64 { return _clock; }
inline auto Thread::setHandle(cothread_t handle) -> void {
_handle = handle;
}
inline auto Thread::setFrequency(double frequency) -> void {
_frequency = frequency + 0.5;
_scalar = Second / _frequency;
}
inline auto Thread::setScalar(u64 scalar) -> void {
_scalar = scalar;
}
inline auto Thread::setClock(u64 clock) -> void {
_clock = clock;
}
inline auto Thread::create(double frequency, function<void ()> entryPoint) -> void {
if(!_handle) {
_handle = co_create(Thread::Size, &Thread::Enter);
} else {
co_derive(_handle, Thread::Size, &Thread::Enter);
}
EntryPoints().append({_handle, entryPoint});
setFrequency(frequency);
setClock(0);
scheduler.append(*this);
}
//returns a thread to its entry point (eg for a reset), without resetting the clock value
inline auto Thread::restart(function<void()> entryPoint) -> void {
co_derive(_handle, Thread::Size, &Thread::Enter);
EntryPoints().append({_handle, entryPoint});
}
inline auto Thread::destroy() -> void {
scheduler.remove(*this);
if(_handle) co_delete(_handle);
_handle = nullptr;
}
inline auto Thread::step(u32 clocks) -> void {
_clock += _scalar * clocks;
}
//ensure all threads are caught up to the current thread before proceeding.
inline auto Thread::synchronize() -> void {
//note: this will call Thread::synchronize(*this) at some point, but this is safe:
//the comparison will always fail as the current thread can never be behind itself.
for(auto thread : scheduler._threads) synchronize(*thread);
}
//ensure the specified thread(s) are caught up the current thread before proceeding.
template<typename... P>
inline auto Thread::synchronize(Thread& thread, P&&... p) -> void {
//switching to another thread does not guarantee it will catch up before switching back.
while(thread.clock() < clock()) {
//disable synchronization for auxiliary threads during scheduler synchronization.
//synchronization can begin inside of this while loop.
if(scheduler.synchronizing()) break;
co_switch(thread.handle());
}
//convenience: allow synchronizing multiple threads with one function call.
if constexpr(sizeof...(p) > 0) synchronize(forward<P>(p)...);
}
inline auto Thread::serialize(serializer& s) -> void {
s(_frequency);
s(_scalar);
s(_clock);
if(!scheduler._synchronize) {
static u8 stack[Thread::Size];
bool resume = co_active() == _handle;
if(s.reading()) {
s(stack);
s(resume);
memory::copy(_handle, stack, Thread::Size);
if(resume) scheduler._resume = _handle;
}
if(s.writing()) {
memory::copy(stack, _handle, Thread::Size);
s(stack);
s(resume);
}
}
}

View File

@ -0,0 +1,50 @@
struct Scheduler;
struct Thread {
enum : u64 { Second = (u64)-1 >> 1 };
enum : u64 { Size = 16_KiB * sizeof(void*) };
struct EntryPoint {
cothread_t handle = nullptr;
function<void ()> entryPoint;
};
static auto EntryPoints() -> vector<EntryPoint>&;
static auto Enter() -> void;
Thread() = default;
Thread(const Thread&) = delete;
auto operator=(const Thread&) = delete;
virtual ~Thread();
explicit operator bool() const { return _handle; }
auto active() const -> bool;
auto handle() const -> cothread_t;
auto frequency() const -> u64;
auto scalar() const -> u64;
auto clock() const -> u64;
auto setHandle(cothread_t handle) -> void;
auto setFrequency(double frequency) -> void;
auto setScalar(u64 scalar) -> void;
auto setClock(u64 clock) -> void;
auto create(double frequency, function<void ()> entryPoint) -> void;
auto restart(function<void ()> entryPoint) -> void;
auto destroy() -> void;
auto step(u32 clocks) -> void;
auto synchronize() -> void;
template<typename... P> auto synchronize(Thread&, P&&...) -> void;
auto serialize(serializer& s) -> void;
protected:
cothread_t _handle = nullptr;
u32 _uniqueID = 0;
u64 _frequency = 0;
u64 _scalar = 0;
u64 _clock = 0;
friend class Scheduler;
};

View File

@ -0,0 +1,134 @@
using b1 = nall::Boolean;
using i1 = nall::Integer< 1>; using s1 = nall::IntegerPrimitive< 1>;
using i2 = nall::Integer< 2>; using s2 = nall::IntegerPrimitive< 2>;
using i3 = nall::Integer< 3>; using s3 = nall::IntegerPrimitive< 3>;
using i4 = nall::Integer< 4>; using s4 = nall::IntegerPrimitive< 4>;
using i5 = nall::Integer< 5>; using s5 = nall::IntegerPrimitive< 5>;
using i6 = nall::Integer< 6>; using s6 = nall::IntegerPrimitive< 6>;
using i7 = nall::Integer< 7>; using s7 = nall::IntegerPrimitive< 7>;
using i8 = nall::Integer< 8>;
using i9 = nall::Integer< 9>; using s9 = nall::IntegerPrimitive< 9>;
using i10 = nall::Integer<10>; using s10 = nall::IntegerPrimitive<10>;
using i11 = nall::Integer<11>; using s11 = nall::IntegerPrimitive<11>;
using i12 = nall::Integer<12>; using s12 = nall::IntegerPrimitive<12>;
using i13 = nall::Integer<13>; using s13 = nall::IntegerPrimitive<13>;
using i14 = nall::Integer<14>; using s14 = nall::IntegerPrimitive<14>;
using i15 = nall::Integer<15>; using s15 = nall::IntegerPrimitive<15>;
using i16 = nall::Integer<16>;
using i17 = nall::Integer<17>; using s17 = nall::IntegerPrimitive<17>;
using i18 = nall::Integer<18>; using s18 = nall::IntegerPrimitive<18>;
using i19 = nall::Integer<19>; using s19 = nall::IntegerPrimitive<19>;
using i20 = nall::Integer<20>; using s20 = nall::IntegerPrimitive<20>;
using i21 = nall::Integer<21>; using s21 = nall::IntegerPrimitive<21>;
using i22 = nall::Integer<22>; using s22 = nall::IntegerPrimitive<22>;
using i23 = nall::Integer<23>; using s23 = nall::IntegerPrimitive<23>;
using i24 = nall::Integer<24>; using s24 = nall::IntegerPrimitive<24>;
using i25 = nall::Integer<25>; using s25 = nall::IntegerPrimitive<25>;
using i26 = nall::Integer<26>; using s26 = nall::IntegerPrimitive<26>;
using i27 = nall::Integer<27>; using s27 = nall::IntegerPrimitive<27>;
using i28 = nall::Integer<28>; using s28 = nall::IntegerPrimitive<28>;
using i29 = nall::Integer<29>; using s29 = nall::IntegerPrimitive<29>;
using i30 = nall::Integer<30>; using s30 = nall::IntegerPrimitive<30>;
using i31 = nall::Integer<31>; using s31 = nall::IntegerPrimitive<31>;
using i32 = nall::Integer<32>;
using i33 = nall::Integer<33>; using s33 = nall::IntegerPrimitive<33>;
using i34 = nall::Integer<34>; using s34 = nall::IntegerPrimitive<34>;
using i35 = nall::Integer<35>; using s35 = nall::IntegerPrimitive<35>;
using i36 = nall::Integer<36>; using s36 = nall::IntegerPrimitive<36>;
using i37 = nall::Integer<37>; using s37 = nall::IntegerPrimitive<37>;
using i38 = nall::Integer<38>; using s38 = nall::IntegerPrimitive<38>;
using i39 = nall::Integer<39>; using s39 = nall::IntegerPrimitive<39>;
using i40 = nall::Integer<40>; using s40 = nall::IntegerPrimitive<40>;
using i41 = nall::Integer<41>; using s41 = nall::IntegerPrimitive<41>;
using i42 = nall::Integer<42>; using s42 = nall::IntegerPrimitive<42>;
using i43 = nall::Integer<43>; using s43 = nall::IntegerPrimitive<43>;
using i44 = nall::Integer<44>; using s44 = nall::IntegerPrimitive<44>;
using i45 = nall::Integer<45>; using s45 = nall::IntegerPrimitive<45>;
using i46 = nall::Integer<46>; using s46 = nall::IntegerPrimitive<46>;
using i47 = nall::Integer<47>; using s47 = nall::IntegerPrimitive<47>;
using i48 = nall::Integer<48>; using s48 = nall::IntegerPrimitive<48>;
using i49 = nall::Integer<49>; using s49 = nall::IntegerPrimitive<49>;
using i50 = nall::Integer<50>; using s50 = nall::IntegerPrimitive<50>;
using i51 = nall::Integer<51>; using s51 = nall::IntegerPrimitive<51>;
using i52 = nall::Integer<52>; using s52 = nall::IntegerPrimitive<52>;
using i53 = nall::Integer<53>; using s53 = nall::IntegerPrimitive<53>;
using i54 = nall::Integer<54>; using s54 = nall::IntegerPrimitive<54>;
using i55 = nall::Integer<55>; using s55 = nall::IntegerPrimitive<55>;
using i56 = nall::Integer<56>; using s56 = nall::IntegerPrimitive<56>;
using i57 = nall::Integer<57>; using s57 = nall::IntegerPrimitive<57>;
using i58 = nall::Integer<58>; using s58 = nall::IntegerPrimitive<58>;
using i59 = nall::Integer<59>; using s59 = nall::IntegerPrimitive<59>;
using i60 = nall::Integer<60>; using s60 = nall::IntegerPrimitive<60>;
using i61 = nall::Integer<61>; using s61 = nall::IntegerPrimitive<61>;
using i62 = nall::Integer<62>; using s62 = nall::IntegerPrimitive<62>;
using i63 = nall::Integer<63>; using s63 = nall::IntegerPrimitive<63>;
using i64 = nall::Integer<64>;
using n1 = nall::Natural< 1>; using u1 = nall::NaturalPrimitive< 1>;
using n2 = nall::Natural< 2>; using u2 = nall::NaturalPrimitive< 2>;
using n3 = nall::Natural< 3>; using u3 = nall::NaturalPrimitive< 3>;
using n4 = nall::Natural< 4>; using u4 = nall::NaturalPrimitive< 4>;
using n5 = nall::Natural< 5>; using u5 = nall::NaturalPrimitive< 5>;
using n6 = nall::Natural< 6>; using u6 = nall::NaturalPrimitive< 6>;
using n7 = nall::Natural< 7>; using u7 = nall::NaturalPrimitive< 7>;
using n8 = nall::Natural< 8>;
using n9 = nall::Natural< 9>; using u9 = nall::NaturalPrimitive< 9>;
using n10 = nall::Natural<10>; using u10 = nall::NaturalPrimitive<10>;
using n11 = nall::Natural<11>; using u11 = nall::NaturalPrimitive<11>;
using n12 = nall::Natural<12>; using u12 = nall::NaturalPrimitive<12>;
using n13 = nall::Natural<13>; using u13 = nall::NaturalPrimitive<13>;
using n14 = nall::Natural<14>; using u14 = nall::NaturalPrimitive<14>;
using n15 = nall::Natural<15>; using u15 = nall::NaturalPrimitive<15>;
using n16 = nall::Natural<16>;
using n17 = nall::Natural<17>; using u17 = nall::NaturalPrimitive<17>;
using n18 = nall::Natural<18>; using u18 = nall::NaturalPrimitive<18>;
using n19 = nall::Natural<19>; using u19 = nall::NaturalPrimitive<19>;
using n20 = nall::Natural<20>; using u20 = nall::NaturalPrimitive<20>;
using n21 = nall::Natural<21>; using u21 = nall::NaturalPrimitive<21>;
using n22 = nall::Natural<22>; using u22 = nall::NaturalPrimitive<22>;
using n23 = nall::Natural<23>; using u23 = nall::NaturalPrimitive<23>;
using n24 = nall::Natural<24>; using u24 = nall::NaturalPrimitive<24>;
using n25 = nall::Natural<25>; using u25 = nall::NaturalPrimitive<25>;
using n26 = nall::Natural<26>; using u26 = nall::NaturalPrimitive<26>;
using n27 = nall::Natural<27>; using u27 = nall::NaturalPrimitive<27>;
using n28 = nall::Natural<28>; using u28 = nall::NaturalPrimitive<28>;
using n29 = nall::Natural<29>; using u29 = nall::NaturalPrimitive<29>;
using n30 = nall::Natural<30>; using u30 = nall::NaturalPrimitive<30>;
using n31 = nall::Natural<31>; using u31 = nall::NaturalPrimitive<31>;
using n32 = nall::Natural<32>;
using n33 = nall::Natural<33>; using u33 = nall::NaturalPrimitive<33>;
using n34 = nall::Natural<34>; using u34 = nall::NaturalPrimitive<34>;
using n35 = nall::Natural<35>; using u35 = nall::NaturalPrimitive<35>;
using n36 = nall::Natural<36>; using u36 = nall::NaturalPrimitive<36>;
using n37 = nall::Natural<37>; using u37 = nall::NaturalPrimitive<37>;
using n38 = nall::Natural<38>; using u38 = nall::NaturalPrimitive<38>;
using n39 = nall::Natural<39>; using u39 = nall::NaturalPrimitive<39>;
using n40 = nall::Natural<40>; using u40 = nall::NaturalPrimitive<40>;
using n41 = nall::Natural<41>; using u41 = nall::NaturalPrimitive<41>;
using n42 = nall::Natural<42>; using u42 = nall::NaturalPrimitive<42>;
using n43 = nall::Natural<43>; using u43 = nall::NaturalPrimitive<43>;
using n44 = nall::Natural<44>; using u44 = nall::NaturalPrimitive<44>;
using n45 = nall::Natural<45>; using u45 = nall::NaturalPrimitive<45>;
using n46 = nall::Natural<46>; using u46 = nall::NaturalPrimitive<46>;
using n47 = nall::Natural<47>; using u47 = nall::NaturalPrimitive<47>;
using n48 = nall::Natural<48>; using u48 = nall::NaturalPrimitive<48>;
using n49 = nall::Natural<49>; using u49 = nall::NaturalPrimitive<49>;
using n50 = nall::Natural<50>; using u50 = nall::NaturalPrimitive<50>;
using n51 = nall::Natural<51>; using u51 = nall::NaturalPrimitive<51>;
using n52 = nall::Natural<52>; using u52 = nall::NaturalPrimitive<52>;
using n53 = nall::Natural<53>; using u53 = nall::NaturalPrimitive<53>;
using n54 = nall::Natural<54>; using u54 = nall::NaturalPrimitive<54>;
using n55 = nall::Natural<55>; using u55 = nall::NaturalPrimitive<55>;
using n56 = nall::Natural<56>; using u56 = nall::NaturalPrimitive<56>;
using n57 = nall::Natural<57>; using u57 = nall::NaturalPrimitive<57>;
using n58 = nall::Natural<58>; using u58 = nall::NaturalPrimitive<58>;
using n59 = nall::Natural<59>; using u59 = nall::NaturalPrimitive<59>;
using n60 = nall::Natural<60>; using u60 = nall::NaturalPrimitive<60>;
using n61 = nall::Natural<61>; using u61 = nall::NaturalPrimitive<61>;
using n62 = nall::Natural<62>; using u62 = nall::NaturalPrimitive<62>;
using n63 = nall::Natural<63>; using u63 = nall::NaturalPrimitive<63>;
using n64 = nall::Natural<64>;
using r32 = nall::Real<32>;
using r64 = nall::Real<64>;

View File

@ -0,0 +1,89 @@
auto SM5K::disassembleInstruction() -> string {
string s;
n8 opcode = ROM[PC + 0 & sizeof(ROM) - 1];
n8 operand = ROM[PC + 1 & sizeof(ROM) - 1];
string p2 = {"0x", hex(n2(opcode), 1L)};
string p4 = {"0x", hex(n4(opcode), 1L)};
string p5 = {"0x", hex(n5(opcode), 2L)};
string p6 = {"0x", hex(n6(opcode), 2L)};
string p8 = {"0x", hex(n8(operand), 2L)};
string pc = {"0x", hex(n4(opcode) << 8 | operand, 3L)};
switch(opcode) {
case 0x00 ... 0x0f: s = {"adx ", p4}; break;
case 0x10 ... 0x1f: s = {"lax ", p4}; break;
case 0x20 ... 0x2f: s = {"lblx ", p4}; break;
case 0x30 ... 0x3f: s = {"lbmx ", p4}; break;
case 0x40 ... 0x43: s = {"rm ", p2}; break;
case 0x44 ... 0x47: s = {"sm ", p2}; break;
case 0x48 ... 0x4b: s = {"tm ", p2}; break;
case 0x4c ... 0x4f: s = {"tpb ", p2}; break;
case 0x50 ... 0x53: s = {"lda ", p2}; break;
case 0x54 ... 0x57: s = {"exc ", p2}; break;
case 0x58 ... 0x5b: s = {"exci ", p2}; break;
case 0x5c ... 0x5f: s = {"excd ", p2}; break;
case 0x60: s = {"rc " }; break;
case 0x61: s = {"sc " }; break;
case 0x62: s = {"id " }; break;
case 0x63: s = {"ie " }; break;
case 0x64: s = {"exax " }; break;
case 0x65: s = {"atx " }; break;
case 0x66: s = {"exbm " }; break;
case 0x67: s = {"exbl " }; break;
case 0x68: s = {"ex " }; break;
case 0x69: s = {"dta ", p8}; break;
case 0x6a: s = {"pat ", p8}; break;
case 0x6b: s = {"tabl " }; break;
case 0x6c: s = {"ta " }; break;
case 0x6d: s = {"tb " }; break;
case 0x6e: s = {"tc " }; break;
case 0x6f: s = {"tam " }; break;
case 0x70: s = {"inl " }; break;
case 0x71: s = {"outl " }; break;
case 0x72: s = {"anp " }; break;
case 0x73: s = {"orp " }; break;
case 0x74: s = {"in " }; break;
case 0x75: s = {"out " }; break;
case 0x76: s = {"stop " }; break;
case 0x77: s = {"halt " }; break;
case 0x78: s = {"incb " }; break;
case 0x79: s = {"coma " }; break;
case 0x7a: s = {"add " }; break;
case 0x7b: s = {"adc " }; break;
case 0x7c: s = {"decb " }; break;
case 0x7d: s = {"rtn " }; break;
case 0x7e: s = {"rtns " }; break;
case 0x7f: s = {"rtni " }; break;
case 0x80 ... 0xbf: s = {"tr ", p6}; break;
case 0xc0 ... 0xdf: s = {"trs ", p5}; break;
case 0xe0 ... 0xef: s = {"tl ", pc}; break;
case 0xf0 ... 0xff: s = {"call ", pc}; break;
}
while(s.size() < 10) s.append(" ");
return s;
}
auto SM5K::disassembleContext() -> string {
string s;
s.append("A:", hex(A, 1L), " ");
s.append("X:", hex(X, 1L), " ");
s.append("B:", hex(B, 2L), " ");
s.append("C:", hex(C, 1L), " ");
s.append("P0:", hex(P0, 1L), " ");
s.append("P1:", hex(P1, 1L), " ");
s.append("P2:", hex(P2, 1L), " ");
s.append("P3:", hex(P3, 1L), " ");
s.append("P4:", hex(P4, 1L), " ");
s.append("P5:", hex(P5, 1L), " ");
s.append("SP:", hex(SP, 1L), " ");
s.append("SB:", hex(SB, 2L), " ");
s.append("IFA:", hex(IFA, 1L), " ");
s.append("IFB:", hex(IFB, 1L), " ");
s.append("IFT:", hex(IFT, 1L), " ");
s.append("IME:", hex(IME, 1L), " ");
s.append("SKIP:", hex(SKIP, 1L));
return s;
}

View File

@ -0,0 +1,74 @@
#define op(id, name, ...) \
case id: \
if(SKIP) { SKIP = 0; return; } \
return instruction##name(__VA_ARGS__); \
auto SM5K::interrupt(n2 id) -> void {
SR[SP++] = PC;
PU = 2;
PL = id << 1;
HALT = 0;
STOP = 0;
}
auto SM5K::instruction() -> void {
if(IFA & RE.bit(0) & IME) return interrupt(0);
if(IFB & RE.bit(1) & IME) return interrupt(1);
if(IFT & RE.bit(2) & IME) return interrupt(2);
if(HALT) return timerStep();
if(STOP) return timerStep();
n8 opcode = fetch();
switch(opcode) {
op(0x00 ... 0x0f, ADX, n4(opcode));
op(0x10 ... 0x1f, LAX, n4(opcode));
op(0x20 ... 0x2f, LBLX, n4(opcode));
op(0x30 ... 0x3f, LBMX, n4(opcode));
op(0x40 ... 0x43, RM, n2(opcode));
op(0x44 ... 0x47, SM, n2(opcode));
op(0x48 ... 0x4b, TM, n2(opcode));
op(0x4c ... 0x4f, TPB, n2(opcode));
op(0x50 ... 0x53, LDA, n2(opcode));
op(0x54 ... 0x57, EXC, n2(opcode));
op(0x58 ... 0x5b, EXCI, n2(opcode));
op(0x5c ... 0x5f, EXCD, n2(opcode));
op(0x60, RC );
op(0x61, SC );
op(0x62, ID );
op(0x63, IE );
op(0x64, EXAX );
op(0x65, ATX );
op(0x66, EXBM );
op(0x67, EXBL );
op(0x68, EX );
op(0x69, DTA, fetch());
op(0x6a, PAT, fetch());
op(0x6b, TABL );
op(0x6c, TA );
op(0x6d, TB );
op(0x6e, TC );
op(0x6f, TAM );
op(0x70, INL );
op(0x71, OUTL );
op(0x72, ANP );
op(0x73, ORP );
op(0x74, IN );
op(0x75, OUT );
op(0x76, STOP );
op(0x77, HALT );
op(0x78, INCB );
op(0x79, COMA );
op(0x7a, ADD );
op(0x7b, ADC );
op(0x7c, DECB );
op(0x7d, RTN );
op(0x7e, RTNS );
op(0x7f, RTNI );
op(0x80 ... 0xbf, TR, n6(opcode));
op(0xc0 ... 0xdf, TRS, n5(opcode));
op(0xe0 ... 0xef, TL, n4(opcode) << 8 | fetch());
op(0xf0 ... 0xff, CALL, n4(opcode) << 8 | fetch());
}
}
#undef op

View File

@ -0,0 +1,275 @@
auto SM5K::instructionADC() -> void {
auto c = C;
C = A + RAM[B] + c >= 0x10;
A = A + RAM[B] + c;
if(C) SKIP = 1;
}
auto SM5K::instructionADD() -> void {
A += RAM[B];
}
auto SM5K::instructionADX(n4 data) -> void {
if(A + data >= 0x10) SKIP = 1;
A += data;
}
auto SM5K::instructionANP() -> void {
switch(BL) {
case 0x0: P0 &= A; break;
case 0x2: P2 &= A; break;
case 0x4: P4 &= A; break;
case 0x5: P5 &= A; break;
}
}
auto SM5K::instructionATX() -> void {
X = A;
}
auto SM5K::instructionCALL(n12 address) -> void {
SR[SP++] = PC;
PC = address;
}
auto SM5K::instructionCOMA() -> void {
A = ~A;
}
auto SM5K::instructionDECB() -> void {
if(!--BL) SKIP = 1;
}
auto SM5K::instructionDR() -> void {
DIV = 0;
}
auto SM5K::instructionDTA(n8 operand) -> void {
switch(operand) {
case 0x02: instructionTT(); break;
case 0x03: instructionDR(); break;
}
static constexpr u8 rom[8] = {0xfc, 0xfc, 0xa5, 0x6c, 0x03, 0x8f, 0x1b, 0x9a};
if(BM >= 4 && BM <= 7) {
SKIP = rom[BM << 1 | BL >> 3] >> n3(BL) & 1;
}
}
auto SM5K::instructionEX() -> void {
swap(B, SB);
}
auto SM5K::instructionEXAX() -> void {
swap(A, X);
}
auto SM5K::instructionEXBL() -> void {
auto a = A;
A = BL;
BL = a;
}
auto SM5K::instructionEXBM() -> void {
auto a = A;
A = BM;
BM = a;
}
auto SM5K::instructionEXC(n2 data) -> void {
swap(A, RAM[B]);
BM ^= data;
}
auto SM5K::instructionEXCD(n2 data) -> void {
swap(A, RAM[B]);
if(!--BL) SKIP = 1;
BM ^= data;
}
auto SM5K::instructionEXCI(n2 data) -> void {
swap(A, RAM[B]);
if(!++BL) SKIP = 1;
BM ^= data;
}
auto SM5K::instructionID() -> void {
IME = 0;
}
auto SM5K::instructionIE() -> void {
IME = 1;
}
auto SM5K::instructionHALT() -> void {
HALT = 1;
}
auto SM5K::instructionIN() -> void {
switch(BL) {
case 0x1: A = P1; break;
case 0x2: A = P2; break;
case 0x3: A = P3; break;
case 0x4: A = P4; break;
case 0x5: A = P5; break;
case 0x8: A = R8 >> 0; X = R8 >> 4; break;
case 0x9: A = R9 >> 0; X = R9 >> 4; break;
case 0xa: A = RA >> 0; X = RA >> 4; break;
case 0xb: A = RB >> 0; X = RB >> 4; break;
case 0xc: A = RC; break;
case 0xe: A = RE; break;
case 0xf: A = RF; break;
}
}
auto SM5K::instructionINCB() -> void {
if(!++BL) SKIP = 1;
}
auto SM5K::instructionINL() -> void {
A = P1;
}
auto SM5K::instructionLAX(n4 data) -> void {
A = data;
}
auto SM5K::instructionLBLX(n4 data) -> void {
BL = data;
}
auto SM5K::instructionLBMX(n4 data) -> void {
BM = data;
}
auto SM5K::instructionLDA(n2 data) -> void {
A = RAM[B];
BM ^= data;
}
auto SM5K::instructionORP() -> void {
switch(BL) {
case 0x0: P0 |= A; break;
case 0x2: P2 |= A; break;
case 0x4: P4 |= A; break;
case 0x5: P5 |= A; break;
}
}
auto SM5K::instructionOUT() -> void {
switch(BL) {
case 0x0: P0 = A; break;
case 0x2: P2 = A; break;
case 0x3: R3 = A; break;
case 0x4: P4 = A; break;
case 0x5: P5 = A; break;
case 0x8: R8 = A << 0 | X << 4; break;
case 0x9: R9 = A << 0 | X << 4; break;
case 0xa: RA = RB; break;
case 0xb: RB = A << 0 | X << 4; break;
case 0xc: RC = A; break;
case 0xe: RE = A; break;
case 0xf: RF = A; break;
}
}
auto SM5K::instructionOUTL() -> void {
P0 = A;
}
auto SM5K::instructionPAT(n8) -> void {
//should this actually modify the stack frame?
n6 pu = 4;
n6 pl = X << 4 | A;
n8 data = ROM[pu << 6 | pl];
A = data >> 0;
X = data >> 4;
}
auto SM5K::instructionRC() -> void {
C = 0;
}
auto SM5K::instructionRM(n2 data) -> void {
RAM[B] &= ~(1 << data);
}
auto SM5K::instructionRTN() -> void {
PC = SR[--SP];
}
auto SM5K::instructionRTNI() -> void {
PC = SR[--SP];
IME = 1;
}
auto SM5K::instructionRTNS() -> void {
PC = SR[--SP];
SKIP = 1;
}
auto SM5K::instructionSC() -> void {
C = 1;
}
auto SM5K::instructionSM(n2 data) -> void {
RAM[B] |= 1 << data;
}
auto SM5K::instructionSTOP() -> void {
STOP = 1;
}
auto SM5K::instructionTA() -> void {
if(IFA) SKIP = 1;
IFA = 0;
}
auto SM5K::instructionTABL() -> void {
if(A == BL) SKIP = 1;
}
auto SM5K::instructionTAM() -> void {
if(A == RAM[B]) SKIP = 1;
}
auto SM5K::instructionTB() -> void {
if(IFB) SKIP = 1;
IFB = 0;
}
auto SM5K::instructionTC() -> void {
if(C == 1) SKIP = 1;
}
auto SM5K::instructionTL(n12 address) -> void {
PC = address;
}
auto SM5K::instructionTM(n2 data) -> void {
if(RAM[B] & 1 << data) SKIP = 1;
}
auto SM5K::instructionTPB(n2 port) -> void {
switch(port) {
case 0: if(P0 == 1) SKIP = 1; break;
case 1: if(P1 == 1) SKIP = 1; break;
case 2: if(P2 == 1) SKIP = 1; break;
case 3: if(P3 == 1) SKIP = 1; break;
}
}
auto SM5K::instructionTR(n6 address) -> void {
PL = address;
}
auto SM5K::instructionTRS(n5 address) -> void {
SR[SP++] = PC;
PU = 1;
PL = address << 1;
}
auto SM5K::instructionTT() -> void {
if(IFT) SKIP = 1;
IFT = 0;
}

Some files were not shown because too many files have changed in this diff Show More