mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-10-25 07:16:12 -04:00
v0.9.0 (v1.0 beta) (#338)
* move aurora to the aurora-wiicompiled submodule (#315) * Update settings_overlay.cpp (#257) * Simplify controller settings menu (#317) * simplify controller settings menu * platform pairing hint * build for x86-64-v2 on older linux cpus (#319) * Real Wii Remote support over Bluetooth (#323) * pass real wii remotes through emulated bluetooth * build bluetooth tests on macos ci * address review comments * cancel replaced bluetooth reads * Run GX on its own thread (#325) * NTSC-U and NTSC-J support (#326) * add ntsc-u and ntsc-j support Co-authored-by: DarthMDev <mgracer48@yahoo.com> Co-authored-by: rooklz <rookieb@muzo-works.com> * hash inc files and require project for shards * port remaining stale rr us hooks * drop map report generation * drop one-off region porters --------- Co-authored-by: DarthMDev <mgracer48@yahoo.com> Co-authored-by: rooklz <rookieb@muzo-works.com> * Keyboard input for friend code entry (#327) * keyboard input for friend code entry * add new guest imports to synthetic test * replace per-address region table with range map (#328) * paste friend codes from clipboard (#329) * Verify dependency archive hashes (#330) * verify dependency archive hashes * verify cached cppwinrt tool * check dependency path types * Check CPU support before building (#331) * check cpu support before building * build for x86-64-v2 on windows and intel macs * bump aurora for fog fsel fix * add vsync option (#333) * Add exe icons (#334) * add exe icons * rebuild icon resource on ico change * drop icon rebuild tracking * Fix bugs found in forks (#336) * port fixes found in forks * fill whole disk id * dump kamek overlay on crash (#335) * bump aurora and version to 0.9.0 --------- Co-authored-by: DarthMDev <mgracer48@yahoo.com> Co-authored-by: rooklz <rookieb@muzo-works.com>
This commit is contained in:
@@ -157,14 +157,14 @@ public:
|
||||
#define MKW_DETAIL_CAT(a, b) a##b
|
||||
#define MKW_DETAIL_MAKE_UNIQUE(a, b) MKW_DETAIL_CAT(a, b)
|
||||
|
||||
// Three hand-written registration macros. Generated code never registers per function; every
|
||||
// translated function reaches the registry via the bulk BulkTranslatedFunctionRecord tables
|
||||
// in the translator's *_registration TUs.
|
||||
// Three hand-written registration macros, given the PAL address. Generated code never registers
|
||||
// per function; every translated function reaches the registry via the bulk
|
||||
// BulkTranslatedFunctionRecord tables in the translator's *_registration TUs.
|
||||
#define REGISTER_TRANSLATED_FUNCTION(address, fn) \
|
||||
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_trampoline_, __COUNTER__)(address, #fn, fn, FunctionKind::BaseTranslated, false, kPpcAllNonvolatileFprMask, 0, 0, nullptr, KnownTranslatedCpuCall<address>::kMustRemainDynamicallyDispatchable)
|
||||
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_trampoline_, __COUNTER__)(::GuestRegion::Translate(address), #fn, fn, FunctionKind::BaseTranslated, false, kPpcAllNonvolatileFprMask, 0, 0, nullptr, KnownTranslatedCpuCall<address>::kMustRemainDynamicallyDispatchable)
|
||||
|
||||
#define REGISTER_NATIVE_FUNCTION(address, fn) \
|
||||
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_native_trampoline_, __COUNTER__)(address, #fn, fn, FunctionKind::Native, false, kPpcAllNonvolatileFprMask, 0, 0, &AbiRawCpuThunk<&fn>::Invoke)
|
||||
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_native_trampoline_, __COUNTER__)(::GuestRegion::Translate(address), #fn, fn, FunctionKind::Native, false, kPpcAllNonvolatileFprMask, 0, 0, &AbiRawCpuThunk<&fn>::Invoke)
|
||||
|
||||
#define REGISTER_NATIVE_FUNCTION_AS(address, fn, pretty_name) \
|
||||
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_native_trampoline_named_, __COUNTER__)(address, pretty_name, fn, FunctionKind::Native, false, kPpcAllNonvolatileFprMask, 0, 0, &AbiRawCpuThunk<&fn>::Invoke)
|
||||
static AbiTrampoline<decltype(fn)> MKW_DETAIL_MAKE_UNIQUE(_abi_native_trampoline_named_, __COUNTER__)(::GuestRegion::Translate(address), pretty_name, fn, FunctionKind::Native, false, kPpcAllNonvolatileFprMask, 0, 0, &AbiRawCpuThunk<&fn>::Invoke)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "friend_code_keyboard.h"
|
||||
#include "settings_overlay.h"
|
||||
#include "runtime_config.h"
|
||||
#include "gx_worker.h"
|
||||
|
||||
#include <aurora/aurora.h>
|
||||
#include <aurora/event.h>
|
||||
@@ -117,6 +119,7 @@ inline void ProcessAuroraEvents(const AuroraEvent* events) {
|
||||
g_mkwDynamicAspectSurfacePending.store(true, std::memory_order_release);
|
||||
}
|
||||
settings_overlay::HandleEvents(events);
|
||||
FriendCodeKeyboard::HandleEvents(events);
|
||||
}
|
||||
|
||||
inline void ApplyPendingMkwDynamicAspectSurface() {
|
||||
@@ -131,14 +134,42 @@ inline void ApplyPendingMkwDynamicAspectSurface() {
|
||||
UpdateMkwDynamicAspectSurface(surfaceWidth, surfaceHeight);
|
||||
}
|
||||
|
||||
void GxWorkerBeginFrame();
|
||||
void GxWorkerEndFrame(AuroraImguiFrame* imgui);
|
||||
void SetAuroraFrameGxBegun(bool begun);
|
||||
|
||||
inline bool BeginAuroraFrame() {
|
||||
if (GxWorker::ShouldDefer()) {
|
||||
// The window half can touch the surface and render targets the worker draws into.
|
||||
GxWorker::Sync();
|
||||
if (!aurora_begin_frame_window()) {
|
||||
return false;
|
||||
}
|
||||
GxWorker::Submit([] { GxWorkerBeginFrame(); });
|
||||
ApplyPendingMkwDynamicAspectSurface();
|
||||
return true;
|
||||
}
|
||||
if (!aurora_begin_frame()) {
|
||||
return false;
|
||||
}
|
||||
SetAuroraFrameGxBegun(true);
|
||||
ApplyPendingMkwDynamicAspectSurface();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ends the frame; with the GX worker, the game thread joins it here so the next frame never
|
||||
// overlaps guest memory the worker may still read.
|
||||
inline void EndAuroraFrame() {
|
||||
if (GxWorker::ShouldDefer()) {
|
||||
AuroraImguiFrame* imgui = aurora_end_frame_window();
|
||||
GxWorker::Submit([imgui] { GxWorkerEndFrame(imgui); });
|
||||
GxWorker::Sync();
|
||||
return;
|
||||
}
|
||||
aurora_end_frame();
|
||||
SetAuroraFrameGxBegun(false);
|
||||
}
|
||||
|
||||
// Poll Aurora events and update cached window/framebuffer dimensions.
|
||||
inline void UpdateAuroraAndProcessEvents() {
|
||||
ProcessAuroraEvents(aurora_update());
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
// HCI and L2CAP fields are little-endian.
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace Bluetooth {
|
||||
|
||||
inline uint16_t Le16(const uint8_t* p) {
|
||||
return static_cast<uint16_t>(p[0] | (p[1] << 8));
|
||||
}
|
||||
|
||||
inline void PutLe16(std::vector<uint8_t>& out, uint16_t v) {
|
||||
out.push_back(static_cast<uint8_t>(v));
|
||||
out.push_back(static_cast<uint8_t>(v >> 8));
|
||||
}
|
||||
|
||||
} // namespace Bluetooth
|
||||
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
// Emulated Bluetooth controller behind the Wii's internal USB module
|
||||
// (/dev/usb/oh1/57e/305). The game's own Broadcom stack (BTE), WUD, WPAD and KPAD
|
||||
// talk HCI to it; each of the four virtual Wii Remotes it hosts is backed by a
|
||||
// physical remote on the PC (see RemoteHost), whose HID reports are passed
|
||||
// through unchanged. Pure host code: no guest memory, so it is unit-testable.
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Bluetooth {
|
||||
|
||||
// Bluetooth device address in HCI (least significant byte first) order.
|
||||
using BdAddr = std::array<uint8_t, 6>;
|
||||
|
||||
inline constexpr size_t kRemoteSlots = 4;
|
||||
|
||||
enum class LinkState : uint8_t {
|
||||
Down, // no baseband connection to the game
|
||||
Connecting, // baseband up, HID channels being opened
|
||||
Ready, // HID control and interrupt channels open
|
||||
};
|
||||
|
||||
// The physical side of the virtual remotes. Called from the thread that drives
|
||||
// the Controller; implementations synchronize with their own I/O threads.
|
||||
class RemoteHost {
|
||||
public:
|
||||
virtual ~RemoteHost() = default;
|
||||
// A physical remote is attached to `slot` and can carry a connection.
|
||||
virtual bool IsAttached(size_t slot) const = 0;
|
||||
// True once each time the remote on `slot` asks to connect (it was just
|
||||
// attached, or a button was pressed while it had no connection).
|
||||
virtual bool ConsumeWake(size_t slot) = 0;
|
||||
// Next input report (report id first, no HID header), false when none.
|
||||
virtual bool PopInputReport(size_t slot, std::vector<uint8_t>& report) = 0;
|
||||
// Output report from the game (report id first, no HID header).
|
||||
virtual void SendOutputReport(size_t slot, const uint8_t* report, size_t size) = 0;
|
||||
virtual void OnLinkStateChanged(size_t slot, LinkState state) = 0;
|
||||
// The game ended a working connection; a real remote powers off here.
|
||||
virtual void OnGuestDisconnect(size_t slot) = 0;
|
||||
// The game is searching for remotes (its 1+2 / reconnect screens).
|
||||
virtual void OnGuestInquiry(bool active) = 0;
|
||||
// "Nintendo RVL-CNT-01" or "Nintendo RVL-CNT-01-TR".
|
||||
virtual const char* DeviceName(size_t slot) const = 0;
|
||||
};
|
||||
|
||||
class RemoteLink;
|
||||
|
||||
class Controller {
|
||||
public:
|
||||
explicit Controller(RemoteHost& host);
|
||||
~Controller();
|
||||
Controller(const Controller&) = delete;
|
||||
Controller& operator=(const Controller&) = delete;
|
||||
|
||||
// The guest opened / closed the USB device.
|
||||
void Open(uint64_t nowUs);
|
||||
void Close();
|
||||
bool IsOpen() const { return m_open; }
|
||||
|
||||
// Raw HCI command packet (opcode first, no H4 byte).
|
||||
void ExecuteCommand(const uint8_t* packet, size_t size, uint64_t nowUs);
|
||||
// Raw ACL packet from the guest (handle first).
|
||||
void ReceiveAcl(const uint8_t* packet, size_t size, uint64_t nowUs);
|
||||
// Advances timers, connection attempts, input forwarding and flow control.
|
||||
void Tick(uint64_t nowUs);
|
||||
|
||||
bool HasEvent() const { return !m_events.empty(); }
|
||||
bool PopEvent(std::vector<uint8_t>& event);
|
||||
// ACL data is only handed out once every queued event has been delivered,
|
||||
// so the guest always sees a connection before data on it.
|
||||
bool PopAcl(std::vector<uint8_t>& packet);
|
||||
size_t AclQueued() const { return m_acl.size(); }
|
||||
|
||||
LinkState SlotLinkState(size_t slot) const;
|
||||
bool PageScanEnabled() const { return (m_scanEnable & 0x02) != 0; }
|
||||
bool InquiryActive() const { return m_inquiry.active; }
|
||||
|
||||
static BdAddr LocalAddress();
|
||||
static BdAddr RemoteAddress(size_t slot);
|
||||
static uint16_t ConnectionHandle(size_t slot);
|
||||
|
||||
// Used by RemoteLink.
|
||||
void QueueEvent(std::vector<uint8_t> event);
|
||||
void QueueAcl(uint16_t handle, const std::vector<uint8_t>& l2capFrame);
|
||||
RemoteHost& Host() { return m_host; }
|
||||
uint32_t PageTimeoutUs() const { return static_cast<uint32_t>(m_pageTimeoutSlots) * 625u; }
|
||||
|
||||
private:
|
||||
struct StoredKey {
|
||||
BdAddr addr{};
|
||||
std::array<uint8_t, 16> key{};
|
||||
};
|
||||
struct Inquiry {
|
||||
bool active = false;
|
||||
uint64_t endUs = 0;
|
||||
uint8_t maxResponses = 0;
|
||||
uint8_t reported = 0;
|
||||
std::array<bool, kRemoteSlots> slotReported{};
|
||||
};
|
||||
struct Reassembly {
|
||||
std::vector<uint8_t> frame;
|
||||
size_t expected = 0;
|
||||
};
|
||||
// A reply the controller sends after a delay (page / name request timeouts).
|
||||
struct PendingEvent {
|
||||
uint64_t dueUs = 0;
|
||||
std::vector<uint8_t> event;
|
||||
};
|
||||
|
||||
void ResetState(bool keepStoredKeys);
|
||||
void CommandComplete(uint16_t opcode, const std::vector<uint8_t>& params);
|
||||
void CommandStatus(uint16_t opcode, uint8_t status);
|
||||
void TickInquiry(uint64_t nowUs);
|
||||
void FinishInquiry();
|
||||
void SendNumberOfCompletedPackets();
|
||||
int SlotForAddress(const BdAddr& addr) const;
|
||||
int SlotForHandle(uint16_t handle) const;
|
||||
const StoredKey* FindStoredKey(const BdAddr& addr) const;
|
||||
|
||||
void HandleLinkControl(uint16_t opcode, const uint8_t* p, size_t n, uint64_t nowUs);
|
||||
void HandleLinkPolicy(uint16_t opcode, const uint8_t* p, size_t n);
|
||||
void HandleControllerBaseband(uint16_t opcode, const uint8_t* p, size_t n, uint64_t nowUs);
|
||||
void HandleInformational(uint16_t opcode, const uint8_t* p, size_t n);
|
||||
void HandleStatusParameters(uint16_t opcode, const uint8_t* p, size_t n);
|
||||
void HandleVendor(uint16_t opcode, const uint8_t* p, size_t n);
|
||||
void UnknownCommand(uint16_t opcode);
|
||||
|
||||
RemoteHost& m_host;
|
||||
bool m_open = false;
|
||||
uint8_t m_scanEnable = 0;
|
||||
uint16_t m_pageTimeoutSlots = 0x2000;
|
||||
uint8_t m_inquiryMode = 0;
|
||||
std::array<uint8_t, 3> m_classOfDevice{};
|
||||
std::array<char, 248> m_localName{};
|
||||
Inquiry m_inquiry;
|
||||
bool m_hostInquiryNotified = false;
|
||||
std::vector<StoredKey> m_storedKeys;
|
||||
std::array<std::unique_ptr<RemoteLink>, kRemoteSlots> m_links;
|
||||
std::array<uint16_t, kRemoteSlots> m_completedPackets{};
|
||||
std::array<Reassembly, kRemoteSlots> m_reassembly;
|
||||
std::array<bool, kRemoteSlots> m_authPending{};
|
||||
std::vector<PendingEvent> m_pending;
|
||||
std::deque<std::vector<uint8_t>> m_events;
|
||||
std::deque<std::vector<uint8_t>> m_acl;
|
||||
};
|
||||
|
||||
} // namespace Bluetooth
|
||||
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
// One emulated Wii Remote as seen from the controller: its baseband connection,
|
||||
// L2CAP channels (SDP, HID control, HID interrupt) and the HID transaction layer
|
||||
// that carries the physical remote's reports.
|
||||
|
||||
#include "bluetooth/bt_controller.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace Bluetooth {
|
||||
|
||||
class RemoteLink {
|
||||
public:
|
||||
enum class Baseband : uint8_t {
|
||||
Idle,
|
||||
Requesting, // Connection Request sent, waiting for the guest to accept
|
||||
Paging, // the guest paged this remote (Create Connection)
|
||||
Connected,
|
||||
};
|
||||
|
||||
RemoteLink(Controller& controller, size_t slot);
|
||||
|
||||
// Drops every connection without telling the guest (controller reset/close).
|
||||
void Reset();
|
||||
void Tick(uint64_t nowUs);
|
||||
|
||||
// Remote side wants a connection and should be offered to the guest's inquiry.
|
||||
bool Discoverable() const;
|
||||
bool ConnectWanted() const { return m_connectWanted; }
|
||||
|
||||
// Guest accepted / rejected this remote's Connection Request.
|
||||
bool IsRequesting() const { return m_baseband == Baseband::Requesting; }
|
||||
void OnAccepted(uint8_t role);
|
||||
void OnRejected(uint8_t reason, uint64_t nowUs);
|
||||
// Guest paged this remote. Returns false when the remote cannot answer.
|
||||
bool OnPaged(uint64_t nowUs);
|
||||
// Baseband went down. `byGuest` = the guest's HCI Disconnect.
|
||||
void OnDisconnected(bool byGuest, uint64_t nowUs);
|
||||
// The physical remote vanished while connected: tells the guest.
|
||||
void DropFromRemote(uint8_t reason);
|
||||
|
||||
void ReceiveL2cap(const uint8_t* frame, size_t size);
|
||||
|
||||
Baseband BasebandState() const { return m_baseband; }
|
||||
LinkState State() const { return m_state; }
|
||||
|
||||
private:
|
||||
struct Channel {
|
||||
uint16_t psm = 0;
|
||||
uint16_t localCid = 0;
|
||||
uint16_t remoteCid = 0;
|
||||
uint16_t remoteMtu = 672;
|
||||
bool weInitiated = false;
|
||||
bool connected = false; // connection response success seen/sent
|
||||
bool configSent = false; // our Configuration Request is out
|
||||
bool localConfigured = false; // the guest accepted our configuration
|
||||
bool remoteConfigured = false; // we accepted the guest's configuration
|
||||
uint8_t connectIdent = 0;
|
||||
uint8_t configIdent = 0;
|
||||
uint64_t requestUs = 0;
|
||||
std::vector<uint8_t> pendingOptions; // continued Configuration Request
|
||||
bool Open() const { return connected && localConfigured && remoteConfigured; }
|
||||
};
|
||||
|
||||
Channel* FindByLocal(uint16_t cid);
|
||||
Channel* FindByPsm(uint16_t psm);
|
||||
uint16_t AllocateCid() const;
|
||||
uint8_t NextIdent();
|
||||
|
||||
void SendSignal(uint8_t code, uint8_t ident, const std::vector<uint8_t>& data);
|
||||
void SendData(const Channel& channel, const std::vector<uint8_t>& payload);
|
||||
void RequestChannel(uint16_t psm, uint64_t nowUs);
|
||||
void SendConfigRequest(Channel& channel);
|
||||
void HandleSignalling(const uint8_t* data, size_t size);
|
||||
void HandleConnectionRequest(uint8_t ident, const uint8_t* p, size_t n);
|
||||
void HandleConnectionResponse(uint8_t ident, const uint8_t* p, size_t n);
|
||||
void HandleConfigurationRequest(uint8_t ident, const uint8_t* p, size_t n);
|
||||
void HandleConfigurationResponse(uint8_t ident, const uint8_t* p, size_t n);
|
||||
void HandleDisconnectionRequest(uint8_t ident, const uint8_t* p, size_t n);
|
||||
void HandleDisconnectionResponse(const uint8_t* p, size_t n);
|
||||
void HandleChannelData(Channel& channel, const uint8_t* data, size_t size);
|
||||
void HandleHidControl(Channel& channel, const uint8_t* data, size_t size);
|
||||
void UpdateState();
|
||||
void SetState(LinkState state);
|
||||
void ForwardInput();
|
||||
void SendInput(const Channel& interrupt, const std::vector<uint8_t>& report);
|
||||
void ClearConnection();
|
||||
|
||||
Controller& m_controller;
|
||||
size_t m_slot;
|
||||
Baseband m_baseband = Baseband::Idle;
|
||||
LinkState m_state = LinkState::Down;
|
||||
bool m_connectWanted = false;
|
||||
bool m_guestInitiated = false;
|
||||
bool m_everReady = false;
|
||||
uint8_t m_ident = 0;
|
||||
uint64_t m_stateSinceUs = 0;
|
||||
uint64_t m_retryAtUs = 0;
|
||||
uint64_t m_nowUs = 0;
|
||||
std::vector<Channel> m_channels;
|
||||
std::vector<uint8_t> m_report;
|
||||
};
|
||||
|
||||
} // namespace Bluetooth
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
// SDP server of an emulated Wii Remote: the HID service record (0x00010000)
|
||||
// and the Device ID record (0x00010001) a real RVL-CNT-01 publishes.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace Bluetooth::Sdp {
|
||||
|
||||
// The Wii Remote's HID report descriptor, as the game also embeds it.
|
||||
const std::vector<uint8_t>& WiiRemoteHidDescriptor();
|
||||
|
||||
// Answers one SDP request PDU. `maxResponse` bounds the PDU returned (the
|
||||
// guest's L2CAP MTU on the SDP channel).
|
||||
std::vector<uint8_t> HandleRequest(const uint8_t* pdu, size_t size, size_t maxResponse);
|
||||
|
||||
} // namespace Bluetooth::Sdp
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <aurora/event.h>
|
||||
|
||||
// Lets the keyboard type into Retro Rewind's friend code entry page.
|
||||
namespace FriendCodeKeyboard {
|
||||
void HandleEvents(const AuroraEvent* events) noexcept;
|
||||
} // namespace FriendCodeKeyboard
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
// Bump whenever guest_abi/, isa/ or ppc_isa_memory.h changes what translated code compiles against.
|
||||
#define MKW_GUEST_ABI_VERSION 1
|
||||
#define MKW_GUEST_ABI_VERSION 2
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "guest_abi/cpu_context.h"
|
||||
#include "guest_abi/exec_guard.h"
|
||||
#include "guest_abi/guest_region.h"
|
||||
#include "isa/ppc_isa_config.h"
|
||||
#include "isa/ppc_isa_cr.h"
|
||||
|
||||
@@ -24,7 +25,7 @@ uint32_t FilterScnRendererPathMaskOption(uint32_t pathMask) noexcept;
|
||||
// (used for the path mask filtering in ScnRenderer::createPath: depth of
|
||||
// field is always removed, bloom when the user disabled it)
|
||||
inline void ApplyRuntimeCallOptions(uint32_t target, CpuContext* ctx) {
|
||||
if (target == 0x8023BD38u) {
|
||||
if (::GuestRegion::Is<0x8023BD38u>(target)) {
|
||||
// ScnRenderer::createPath receives the post-processing path mask in r4.
|
||||
ctx->gpr[4] = FilterScnRendererPathMaskOption(ctx->gpr[4]);
|
||||
}
|
||||
@@ -242,13 +243,11 @@ private:
|
||||
std::array<uint32_t, 18> saved_;
|
||||
};
|
||||
|
||||
// Guest address of OSLoadContext, which models rfi-style context restoration
|
||||
// and intentionally replaces the full guest register file instead of returning
|
||||
// like a normal ABI call. It can never be guarded.
|
||||
inline constexpr uint32_t kOSLoadContextAddress = 0x801A1F58u;
|
||||
|
||||
// OSLoadContext models rfi-style context restoration and intentionally replaces
|
||||
// the full guest register file instead of returning like a normal ABI call. It
|
||||
// can never be guarded.
|
||||
inline bool ShouldPreserveNonvolatileGprsForRawCpuCall(const TranslatedFunctionInfo* info) noexcept {
|
||||
return info->kind == FunctionKind::Native && info->address != kOSLoadContextAddress;
|
||||
return info->kind == FunctionKind::Native && info->address != MKW_GADDR(801A1F58);
|
||||
}
|
||||
|
||||
inline uint32_t NonvolatileFprGuardMaskFor(const TranslatedFunctionInfo* info) noexcept {
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
// Guest addresses are named by their PAL (RMCP01) address. Which address that identity has
|
||||
// depends on the region the game was translated from, which runtime/game supplies at link
|
||||
// time, so one compiled runtime serves every region.
|
||||
//
|
||||
// MKW_GADDR(801A9E84) address of that identity in the running region
|
||||
// MKW_GADDR_CT(801A9E84) same, as a constant expression; translated code only
|
||||
//
|
||||
// guest_versions.inc maps PAL ranges to the other regions. A PAL address outside every range
|
||||
// of a region does not compile.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace GuestRegion {
|
||||
|
||||
enum class Id : uint8_t {
|
||||
Pal = 0,
|
||||
NtscU = 1,
|
||||
NtscJ = 2,
|
||||
};
|
||||
inline constexpr size_t kCount = 3;
|
||||
|
||||
struct Row {
|
||||
uint32_t address[kCount];
|
||||
|
||||
constexpr uint32_t In(Id region) const { return address[static_cast<size_t>(region)]; }
|
||||
};
|
||||
|
||||
struct Range {
|
||||
Id region;
|
||||
uint32_t first;
|
||||
uint32_t last;
|
||||
int32_t delta;
|
||||
};
|
||||
|
||||
inline constexpr Range kRanges[] = {
|
||||
#define MKW_REGION_RANGE(region, first, last, delta) {Id::region, 0x##first##u, 0x##last##u, delta},
|
||||
#include "guest_abi/guest_versions.inc"
|
||||
#undef MKW_REGION_RANGE
|
||||
};
|
||||
inline constexpr size_t kRangeCount = sizeof(kRanges) / sizeof(kRanges[0]);
|
||||
|
||||
constexpr bool Precedes(Id region, uint32_t address, const Range& range) {
|
||||
return region < range.region || (region == range.region && address < range.first);
|
||||
}
|
||||
|
||||
constexpr bool RangesAreOrdered() {
|
||||
for (size_t i = 0; i < kRangeCount; ++i) {
|
||||
const Range& range = kRanges[i];
|
||||
if (range.region == Id::Pal || range.first > range.last) {
|
||||
return false;
|
||||
}
|
||||
if (i != 0 && !Precedes(kRanges[i - 1].region, kRanges[i - 1].last, range)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static_assert(RangesAreOrdered(), "guest_versions.inc ranges must be sorted by region and PAL address without overlaps");
|
||||
|
||||
struct Mapping {
|
||||
bool found;
|
||||
uint32_t address;
|
||||
};
|
||||
|
||||
constexpr Mapping Map(uint32_t pal, Id region) {
|
||||
if (region == Id::Pal) {
|
||||
return {true, pal};
|
||||
}
|
||||
size_t lo = 0;
|
||||
size_t hi = kRangeCount;
|
||||
while (lo < hi) {
|
||||
const size_t mid = lo + (hi - lo) / 2;
|
||||
if (Precedes(region, pal, kRanges[mid])) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
if (lo == 0) {
|
||||
return {false, 0};
|
||||
}
|
||||
const Range& range = kRanges[lo - 1];
|
||||
if (range.region != region || pal > range.last) {
|
||||
return {false, 0};
|
||||
}
|
||||
return {true, static_cast<uint32_t>(static_cast<int64_t>(pal) + range.delta)};
|
||||
}
|
||||
|
||||
constexpr Row RowOf(uint32_t pal) {
|
||||
const Mapping ntscU = Map(pal, Id::NtscU);
|
||||
const Mapping ntscJ = Map(pal, Id::NtscJ);
|
||||
return ntscU.found && ntscJ.found ? Row{{pal, ntscU.address, ntscJ.address}}
|
||||
: throw "PAL address outside guest_abi/guest_versions.inc";
|
||||
}
|
||||
|
||||
} // namespace GuestRegion
|
||||
|
||||
// Defined by runtime/game for the translated region.
|
||||
extern "C" const uint8_t kMkwGuestRegion;
|
||||
|
||||
namespace GuestRegion {
|
||||
|
||||
inline Id Current() noexcept { return static_cast<Id>(kMkwGuestRegion); }
|
||||
|
||||
template <uint32_t Pal>
|
||||
inline uint32_t Address() noexcept {
|
||||
constexpr Row row = RowOf(Pal);
|
||||
if constexpr (row.address[1] == Pal && row.address[2] == Pal) {
|
||||
return Pal;
|
||||
} else {
|
||||
return row.In(Current());
|
||||
}
|
||||
}
|
||||
|
||||
// Folds to false at compile time for a constant target that no region uses for Pal.
|
||||
template <uint32_t Pal>
|
||||
inline bool Is(uint32_t target) noexcept {
|
||||
constexpr Row row = RowOf(Pal);
|
||||
return (target == row.address[0] || target == row.address[1] || target == row.address[2]) &&
|
||||
target == row.In(Current());
|
||||
}
|
||||
|
||||
[[noreturn]] void FatalUnknownAddress(uint32_t pal) noexcept;
|
||||
|
||||
// For hand-written registrations, which name the PAL address.
|
||||
inline uint32_t Translate(uint32_t pal) noexcept {
|
||||
const Mapping mapping = Map(pal, Current());
|
||||
if (!mapping.found) {
|
||||
FatalUnknownAddress(pal);
|
||||
}
|
||||
return mapping.address;
|
||||
}
|
||||
|
||||
struct Facts {
|
||||
const char* gameId;
|
||||
char letter;
|
||||
uint32_t gameCode;
|
||||
uint32_t viTvFormat;
|
||||
uint32_t mem1ArenaLo;
|
||||
// setting.txt defaults for a new NAND
|
||||
const char* area;
|
||||
const char* model;
|
||||
const char* productCode;
|
||||
const char* video;
|
||||
const char* gameRegion;
|
||||
};
|
||||
|
||||
inline constexpr Facts kFacts[kCount] = {
|
||||
{"RMCP01", 'P', 0x524D4350u, 1u, 0x80399180u, "EUR", "RVL-001(EUR)", "LEH", "PAL", "EU"},
|
||||
{"RMCE01", 'E', 0x524D4345u, 0u, 0x80394E00u, "USA", "RVL-001(USA)", "LU", "NTSC", "US"},
|
||||
{"RMCJ01", 'J', 0x524D434Au, 0u, 0x80398B00u, "JPN", "RVL-001(JPN)", "LJ", "NTSC", "JP"},
|
||||
};
|
||||
|
||||
inline const Facts& CurrentFacts() noexcept { return kFacts[static_cast<size_t>(Current())]; }
|
||||
|
||||
} // namespace GuestRegion
|
||||
|
||||
#define MKW_GADDR(pal) (::GuestRegion::Address<0x##pal##u>())
|
||||
|
||||
#if defined(MKW_TRANSLATED_GUEST_REGION)
|
||||
#define MKW_GADDR_CT(pal) \
|
||||
(::GuestRegion::RowOf(0x##pal##u).In(static_cast<::GuestRegion::Id>(MKW_TRANSLATED_GUEST_REGION)))
|
||||
#endif
|
||||
+416
@@ -0,0 +1,416 @@
|
||||
// PAL address ranges -> address in each other region, in the style of Retro Rewind's
|
||||
// versions.txt: MKW_REGION_RANGE(region, first PAL, last PAL (inclusive), delta).
|
||||
// PAL maps to itself. Code ranges come from mkw-sp's port.py chunk table (MIT, Pablo Stebler),
|
||||
// data ranges from Retro Rewind's versions.txt plus the verified data_addresses.txt anchors.
|
||||
// An address outside every range has no known counterpart and fails to compile.
|
||||
|
||||
MKW_REGION_RANGE(NtscU, 80000000, 8000802F, +0x0)
|
||||
MKW_REGION_RANGE(NtscU, 8000808C, 8000ADBB, -0x40)
|
||||
MKW_REGION_RANGE(NtscU, 8000AF24, 8000B6B3, -0xA0)
|
||||
MKW_REGION_RANGE(NtscU, 8000B6B4, 8000C173, +0x15994)
|
||||
MKW_REGION_RANGE(NtscU, 8000C174, 80021BA7, -0xB60)
|
||||
MKW_REGION_RANGE(NtscU, 80021BA8, 80225F1B, -0xA0)
|
||||
MKW_REGION_RANGE(NtscU, 802261F8, 802402DF, -0x384)
|
||||
MKW_REGION_RANGE(NtscU, 802402E0, 80240E17, +0x3F1C)
|
||||
MKW_REGION_RANGE(NtscU, 80240E18, 80244DD3, -0xEBC)
|
||||
MKW_REGION_RANGE(NtscU, 80244DDA, 80244EBF, -0xA0)
|
||||
MKW_REGION_RANGE(NtscU, 80244EC0, 80244ECF, -0x80)
|
||||
MKW_REGION_RANGE(NtscU, 802572BF, 802575DF, -0x320)
|
||||
MKW_REGION_RANGE(NtscU, 80257E64, 80258184, -0x320)
|
||||
MKW_REGION_RANGE(NtscU, 80276054, 8029CEFB, -0x4340)
|
||||
MKW_REGION_RANGE(NtscU, 802A3D60, 802A407F, -0x4368)
|
||||
MKW_REGION_RANGE(NtscU, 802A4080, 80386F27, -0x4380)
|
||||
MKW_REGION_RANGE(NtscU, 80386F48, 80386F8F, -0x4388)
|
||||
MKW_REGION_RANGE(NtscU, 80386FA0, 80388BE3, -0x4380)
|
||||
MKW_REGION_RANGE(NtscU, 80388DD0, 80388DF7, -0x4388)
|
||||
MKW_REGION_RANGE(NtscU, 80389140, 8038917B, -0x4380)
|
||||
MKW_REGION_RANGE(NtscU, 80399200, 80399400, -0x4380)
|
||||
MKW_REGION_RANGE(NtscU, 805103B4, 80510A8F, -0x4380)
|
||||
MKW_REGION_RANGE(NtscU, 80510B84, 8052D297, -0x4474)
|
||||
MKW_REGION_RANGE(NtscU, 8052D298, 8052D96B, +0x22568)
|
||||
MKW_REGION_RANGE(NtscU, 8052D96C, 8053D97B, -0x4B48)
|
||||
MKW_REGION_RANGE(NtscU, 8053D97C, 8053E36F, +0x12558)
|
||||
MKW_REGION_RANGE(NtscU, 8053E370, 8054FB2B, -0x553C)
|
||||
MKW_REGION_RANGE(NtscU, 8054FB2C, 80550547, +0xD9C)
|
||||
MKW_REGION_RANGE(NtscU, 80550548, 805537CB, -0x5F58)
|
||||
MKW_REGION_RANGE(NtscU, 80553894, 8055572B, -0x6020)
|
||||
MKW_REGION_RANGE(NtscU, 8055572C, 8056AB6B, -0x4380)
|
||||
MKW_REGION_RANGE(NtscU, 8056AB6C, 8056B63B, +0x4357C)
|
||||
MKW_REGION_RANGE(NtscU, 8056B63C, 8057402F, -0x4E50)
|
||||
MKW_REGION_RANGE(NtscU, 80574030, 805758AB, +0x3AB88)
|
||||
MKW_REGION_RANGE(NtscU, 80575A44, 80583F2B, -0x6864)
|
||||
MKW_REGION_RANGE(NtscU, 80583F2C, 8059B5A3, -0x6824)
|
||||
MKW_REGION_RANGE(NtscU, 8059B5A4, 8059EAA3, +0x14FE8)
|
||||
MKW_REGION_RANGE(NtscU, 8059EAA4, 805A0557, -0x9D24)
|
||||
MKW_REGION_RANGE(NtscU, 805A068C, 805A1477, +0x1343C)
|
||||
MKW_REGION_RANGE(NtscU, 805A1488, 805A1863, +0x1342C)
|
||||
MKW_REGION_RANGE(NtscU, 805A1864, 805A9EDB, -0xB038)
|
||||
MKW_REGION_RANGE(NtscU, 805A9EDC, 805AB573, -0xB030)
|
||||
MKW_REGION_RANGE(NtscU, 805AB574, 805ABC8F, -0xAF70)
|
||||
MKW_REGION_RANGE(NtscU, 805ABC90, 805ADD97, -0xAF68)
|
||||
MKW_REGION_RANGE(NtscU, 805ADD98, 805B900F, -0xAF28)
|
||||
MKW_REGION_RANGE(NtscU, 805B9010, 805B92FF, -0x4380)
|
||||
MKW_REGION_RANGE(NtscU, 805B9300, 805BAB3F, +0x664F4)
|
||||
MKW_REGION_RANGE(NtscU, 805BAB40, 805BB2B7, -0x5BC0)
|
||||
MKW_REGION_RANGE(NtscU, 805BB2B8, 805BD2D7, +0x65D7C)
|
||||
MKW_REGION_RANGE(NtscU, 805BD2D8, 805BD2E3, -0x697C)
|
||||
MKW_REGION_RANGE(NtscU, 805BD2E4, 805BD39B, +0x65D70)
|
||||
MKW_REGION_RANGE(NtscU, 805BD39C, 805BE5FF, -0x7CA4)
|
||||
MKW_REGION_RANGE(NtscU, 805BE600, 805BE61B, -0x7C98)
|
||||
MKW_REGION_RANGE(NtscU, 805BE66C, 805BE7F3, +0x64AA0)
|
||||
MKW_REGION_RANGE(NtscU, 805BE7F4, 805BE84B, -0x7124)
|
||||
MKW_REGION_RANGE(NtscU, 805BE84C, 805BED67, +0x64A48)
|
||||
MKW_REGION_RANGE(NtscU, 805BED68, 805BED73, -0x7948)
|
||||
MKW_REGION_RANGE(NtscU, 805BED74, 805BF2D7, +0x64A3C)
|
||||
MKW_REGION_RANGE(NtscU, 805BF2D8, 805BF2DB, -0x613C)
|
||||
MKW_REGION_RANGE(NtscU, 805BF3CC, 805BFE1B, -0x89FC)
|
||||
MKW_REGION_RANGE(NtscU, 805BFE1C, 805C00BF, -0x89F0)
|
||||
MKW_REGION_RANGE(NtscU, 805C00C0, 805C1B33, -0x8998)
|
||||
MKW_REGION_RANGE(NtscU, 805C1B34, 805C35F7, -0x8994)
|
||||
MKW_REGION_RANGE(NtscU, 805C35F8, 805C3BCB, +0x60810)
|
||||
MKW_REGION_RANGE(NtscU, 805C3BCC, 805C4767, -0x8F68)
|
||||
MKW_REGION_RANGE(NtscU, 805C4768, 805C579B, +0x5FC74)
|
||||
MKW_REGION_RANGE(NtscU, 805C57B0, 805CADDB, -0x9FB0)
|
||||
MKW_REGION_RANGE(NtscU, 805CADDC, 805CC0A7, +0x5A648)
|
||||
MKW_REGION_RANGE(NtscU, 805CC1B4, 805CD94B, +0x5A5A4)
|
||||
MKW_REGION_RANGE(NtscU, 805CD94C, 805D1887, -0xCB20)
|
||||
MKW_REGION_RANGE(NtscU, 805D1888, 805D1E83, +0x56668)
|
||||
MKW_REGION_RANGE(NtscU, 805D1E84, 805D7F77, -0xD11C)
|
||||
MKW_REGION_RANGE(NtscU, 805D7F78, 805DBFDB, +0x50574)
|
||||
MKW_REGION_RANGE(NtscU, 805DBFDC, 805DC033, +0x2A620)
|
||||
MKW_REGION_RANGE(NtscU, 805DC034, 805DE837, +0x5051C)
|
||||
MKW_REGION_RANGE(NtscU, 805DE838, 805DE843, +0x5C00)
|
||||
MKW_REGION_RANGE(NtscU, 805DE844, 805E0C37, +0x50510)
|
||||
MKW_REGION_RANGE(NtscU, 805E0C38, 805E6B4F, -0x15DDC)
|
||||
MKW_REGION_RANGE(NtscU, 805E6B50, 805E745F, +0x4A5F8)
|
||||
MKW_REGION_RANGE(NtscU, 805E7460, 805EA77F, -0x166EC)
|
||||
MKW_REGION_RANGE(NtscU, 805EA780, 805EEB67, +0x472D8)
|
||||
MKW_REGION_RANGE(NtscU, 805EEB68, 805F2D23, -0x1AAD4)
|
||||
MKW_REGION_RANGE(NtscU, 805F2D24, 805F8B33, +0x4311C)
|
||||
MKW_REGION_RANGE(NtscU, 805F8B34, 805FB5BB, -0x208E4)
|
||||
MKW_REGION_RANGE(NtscU, 805FB5BC, 805FB81F, +0x40694)
|
||||
MKW_REGION_RANGE(NtscU, 805FB820, 805FD517, -0x20B48)
|
||||
MKW_REGION_RANGE(NtscU, 805FD518, 806012AF, +0x3E99C)
|
||||
MKW_REGION_RANGE(NtscU, 806012B0, 80608D17, -0x248E0)
|
||||
MKW_REGION_RANGE(NtscU, 80608D18, 8060A72B, -0x248D4)
|
||||
MKW_REGION_RANGE(NtscU, 8060A72C, 8061186B, +0x35520)
|
||||
MKW_REGION_RANGE(NtscU, 8061186C, 80616843, -0x2BA14)
|
||||
MKW_REGION_RANGE(NtscU, 80616844, 8061AE6B, +0x30548)
|
||||
MKW_REGION_RANGE(NtscU, 8061AE6C, 8061E897, -0x3003C)
|
||||
MKW_REGION_RANGE(NtscU, 8061E898, 8061F6E7, +0x2CB1C)
|
||||
MKW_REGION_RANGE(NtscU, 8061F6E8, 80620CB3, -0x30E8C)
|
||||
MKW_REGION_RANGE(NtscU, 80620D7C, 80634F47, -0x30EB4)
|
||||
MKW_REGION_RANGE(NtscU, 80634F48, 80637493, -0x30E98)
|
||||
MKW_REGION_RANGE(NtscU, 80637494, 80637513, -0x30DF0)
|
||||
MKW_REGION_RANGE(NtscU, 80637514, 80637A2F, +0x14CF0)
|
||||
MKW_REGION_RANGE(NtscU, 80637AC0, 8063BCF7, -0x313A0)
|
||||
MKW_REGION_RANGE(NtscU, 8063BE40, 806453E3, -0x31420)
|
||||
MKW_REGION_RANGE(NtscU, 806453E4, 806467D3, +0x73A8)
|
||||
MKW_REGION_RANGE(NtscU, 806467D4, 8064A3F3, -0x32810)
|
||||
MKW_REGION_RANGE(NtscU, 8064A3F4, 8064AEE3, +0x3788)
|
||||
MKW_REGION_RANGE(NtscU, 8064AEF8, 80651BEB, -0x33314)
|
||||
MKW_REGION_RANGE(NtscU, 80651BEC, 806522FF, -0x3580)
|
||||
MKW_REGION_RANGE(NtscU, 80652300, 80653207, -0x33A14)
|
||||
MKW_REGION_RANGE(NtscU, 80653208, 8065B353, -0x4488)
|
||||
MKW_REGION_RANGE(NtscU, 8065B4E8, 8065C0EB, -0x461C)
|
||||
MKW_REGION_RANGE(NtscU, 8065C0EC, 8065FA13, +0xC88)
|
||||
MKW_REGION_RANGE(NtscU, 8065FA4C, 8065FE4B, -0x7F7C)
|
||||
MKW_REGION_RANGE(NtscU, 8065FE4C, 80662777, -0x7F3C)
|
||||
MKW_REGION_RANGE(NtscU, 80662778, 80663193, -0x20E4)
|
||||
MKW_REGION_RANGE(NtscU, 80663194, 80665537, -0x8958)
|
||||
MKW_REGION_RANGE(NtscU, 80665538, 80668813, +0xDC50)
|
||||
MKW_REGION_RANGE(NtscU, 80668814, 8067906B, -0x7764)
|
||||
MKW_REGION_RANGE(NtscU, 8067906C, 8067937F, -0x2C08)
|
||||
MKW_REGION_RANGE(NtscU, 80679380, 8067ABFF, -0x7A78)
|
||||
MKW_REGION_RANGE(NtscU, 8067AC00, 806BF4C7, -0x4488)
|
||||
MKW_REGION_RANGE(NtscU, 806BF4C8, 806BFB13, +0x22BE0)
|
||||
MKW_REGION_RANGE(NtscU, 806BFB14, 806C050B, -0x4AD4)
|
||||
MKW_REGION_RANGE(NtscU, 806C050C, 806C35A7, +0x221E8)
|
||||
MKW_REGION_RANGE(NtscU, 806C35A8, 806C3AA3, -0x7B70)
|
||||
MKW_REGION_RANGE(NtscU, 806C3AA4, 806C4ED3, +0x21CEC)
|
||||
MKW_REGION_RANGE(NtscU, 806C4EF0, 806C63A7, +0x21CD0)
|
||||
MKW_REGION_RANGE(NtscU, 806C63B0, 806C6B43, +0x21CC8)
|
||||
MKW_REGION_RANGE(NtscU, 806C6B44, 806CCE8F, -0xABF4)
|
||||
MKW_REGION_RANGE(NtscU, 806CCE90, 806CE81F, +0x1B97C)
|
||||
MKW_REGION_RANGE(NtscU, 806CE828, 806D02BB, -0xC58C)
|
||||
MKW_REGION_RANGE(NtscU, 806D02BC, 806D28F3, +0x19EE0)
|
||||
MKW_REGION_RANGE(NtscU, 806D2908, 806D5C5B, -0xEBD0)
|
||||
MKW_REGION_RANGE(NtscU, 806D5C60, 806D5ED7, +0x16B74)
|
||||
MKW_REGION_RANGE(NtscU, 806D5ED8, 806DA913, -0xEE40)
|
||||
MKW_REGION_RANGE(NtscU, 806DA914, 806DB183, +0x12144)
|
||||
MKW_REGION_RANGE(NtscU, 806DB184, 806DDA83, -0xF6B0)
|
||||
MKW_REGION_RANGE(NtscU, 806DDA84, 806DEB3F, +0xF844)
|
||||
MKW_REGION_RANGE(NtscU, 806DEB40, 806DF7CF, -0x1076C)
|
||||
MKW_REGION_RANGE(NtscU, 806DF7D0, 806DFD13, +0xEBB4)
|
||||
MKW_REGION_RANGE(NtscU, 806DFD14, 806E3A8B, -0x10CB0)
|
||||
MKW_REGION_RANGE(NtscU, 806E3A8C, 806E3E1F, +0xAE3C)
|
||||
MKW_REGION_RANGE(NtscU, 806E3E20, 806E95AF, -0x11044)
|
||||
MKW_REGION_RANGE(NtscU, 806E95B0, 806EC7BF, +0x56AC)
|
||||
MKW_REGION_RANGE(NtscU, 806EC7C0, 806ED53B, -0x1425C)
|
||||
MKW_REGION_RANGE(NtscU, 806ED53C, 806F62FB, -0x14254)
|
||||
MKW_REGION_RANGE(NtscU, 806F62FC, 806F7697, -0x4488)
|
||||
MKW_REGION_RANGE(NtscU, 806F77C4, 806F7A53, +0x1C2B4)
|
||||
MKW_REGION_RANGE(NtscU, 806F7AA8, 806F821F, +0x1C260)
|
||||
MKW_REGION_RANGE(NtscU, 806F8220, 806F8933, -0x5010)
|
||||
MKW_REGION_RANGE(NtscU, 806F8934, 806FA3FB, -0x4FBC)
|
||||
MKW_REGION_RANGE(NtscU, 806FA3FC, 806FE23F, -0x4F68)
|
||||
MKW_REGION_RANGE(NtscU, 806FE240, 806FE9DF, -0x4F10)
|
||||
MKW_REGION_RANGE(NtscU, 806FE9E0, 807001A3, +0x15AA0)
|
||||
MKW_REGION_RANGE(NtscU, 80700230, 80700473, +0x15A14)
|
||||
MKW_REGION_RANGE(NtscU, 80700474, 8070E7B7, -0x69A4)
|
||||
MKW_REGION_RANGE(NtscU, 8070E7B8, 8070F8B7, +0x76D0)
|
||||
MKW_REGION_RANGE(NtscU, 8070F8B8, 807179C3, -0x7AA4)
|
||||
MKW_REGION_RANGE(NtscU, 807179C4, 80717E33, -0x7A18)
|
||||
MKW_REGION_RANGE(NtscU, 80717E34, 807182E7, -0xEAC)
|
||||
MKW_REGION_RANGE(NtscU, 807182E8, 8071B86B, -0x7ECC)
|
||||
MKW_REGION_RANGE(NtscU, 8071B86C, 80726573, -0x4430)
|
||||
MKW_REGION_RANGE(NtscU, 80726574, 8072820B, +0x16068)
|
||||
MKW_REGION_RANGE(NtscU, 8072821C, 807285C7, +0x16058)
|
||||
MKW_REGION_RANGE(NtscU, 807285CC, 80729337, +0x16054)
|
||||
MKW_REGION_RANGE(NtscU, 80729350, 80729B87, -0x720C)
|
||||
MKW_REGION_RANGE(NtscU, 80729B88, 8072A893, -0x7204)
|
||||
MKW_REGION_RANGE(NtscU, 8072A894, 8072B95B, -0x71FC)
|
||||
MKW_REGION_RANGE(NtscU, 8072B95C, 8072DE63, -0x71EC)
|
||||
MKW_REGION_RANGE(NtscU, 8072DFCC, 8072FF5F, +0x113C4)
|
||||
MKW_REGION_RANGE(NtscU, 8072FF78, 80730197, +0x113AC)
|
||||
MKW_REGION_RANGE(NtscU, 80730198, 80730A7F, -0x93B0)
|
||||
MKW_REGION_RANGE(NtscU, 80730B40, 8073195F, +0x10A04)
|
||||
MKW_REGION_RANGE(NtscU, 80731960, 80735947, -0xA1B8)
|
||||
MKW_REGION_RANGE(NtscU, 80735948, 80738DB7, +0xCA1C)
|
||||
MKW_REGION_RANGE(NtscU, 80738DB8, 8073C54B, -0xD628)
|
||||
MKW_REGION_RANGE(NtscU, 8073C54C, 8073EDEF, +0x9288)
|
||||
MKW_REGION_RANGE(NtscU, 8073EDF0, 8074C4A7, -0xFECC)
|
||||
MKW_REGION_RANGE(NtscU, 8074C4A8, 8074D5B7, +0x20A04)
|
||||
MKW_REGION_RANGE(NtscU, 8074D5B8, 807519C7, -0x5540)
|
||||
MKW_REGION_RANGE(NtscU, 807519C8, 80754103, +0x1C5F4)
|
||||
MKW_REGION_RANGE(NtscU, 80754104, 80758BDB, -0x7C7C)
|
||||
MKW_REGION_RANGE(NtscU, 80758BDC, 8075DB23, +0x17B1C)
|
||||
MKW_REGION_RANGE(NtscU, 8075DB3C, 8075E78B, -0xCBDC)
|
||||
MKW_REGION_RANGE(NtscU, 8075E78C, 8075EAFB, +0x16ECC)
|
||||
MKW_REGION_RANGE(NtscU, 8075EAFC, 80765C93, -0xCF4C)
|
||||
MKW_REGION_RANGE(NtscU, 80765C94, 807678F3, +0xFD34)
|
||||
MKW_REGION_RANGE(NtscU, 807678F4, 80768D1F, -0xEBAC)
|
||||
MKW_REGION_RANGE(NtscU, 80768D20, 807693EB, +0xE908)
|
||||
MKW_REGION_RANGE(NtscU, 8076960C, 8076C85B, +0xE85C)
|
||||
MKW_REGION_RANGE(NtscU, 8076C85C, 8076EBDB, -0x126E8)
|
||||
MKW_REGION_RANGE(NtscU, 8076EBE0, 8076F2DB, +0xC4D8)
|
||||
MKW_REGION_RANGE(NtscU, 8076F2DC, 807726C3, -0x12DE8)
|
||||
MKW_REGION_RANGE(NtscU, 80772704, 80773C13, +0x90B4)
|
||||
MKW_REGION_RANGE(NtscU, 80773C1C, 8077439B, -0x14340)
|
||||
MKW_REGION_RANGE(NtscU, 807743A0, 807787EF, -0x14294)
|
||||
MKW_REGION_RANGE(NtscU, 807787F0, 8077902B, +0x4520)
|
||||
MKW_REGION_RANGE(NtscU, 8077902C, 8077CE87, -0x14AD0)
|
||||
MKW_REGION_RANGE(NtscU, 8077CEC8, 8077DF23, +0x684)
|
||||
MKW_REGION_RANGE(NtscU, 8077DF24, 807829D7, -0x15B2C)
|
||||
MKW_REGION_RANGE(NtscU, 807829D8, 80787D83, -0x4430)
|
||||
MKW_REGION_RANGE(NtscU, 80787D84, 8078C95F, +0x48598)
|
||||
MKW_REGION_RANGE(NtscU, 8078C960, 807A81B3, -0x900C)
|
||||
MKW_REGION_RANGE(NtscU, 807A81B4, 807A9D6F, +0x2CDC0)
|
||||
MKW_REGION_RANGE(NtscU, 807A9E50, 807A9E53, +0x2CCAC)
|
||||
MKW_REGION_RANGE(NtscU, 807A9EB8, 807AF13F, -0xACA8)
|
||||
MKW_REGION_RANGE(NtscU, 807AF140, 807B2EF7, +0x27A54)
|
||||
MKW_REGION_RANGE(NtscU, 807B2EF8, 807D976B, -0xEA60)
|
||||
MKW_REGION_RANGE(NtscU, 807D976C, 807D9B7F, +0x11E0)
|
||||
MKW_REGION_RANGE(NtscU, 807D9B98, 807DA5BF, -0xEE8C)
|
||||
MKW_REGION_RANGE(NtscU, 807DA5C0, 807DBCCB, +0x7B8)
|
||||
MKW_REGION_RANGE(NtscU, 807DBCCC, 807DC8C7, -0x10598)
|
||||
MKW_REGION_RANGE(NtscU, 807DC950, 807E093B, -0x10620)
|
||||
MKW_REGION_RANGE(NtscU, 807E093C, 807E251F, +0x5BDB4)
|
||||
MKW_REGION_RANGE(NtscU, 807E259C, 807E560F, -0x6090)
|
||||
MKW_REGION_RANGE(NtscU, 807E5654, 807E6413, -0x6070)
|
||||
MKW_REGION_RANGE(NtscU, 807E6414, 807E9C43, +0x57F28)
|
||||
MKW_REGION_RANGE(NtscU, 807E9C50, 807EDD97, -0x98AC)
|
||||
MKW_REGION_RANGE(NtscU, 807EDD98, 807EE23B, +0x53DD4)
|
||||
MKW_REGION_RANGE(NtscU, 807EE250, 807EE467, -0x9D64)
|
||||
MKW_REGION_RANGE(NtscU, 807EE474, 807EEA13, +0x53B9C)
|
||||
MKW_REGION_RANGE(NtscU, 807EEA14, 807EF9F3, -0xA310)
|
||||
MKW_REGION_RANGE(NtscU, 807EFD0C, 807F76EB, -0xA628)
|
||||
MKW_REGION_RANGE(NtscU, 807F76EC, 807F7BA3, +0x4B1FC)
|
||||
MKW_REGION_RANGE(NtscU, 807F7BC4, 807F890B, -0xAB00)
|
||||
MKW_REGION_RANGE(NtscU, 807F8968, 807F927F, +0x4A438)
|
||||
MKW_REGION_RANGE(NtscU, 807F9280, 807F9387, -0xB3F0)
|
||||
MKW_REGION_RANGE(NtscU, 807F9580, 807FAB57, +0x4A320)
|
||||
MKW_REGION_RANGE(NtscU, 807FAB58, 807FEB67, -0xCBC0)
|
||||
MKW_REGION_RANGE(NtscU, 807FEB68, 807FFADF, +0x46310)
|
||||
MKW_REGION_RANGE(NtscU, 807FFB20, 80805A0B, -0xDB78)
|
||||
MKW_REGION_RANGE(NtscU, 80805A0C, 8080761B, +0x403E4)
|
||||
MKW_REGION_RANGE(NtscU, 8080761C, 80809447, -0xF748)
|
||||
MKW_REGION_RANGE(NtscU, 80809448, 8080AD1F, +0x3E5B8)
|
||||
MKW_REGION_RANGE(NtscU, 8080AD20, 80811E47, -0x11020)
|
||||
MKW_REGION_RANGE(NtscU, 80811E48, 80813BD3, +0x37490)
|
||||
MKW_REGION_RANGE(NtscU, 80813BD4, 8081E283, -0x12DAC)
|
||||
MKW_REGION_RANGE(NtscU, 8081E284, 8081EFEB, +0x2CDE0)
|
||||
MKW_REGION_RANGE(NtscU, 8081EFEC, 8082E53F, -0x13B14)
|
||||
MKW_REGION_RANGE(NtscU, 8082E540, 8082E853, +0x1D88C)
|
||||
MKW_REGION_RANGE(NtscU, 8082E854, 8082F407, -0x13E28)
|
||||
MKW_REGION_RANGE(NtscU, 8082F408, 8083349F, +0x1CCD8)
|
||||
MKW_REGION_RANGE(NtscU, 808334E0, 80833AFF, -0x17F00)
|
||||
MKW_REGION_RANGE(NtscU, 80833B00, 80838E5F, -0x17EF8)
|
||||
MKW_REGION_RANGE(NtscU, 80838E60, 8083B0CB, +0x17358)
|
||||
MKW_REGION_RANGE(NtscU, 8083B0CC, 8083CB43, -0x1A178)
|
||||
MKW_REGION_RANGE(NtscU, 8083CB44, 8083D42B, +0x158E0)
|
||||
MKW_REGION_RANGE(NtscU, 8083D42C, 8084233F, -0x1AA60)
|
||||
MKW_REGION_RANGE(NtscU, 80842340, 808447AB, +0x109CC)
|
||||
MKW_REGION_RANGE(NtscU, 808447AC, 8084A99F, -0x1CED8)
|
||||
MKW_REGION_RANGE(NtscU, 8084A9A0, 8084D0DB, +0xA7E4)
|
||||
MKW_REGION_RANGE(NtscU, 8084D0DC, 80851D2B, -0x1F614)
|
||||
MKW_REGION_RANGE(NtscU, 80851D38, 80852C5F, +0x5B88)
|
||||
MKW_REGION_RANGE(NtscU, 80852C60, 80853CA3, -0x20548)
|
||||
MKW_REGION_RANGE(NtscU, 80853CA4, 808551EB, +0x4B50)
|
||||
MKW_REGION_RANGE(NtscU, 808551EC, 8085C3CB, -0x21A90)
|
||||
MKW_REGION_RANGE(NtscU, 8085C3CC, 8085E673, -0x2690)
|
||||
MKW_REGION_RANGE(NtscU, 8085E674, 8085F0AB, -0x23D24)
|
||||
MKW_REGION_RANGE(NtscU, 8085F0AC, 8085FFD3, -0x30C8)
|
||||
MKW_REGION_RANGE(NtscU, 8085FFD4, 80860F2B, -0x24C4C)
|
||||
MKW_REGION_RANGE(NtscU, 80860F2C, 80862E23, -0x4020)
|
||||
MKW_REGION_RANGE(NtscU, 80863234, 8086708B, -0x4430)
|
||||
MKW_REGION_RANGE(NtscU, 8086708C, 808676DF, -0x2354)
|
||||
MKW_REGION_RANGE(NtscU, 808676E0, 808697BB, -0x4A84)
|
||||
MKW_REGION_RANGE(NtscU, 808697BC, 8086A253, -0x4430)
|
||||
MKW_REGION_RANGE(NtscU, 8086A254, 8086C097, +0xC4A0)
|
||||
MKW_REGION_RANGE(NtscU, 8086C108, 8086C987, +0xC430)
|
||||
MKW_REGION_RANGE(NtscU, 8086CA40, 80872CA3, +0xC378)
|
||||
MKW_REGION_RANGE(NtscU, 80872CA4, 808739AF, -0xCE80)
|
||||
MKW_REGION_RANGE(NtscU, 808739B0, 80875453, -0xCDC8)
|
||||
MKW_REGION_RANGE(NtscU, 80875454, 8088344B, -0xCD58)
|
||||
MKW_REGION_RANGE(NtscU, 8088344C, 8088F70F, -0x4430)
|
||||
MKW_REGION_RANGE(NtscU, 8088FDB8, 8088FDBB, -0x4458)
|
||||
MKW_REGION_RANGE(NtscU, 80890030, 80890215, -0x4470)
|
||||
MKW_REGION_RANGE(NtscU, 80890490, 80890977, -0x4578)
|
||||
MKW_REGION_RANGE(NtscU, 808913C0, 808913C3, -0x4448)
|
||||
MKW_REGION_RANGE(NtscU, 80891750, 8089175F, -0x44D0)
|
||||
MKW_REGION_RANGE(NtscU, 80891A28, 80891C28, -0x4518)
|
||||
MKW_REGION_RANGE(NtscU, 80892F2C, 80892F96, -0x1B0)
|
||||
MKW_REGION_RANGE(NtscU, 80893217, 8089321D, -0x4A30)
|
||||
MKW_REGION_RANGE(NtscU, 808944DE, 80894769, -0x50E8)
|
||||
MKW_REGION_RANGE(NtscU, 80894B98, 80894B9C, -0x1330)
|
||||
MKW_REGION_RANGE(NtscU, 80895238, 808952B7, -0x58F8)
|
||||
MKW_REGION_RANGE(NtscU, 80895A08, 80895C8C, -0x1930)
|
||||
MKW_REGION_RANGE(NtscU, 80895CBC, 80895CF0, -0x5FA8)
|
||||
MKW_REGION_RANGE(NtscU, 8089647A, 808966D4, -0x1CA0)
|
||||
MKW_REGION_RANGE(NtscU, 80896820, 80897040, -0x6678)
|
||||
MKW_REGION_RANGE(NtscU, 80897C63, 80897C67, -0x6DA0)
|
||||
MKW_REGION_RANGE(NtscU, 80897DD3, 80897DEF, -0x6DA8)
|
||||
MKW_REGION_RANGE(NtscU, 80898955, 80898959, -0x7388)
|
||||
MKW_REGION_RANGE(NtscU, 80899692, 8089973A, -0x7730)
|
||||
MKW_REGION_RANGE(NtscU, 808998B3, 808998CA, -0x3FB0)
|
||||
MKW_REGION_RANGE(NtscU, 80899A36, 80899EEF, -0x7890)
|
||||
MKW_REGION_RANGE(NtscU, 8089A194, 8089A1B7, -0x46D0)
|
||||
MKW_REGION_RANGE(NtscU, 808A1D80, 808A1E6F, -0x4A08)
|
||||
MKW_REGION_RANGE(NtscU, 808A22E0, 808A22EF, -0x46D0)
|
||||
MKW_REGION_RANGE(NtscU, 808A4E00, 808A56FF, -0x49C0)
|
||||
MKW_REGION_RANGE(NtscU, 808A587C, 808A5897, -0x2B20)
|
||||
MKW_REGION_RANGE(NtscU, 808A5A30, 808A5DC3, -0x4AD8)
|
||||
MKW_REGION_RANGE(NtscU, 808A7DE0, 808A85F3, -0x4CD8)
|
||||
MKW_REGION_RANGE(NtscU, 808A98D0, 808A98DF, -0x5728)
|
||||
MKW_REGION_RANGE(NtscU, 808A9AF8, 808A9AFF, +0x780)
|
||||
MKW_REGION_RANGE(NtscU, 808A9C0A, 808A9CC8, -0x58C8)
|
||||
MKW_REGION_RANGE(NtscU, 808A9FEC, 808AA1AF, -0x5928)
|
||||
MKW_REGION_RANGE(NtscU, 808AAA04, 808AAA10, -0x5980)
|
||||
MKW_REGION_RANGE(NtscU, 808ACB3F, 808ACB43, -0x5F10)
|
||||
MKW_REGION_RANGE(NtscU, 808ACFDF, 808AD02B, -0x60C0)
|
||||
MKW_REGION_RANGE(NtscU, 808AD3C4, 808AD3C7, -0x60C0)
|
||||
MKW_REGION_RANGE(NtscU, 808AD48E, 808AD492, -0x2980)
|
||||
MKW_REGION_RANGE(NtscU, 808AD9F7, 808AD9FC, -0x6320)
|
||||
MKW_REGION_RANGE(NtscU, 808ADCBC, 808ADD2C, -0x2FA8)
|
||||
MKW_REGION_RANGE(NtscU, 808AEB45, 808AEB55, -0x68A8)
|
||||
MKW_REGION_RANGE(NtscU, 808AF8E0, 808AF95F, -0x6AC0)
|
||||
MKW_REGION_RANGE(NtscU, 808AFC18, 808AFC1B, -0x46B8)
|
||||
MKW_REGION_RANGE(NtscU, 808B30C8, 808B3108, -0x46B0)
|
||||
MKW_REGION_RANGE(NtscU, 808B35A0, 808B35AF, -0x46B0)
|
||||
MKW_REGION_RANGE(NtscU, 808B3984, 808B3987, -0x4850)
|
||||
MKW_REGION_RANGE(NtscU, 808B3A90, 808B3A9F, -0x4850)
|
||||
MKW_REGION_RANGE(NtscU, 808B4C20, 808B4DEF, -0x46B0)
|
||||
MKW_REGION_RANGE(NtscU, 808B54B8, 808B550F, -0x4738)
|
||||
MKW_REGION_RANGE(NtscU, 808B59F4, 808B5CCF, -0x48C0)
|
||||
MKW_REGION_RANGE(NtscU, 808B5CD8, 808B5CDB, -0x48BC)
|
||||
MKW_REGION_RANGE(NtscU, 808BB034, 808BB097, -0x60F8)
|
||||
MKW_REGION_RANGE(NtscU, 808BBEC8, 808BE908, -0x6430)
|
||||
MKW_REGION_RANGE(NtscU, 808BFAF0, 808BFAFF, -0x65B0)
|
||||
MKW_REGION_RANGE(NtscU, 808C069C, 808C06FF, -0x66B0)
|
||||
MKW_REGION_RANGE(NtscU, 808CB550, 808CB553, -0x5508)
|
||||
MKW_REGION_RANGE(NtscU, 808CB700, 808CB70F, -0x5508)
|
||||
MKW_REGION_RANGE(NtscU, 808D1DD0, 808D1E2B, -0x3A98)
|
||||
MKW_REGION_RANGE(NtscU, 808D30D8, 808D324F, -0x4908)
|
||||
MKW_REGION_RANGE(NtscU, 808D3698, 808D369B, +0x1AB0)
|
||||
MKW_REGION_RANGE(NtscU, 808D36CC, 808D36CF, +0x1AB0)
|
||||
MKW_REGION_RANGE(NtscU, 808D36D4, 808D36D7, +0x1AB0)
|
||||
MKW_REGION_RANGE(NtscU, 808D3744, 808D3747, +0x1AB0)
|
||||
MKW_REGION_RANGE(NtscU, 808D374C, 808D374F, +0x1AB0)
|
||||
MKW_REGION_RANGE(NtscU, 808D3C78, 808D3D1B, +0x1610)
|
||||
MKW_REGION_RANGE(NtscU, 808D3E14, 808D3F2F, -0x4F68)
|
||||
MKW_REGION_RANGE(NtscU, 808D9A58, 808D9A98, -0x60F0)
|
||||
MKW_REGION_RANGE(NtscU, 808D9BC8, 808D9C5F, -0x36C0)
|
||||
MKW_REGION_RANGE(NtscU, 808D9DB0, 808D9F97, -0x6220)
|
||||
MKW_REGION_RANGE(NtscU, 808DA070, 808DA07F, -0x6220)
|
||||
MKW_REGION_RANGE(NtscU, 808DA318, 808DA367, -0x63B8)
|
||||
MKW_REGION_RANGE(NtscU, 808DA5DC, 808DA63F, -0x64C0)
|
||||
MKW_REGION_RANGE(NtscU, 808DAD60, 808DAD6F, -0x6778)
|
||||
MKW_REGION_RANGE(NtscU, 808DC540, 808DC54F, -0x4138)
|
||||
MKW_REGION_RANGE(NtscU, 808E2BBC, 808E2BBE, +0x0)
|
||||
MKW_REGION_RANGE(NtscU, 809BD6E0, 809C186B, -0x47C0)
|
||||
MKW_REGION_RANGE(NtscU, 809C1874, 809C1877, -0x47C0)
|
||||
MKW_REGION_RANGE(NtscU, 809C18F8, 809C18FB, -0x47E8)
|
||||
MKW_REGION_RANGE(NtscU, 809C1918, 809C191B, -0x47E8)
|
||||
MKW_REGION_RANGE(NtscU, 809C1980, 809C198F, -0x4610)
|
||||
MKW_REGION_RANGE(NtscU, 809C19A0, 809C19BB, -0x4820)
|
||||
MKW_REGION_RANGE(NtscU, 809C1E38, 809C1E3B, -0x4930)
|
||||
MKW_REGION_RANGE(NtscU, 809C1F50, 809C20EF, -0x47C0)
|
||||
MKW_REGION_RANGE(NtscU, 809C20F0, 809C20F7, -0x47C8)
|
||||
MKW_REGION_RANGE(NtscU, 809C20F8, 809C20FB, -0x47A8)
|
||||
MKW_REGION_RANGE(NtscU, 809C2100, 809C2107, -0x47D0)
|
||||
MKW_REGION_RANGE(NtscU, 809C2108, 809C210B, -0x47B0)
|
||||
MKW_REGION_RANGE(NtscU, 809C210C, 809C210F, -0x47D0)
|
||||
MKW_REGION_RANGE(NtscU, 809C2110, 809C211B, -0x47D8)
|
||||
MKW_REGION_RANGE(NtscU, 809C21D0, 809C21D3, -0x47C0)
|
||||
MKW_REGION_RANGE(NtscU, 809C21D4, 809C21D7, +0xB840)
|
||||
MKW_REGION_RANGE(NtscU, 809C21D8, 809C21DB, -0x47C0)
|
||||
MKW_REGION_RANGE(NtscU, 809C2328, 809C232B, -0x47C8)
|
||||
MKW_REGION_RANGE(NtscU, 809C26B8, 809C26BB, -0x4700)
|
||||
MKW_REGION_RANGE(NtscU, 809C27F0, 809C289B, -0x4C40)
|
||||
MKW_REGION_RANGE(NtscU, 809C28B8, 809C28BB, -0x47C0)
|
||||
MKW_REGION_RANGE(NtscU, 809C2BE8, 809C2BEB, -0x4850)
|
||||
MKW_REGION_RANGE(NtscU, 809C2CB8, 809C2CBB, -0x47E0)
|
||||
MKW_REGION_RANGE(NtscU, 809C2EF0, 809C2EF3, -0x47C0)
|
||||
MKW_REGION_RANGE(NtscU, 809C2F38, 809C361B, -0x47F8)
|
||||
MKW_REGION_RANGE(NtscU, 809C3670, 809C38CF, -0x4808)
|
||||
MKW_REGION_RANGE(NtscU, 809C4330, 809C4333, -0x4840)
|
||||
MKW_REGION_RANGE(NtscU, 809C4680, 809C4683, -0x48C0)
|
||||
MKW_REGION_RANGE(NtscU, 809C4730, 809C474F, -0x47B0)
|
||||
MKW_REGION_RANGE(NtscU, 809C496C, 809C496F, -0x4820)
|
||||
|
||||
MKW_REGION_RANGE(NtscJ, 80000000, 80008023, +0x0)
|
||||
MKW_REGION_RANGE(NtscJ, 800080E8, 8000ADBF, -0xA4)
|
||||
MKW_REGION_RANGE(NtscJ, 8000AF08, 80021BAB, -0xDC)
|
||||
MKW_REGION_RANGE(NtscJ, 80021BAC, 80244EBF, -0xE0)
|
||||
MKW_REGION_RANGE(NtscJ, 802572BF, 802575DF, -0x660)
|
||||
MKW_REGION_RANGE(NtscJ, 80257F40, 80258184, -0x660)
|
||||
MKW_REGION_RANGE(NtscJ, 80276054, 802A407F, -0x660)
|
||||
MKW_REGION_RANGE(NtscJ, 802A4080, 8038917B, -0x680)
|
||||
MKW_REGION_RANGE(NtscJ, 80399200, 80399400, -0x680)
|
||||
MKW_REGION_RANGE(NtscJ, 805103B4, 805CC0A7, -0x680)
|
||||
MKW_REGION_RANGE(NtscJ, 805CC1B4, 805FA33B, -0x724)
|
||||
MKW_REGION_RANGE(NtscJ, 805FA344, 805FF6E7, -0x724)
|
||||
MKW_REGION_RANGE(NtscJ, 805FFD70, 806003E7, -0x848)
|
||||
MKW_REGION_RANGE(NtscJ, 80600C78, 80620CB3, -0x88C)
|
||||
MKW_REGION_RANGE(NtscJ, 80620D7C, 80637A23, -0x8B4)
|
||||
MKW_REGION_RANGE(NtscJ, 80637A80, 8063BCF7, -0x914)
|
||||
MKW_REGION_RANGE(NtscJ, 8063BD28, 8063BD2F, -0x920)
|
||||
MKW_REGION_RANGE(NtscJ, 8063BD38, 8063BD3F, -0x920)
|
||||
MKW_REGION_RANGE(NtscJ, 8063BE40, 8088F70F, -0x994)
|
||||
MKW_REGION_RANGE(NtscJ, 8088F720, 8088FDBB, -0x9A0)
|
||||
MKW_REGION_RANGE(NtscJ, 80890208, 80890977, -0x9B0)
|
||||
MKW_REGION_RANGE(NtscJ, 808913C0, 808913C3, -0x9B0)
|
||||
MKW_REGION_RANGE(NtscJ, 80891750, 8089175F, -0x9B0)
|
||||
MKW_REGION_RANGE(NtscJ, 80891A60, 80891A6F, -0x9B0)
|
||||
MKW_REGION_RANGE(NtscJ, 80891B28, 80891C28, -0x9B0)
|
||||
MKW_REGION_RANGE(NtscJ, 80892F2C, 80897DEF, -0x9B0)
|
||||
MKW_REGION_RANGE(NtscJ, 80898955, 80898959, -0xE60)
|
||||
MKW_REGION_RANGE(NtscJ, 80899692, 808B5CCF, -0xEA0)
|
||||
MKW_REGION_RANGE(NtscJ, 808B5CD8, 808B5CDB, -0xE9C)
|
||||
MKW_REGION_RANGE(NtscJ, 808BB034, 808BB097, -0xEB0)
|
||||
MKW_REGION_RANGE(NtscJ, 808BBEC8, 808DA617, -0xEB0)
|
||||
MKW_REGION_RANGE(NtscJ, 808DC540, 808DC54F, -0xEB0)
|
||||
MKW_REGION_RANGE(NtscJ, 808E2BBC, 808E2BBE, +0x0)
|
||||
MKW_REGION_RANGE(NtscJ, 809BD6E8, 809C474B, -0xFA0)
|
||||
MKW_REGION_RANGE(NtscJ, 809C496C, 809C496F, -0xFA0)
|
||||
@@ -44,56 +44,62 @@ extern "C" void GX__SetBlendMode_8017277c(uint32_t t, uint32_t s, uint32_t d, ui
|
||||
extern "C" void GX__LoadPosMtxImm_8017310c(uint32_t ma, uint32_t id);
|
||||
extern "C" void GX__SetCurrentMtx_80173214(uint32_t id);
|
||||
|
||||
// Keyed by the translated region's addresses, so only translated code sees them;
|
||||
// runtime calls resolve through the registry.
|
||||
#if defined(MKW_TRANSLATED_GUEST_REGION)
|
||||
|
||||
#define MKW_KNOWN_TYPED_NATIVE(addr, fn, ...) \
|
||||
template <> struct KnownTypedNativeCpuCall<addr> { \
|
||||
template <> struct KnownTypedNativeCpuCall<MKW_GADDR_CT(addr)> { \
|
||||
static constexpr bool kAvailable = true; \
|
||||
static void Invoke(CpuContext* cpu) { (void)cpu; fn(__VA_ARGS__); } \
|
||||
}
|
||||
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016D3A4u, GX__SetVtxDesc_8016d3a4, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016DC34u, GX__ClearVtxDesc_8016dc34);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016DC68u, GX__SetVtxAttrFmt_8016dc68, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016E37Cu, GX__SetTexCoordGen2_8016e37c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016E5A4u, GX__SetNumTexGens_8016e5a4, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016EAB0u, GX__DrawDone_8016eab0);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016F0F0u, GX__Begin_8016f0f0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016F314u, GX__SetLineWidth_8016f314, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016F348u, GX__SetPointSize_8016f348, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8016F3B8u, GX__SetCullMode_8016f3b8, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8017054Cu, GX__SetNumChans_8017054c, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80170570u, GX__SetChanCtrl_80170570, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8], cpu->gpr[9]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x801707F8u, GX__InitTexObj_801707f8, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8], cpu->gpr[9], cpu->gpr[10]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80170A4Cu, GX__InitTexObjLOD_80170a4c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], static_cast<float>(cpu->fpr[1].d), static_cast<float>(cpu->fpr[2].d), static_cast<float>(cpu->fpr[3].d), cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171110u, GX__InvalidateTexAll_80171110);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80170F2Cu, GX__LoadTexObj_80170f2c, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171B38u, GX__SetNumIndStages_80171b38, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171B58u, GX__SetTevDirect_80171b58, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171CE0u, GX__SetTevColorIn_80171ce0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171D20u, GX__SetTevAlphaIn_80171d20, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171D60u, GX__SetTevColorOp_80171d60, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171DB8u, GX__SetTevAlphaOp_80171db8, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171E70u, GX__SetTevColorS10_80171e70, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171ED4u, GX__SetTevKColor_80171ed4, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171F30u, GX__SetTevKColorSel_80171f30, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171F80u, GX__SetTevKAlphaSel_80171f80, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80171FD0u, GX__SetTevSwapMode_80171fd0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8017200Cu, GX__SetTevSwapModeTable_8017200c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8017214Cu, GX__SetTevOrder_8017214c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x801722A8u, GX__SetNumTevStages_801722a8, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8017277Cu, GX__SetBlendMode_8017277c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x8017310Cu, GX__LoadPosMtxImm_8017310c, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(0x80173214u, GX__SetCurrentMtx_80173214, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016D3A4, GX__SetVtxDesc_8016d3a4, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016DC34, GX__ClearVtxDesc_8016dc34);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016DC68, GX__SetVtxAttrFmt_8016dc68, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016E37C, GX__SetTexCoordGen2_8016e37c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016E5A4, GX__SetNumTexGens_8016e5a4, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016EAB0, GX__DrawDone_8016eab0);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016F0F0, GX__Begin_8016f0f0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016F314, GX__SetLineWidth_8016f314, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016F348, GX__SetPointSize_8016f348, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8016F3B8, GX__SetCullMode_8016f3b8, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8017054C, GX__SetNumChans_8017054c, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80170570, GX__SetChanCtrl_80170570, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8], cpu->gpr[9]);
|
||||
MKW_KNOWN_TYPED_NATIVE(801707F8, GX__InitTexObj_801707f8, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8], cpu->gpr[9], cpu->gpr[10]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80170A4C, GX__InitTexObjLOD_80170a4c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], static_cast<float>(cpu->fpr[1].d), static_cast<float>(cpu->fpr[2].d), static_cast<float>(cpu->fpr[3].d), cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171110, GX__InvalidateTexAll_80171110);
|
||||
MKW_KNOWN_TYPED_NATIVE(80170F2C, GX__LoadTexObj_80170f2c, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171B38, GX__SetNumIndStages_80171b38, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171B58, GX__SetTevDirect_80171b58, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171CE0, GX__SetTevColorIn_80171ce0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171D20, GX__SetTevAlphaIn_80171d20, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171D60, GX__SetTevColorOp_80171d60, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171DB8, GX__SetTevAlphaOp_80171db8, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7], cpu->gpr[8]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171E70, GX__SetTevColorS10_80171e70, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171ED4, GX__SetTevKColor_80171ed4, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171F30, GX__SetTevKColorSel_80171f30, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171F80, GX__SetTevKAlphaSel_80171f80, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80171FD0, GX__SetTevSwapMode_80171fd0, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8017200C, GX__SetTevSwapModeTable_8017200c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6], cpu->gpr[7]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8017214C, GX__SetTevOrder_8017214c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6]);
|
||||
MKW_KNOWN_TYPED_NATIVE(801722A8, GX__SetNumTevStages_801722a8, cpu->gpr[3]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8017277C, GX__SetBlendMode_8017277c, cpu->gpr[3], cpu->gpr[4], cpu->gpr[5], cpu->gpr[6]);
|
||||
MKW_KNOWN_TYPED_NATIVE(8017310C, GX__LoadPosMtxImm_8017310c, cpu->gpr[3], cpu->gpr[4]);
|
||||
MKW_KNOWN_TYPED_NATIVE(80173214, GX__SetCurrentMtx_80173214, cpu->gpr[3]);
|
||||
|
||||
#undef MKW_KNOWN_TYPED_NATIVE
|
||||
|
||||
#define MKW_KNOWN_NATIVE_CPU_CALL(addr, fn) \
|
||||
template <> struct KnownNativeCpuCall<addr> { \
|
||||
template <> struct KnownNativeCpuCall<MKW_GADDR_CT(addr)> { \
|
||||
static constexpr bool kAvailable = true; \
|
||||
static constexpr uint32_t kNonvolatileFprWriteMask = 0; \
|
||||
static constexpr void (*Entry)(CpuContext*) = &fn; \
|
||||
}
|
||||
|
||||
MKW_KNOWN_NATIVE_CPU_CALL(0x800847C0u, nw4r__lyt__detail__DrawQuad_800847c0);
|
||||
MKW_KNOWN_NATIVE_CPU_CALL(0x80084D20u, nw4r__lyt__detail__DrawQuad_80084d20);
|
||||
MKW_KNOWN_NATIVE_CPU_CALL(800847C0, nw4r__lyt__detail__DrawQuad_800847c0);
|
||||
MKW_KNOWN_NATIVE_CPU_CALL(80084D20, nw4r__lyt__detail__DrawQuad_80084d20);
|
||||
|
||||
#undef MKW_KNOWN_NATIVE_CPU_CALL
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
// Base translations the runtime calls directly, named by PAL identity and resolved through the
|
||||
// function registry on first use.
|
||||
// MKW_GUEST_FUNC(801A0620)(ctx);
|
||||
|
||||
#include "guest_abi/cpu_context.h"
|
||||
#include "guest_abi/guest_region.h"
|
||||
|
||||
namespace GuestFunctions {
|
||||
using Entry = void (*)(CpuContext*);
|
||||
|
||||
// The base translation at a guest address, also where a native or mod overrides it.
|
||||
Entry BaseTranslation(uint32_t address);
|
||||
} // namespace GuestFunctions
|
||||
|
||||
#define MKW_GUEST_FUNC(pal) \
|
||||
(*[] { \
|
||||
static const ::GuestFunctions::Entry entry = ::GuestFunctions::BaseTranslation(MKW_GADDR(pal)); \
|
||||
return entry; \
|
||||
}())
|
||||
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
// Dedicated GX thread (Dolphin "dual core"): the game thread records GX HLE calls in order and
|
||||
// the worker executes them. While active, GX work runs on the worker except inside inline scopes,
|
||||
// which run on the game thread after the worker has caught up.
|
||||
namespace GxWorker {
|
||||
|
||||
namespace detail {
|
||||
inline constexpr size_t kAlign = 16;
|
||||
|
||||
extern std::atomic<bool> g_active;
|
||||
extern constinit thread_local bool t_onWorker;
|
||||
extern constinit thread_local int t_inlineDepth;
|
||||
extern bool g_fifoPending;
|
||||
|
||||
void* Reserve(size_t payloadSize, void (*run)(void*));
|
||||
void Commit();
|
||||
void FlushFifo();
|
||||
} // namespace detail
|
||||
|
||||
// Starts the worker unless [video] gx_worker disables it. Call once Aurora is up.
|
||||
void Start();
|
||||
void Stop();
|
||||
// Stops without running queued work; bounded, for fatal exits.
|
||||
void Abandon();
|
||||
|
||||
inline bool Active() noexcept { return detail::g_active.load(std::memory_order_relaxed); }
|
||||
inline bool OnWorker() noexcept { return detail::t_onWorker; }
|
||||
// True when a GX entry point on this thread must be recorded instead of executed.
|
||||
inline bool ShouldDefer() noexcept {
|
||||
return Active() && !detail::t_onWorker && detail::t_inlineDepth == 0;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void Submit(F&& fn) {
|
||||
using Fn = std::decay_t<F>;
|
||||
static_assert(alignof(Fn) <= detail::kAlign, "over-aligned GX command");
|
||||
if (detail::g_fifoPending) {
|
||||
detail::FlushFifo();
|
||||
}
|
||||
void* storage = detail::Reserve(sizeof(Fn), [](void* p) {
|
||||
struct Destroy {
|
||||
Fn* f;
|
||||
~Destroy() { f->~Fn(); }
|
||||
} guard{static_cast<Fn*>(p)};
|
||||
(*guard.f)();
|
||||
});
|
||||
new (storage) Fn(std::forward<F>(fn));
|
||||
detail::Commit();
|
||||
}
|
||||
|
||||
// Records fn when deferring, otherwise runs it now. Read call-time guest memory before calling.
|
||||
template <typename F>
|
||||
void Run(F&& fn) {
|
||||
if (ShouldDefer()) {
|
||||
Submit(std::forward<F>(fn));
|
||||
} else {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
|
||||
// Gather-pipe writes, coalesced into one record per run of consecutive writes and replayed
|
||||
// with their original sizes.
|
||||
void FifoWrite(uint32_t value, uint32_t sizeBytes);
|
||||
void FifoBurst(const uint8_t* data, uint32_t sizeBytes);
|
||||
|
||||
// Called about every millisecond while the game thread waits in Sync.
|
||||
void SetWaitCallback(void (*callback)());
|
||||
|
||||
// Blocks until every recorded command has executed.
|
||||
void Sync();
|
||||
|
||||
// GX entry points run inline on the game thread while inside (after a Sync), for work whose
|
||||
// results the guest needs immediately.
|
||||
void BeginInline();
|
||||
void EndInline();
|
||||
|
||||
class InlineScope {
|
||||
public:
|
||||
InlineScope() { BeginInline(); }
|
||||
~InlineScope() { EndInline(); }
|
||||
InlineScope(const InlineScope&) = delete;
|
||||
InlineScope& operator=(const InlineScope&) = delete;
|
||||
};
|
||||
|
||||
} // namespace GxWorker
|
||||
|
||||
// Records the enclosing call for the GX worker and returns, or falls through when it should run
|
||||
// here (worker thread, inline scope, worker disabled).
|
||||
#define GX_DEFER_CALL(fn, ...) \
|
||||
do { \
|
||||
if (GxWorker::ShouldDefer()) { \
|
||||
GxWorker::Submit([=] { fn(__VA_ARGS__); }); \
|
||||
return; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define GX_DEFER_CALL0(fn) \
|
||||
do { \
|
||||
if (GxWorker::ShouldDefer()) { \
|
||||
GxWorker::Submit([] { fn(); }); \
|
||||
return; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
// Runs the rest of the enclosing function on this thread once the worker has caught up.
|
||||
#define GX_SYNC_INLINE() GxWorker::InlineScope gxInlineScope_
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
// IOS side of the emulated Bluetooth module (/dev/usb/oh1/57e/305): routes the
|
||||
// guest's USB control / bulk / interrupt transfers to Bluetooth::Controller and
|
||||
// completes the held event and ACL reads from the alarm pump.
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct CpuContext;
|
||||
|
||||
// Returns the fd for the Bluetooth device path, 0 when `path` is not ours.
|
||||
int32_t Bluetooth_HLE_OpenDevice(const char* path);
|
||||
bool Bluetooth_HLE_IsFd(uint32_t fd);
|
||||
// Cancels the held reads (their callbacks are queued) and closes the device.
|
||||
int32_t Bluetooth_HLE_Close(uint32_t fd);
|
||||
// True when the request completed now with `result`; false when it is held
|
||||
// until data arrives (its callback is queued later by the pump).
|
||||
bool Bluetooth_HLE_IoctlvAsync(uint32_t fd, uint32_t cmd, uint32_t numIn, uint32_t numOut, uint32_t vectorPtr,
|
||||
uint32_t callback, uint32_t callbackArg, int32_t& result);
|
||||
int32_t Bluetooth_HLE_Ioctlv(uint32_t fd, uint32_t cmd, uint32_t numIn, uint32_t numOut, uint32_t vectorPtr);
|
||||
// Runs the controller and completes held reads. Emulation thread only.
|
||||
bool Bluetooth_HLE_ProcessCompletions(CpuContext* cpu);
|
||||
@@ -41,42 +41,3 @@ inline GuestStatus Encode(const Fields& fields)
|
||||
}
|
||||
|
||||
} // namespace PadStatusContract
|
||||
|
||||
namespace WpadContract {
|
||||
|
||||
inline constexpr std::size_t kChannelCount = 4;
|
||||
inline constexpr int32_t kStatusDisabled = 0;
|
||||
inline constexpr int32_t kStatusReady = 3;
|
||||
inline constexpr int32_t kErrorNoController = -1;
|
||||
inline constexpr int32_t kErrorNotReady = -2;
|
||||
inline constexpr int32_t kErrorBadChannel = -6;
|
||||
inline constexpr int32_t kExtensionCore = 0;
|
||||
|
||||
class State {
|
||||
public:
|
||||
void Initialize() { m_initialized = true; }
|
||||
bool IsInitialized() const { return m_initialized; }
|
||||
int32_t GetLibraryStatus() const { return m_initialized ? kStatusReady : kStatusDisabled; }
|
||||
|
||||
int32_t GetDataFormat(uint32_t chan) const
|
||||
{
|
||||
if (chan >= kChannelCount) {
|
||||
return kErrorBadChannel;
|
||||
}
|
||||
return m_initialized ? 0 : kErrorNotReady;
|
||||
}
|
||||
|
||||
int32_t SetDataFormat(uint32_t chan, int32_t format) const
|
||||
{
|
||||
(void)format;
|
||||
if (chan >= kChannelCount) {
|
||||
return kErrorBadChannel;
|
||||
}
|
||||
return m_initialized ? kErrorNoController : kErrorNotReady;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_initialized = false;
|
||||
};
|
||||
|
||||
} // namespace WpadContract
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// HW_GPIOB_OUT, the GPIO outputs the PPC owns. WPAD sets bit 8 to power the
|
||||
// sensor bar; there is nothing behind it on a PC, so the register only latches.
|
||||
inline constexpr uint32_t kHollywoodGpioBOut = 0xCD0000C0u;
|
||||
|
||||
bool HollywoodGpio_Read32(uint32_t address, uint32_t& value);
|
||||
bool HollywoodGpio_Write32(uint32_t address, uint32_t value);
|
||||
@@ -38,10 +38,12 @@ extern "C" void OS_HLE_EndDeferredGuestCallbacks();
|
||||
// renaming requires updating Translator.Cli/Program.cs, RuntimeNativeGuestEffectAnalyzer.cs,
|
||||
// TranslatedBuildShardEmitter.cs and RuntimeNativeFunctionAbiProvider.cs together.
|
||||
|
||||
// addr_hex is the PAL address (see guest_abi/guest_region.h). The translator reads arg_list as
|
||||
// the guest ABI, so the unused wrapper keeps it checked against the implementation.
|
||||
#define PPC_NATIVE_OVERRIDE(addr_hex, name, ret_type, arg_list, call_list) \
|
||||
extern "C" ret_type func_##addr_hex arg_list { return name call_list; } \
|
||||
[[maybe_unused]] static ret_type native_signature_##addr_hex arg_list { return name call_list; } \
|
||||
REGISTER_NATIVE_FUNCTION(0x##addr_hex, name)
|
||||
|
||||
#define PPC_NATIVE_OVERRIDE_VOID(addr_hex, name, arg_list, call_list) \
|
||||
extern "C" void func_##addr_hex arg_list { name call_list; } \
|
||||
[[maybe_unused]] static void native_signature_##addr_hex arg_list { name call_list; } \
|
||||
REGISTER_NATIVE_FUNCTION(0x##addr_hex, name)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "runtime_config.h"
|
||||
#include "guest_abi/guest_region.h"
|
||||
#include "nand_settings.h"
|
||||
#include "runtime_log.h"
|
||||
#include "system_bridge.h"
|
||||
@@ -184,7 +185,9 @@ inline std::filesystem::path DiscoverNandRootPath() {
|
||||
static const auto root = [] {
|
||||
const auto resolved = ResolveNandRootPath();
|
||||
std::string error;
|
||||
if (!RuntimeNandSettings::Ensure(resolved, error)) {
|
||||
const auto& facts = ::GuestRegion::CurrentFacts();
|
||||
if (!RuntimeNandSettings::Ensure(resolved, error,
|
||||
{facts.area, facts.model, facts.productCode, facts.video, facts.gameRegion})) {
|
||||
FailNandRoot(error.c_str(), RuntimeNandSettings::FilePath(resolved));
|
||||
}
|
||||
return resolved;
|
||||
|
||||
@@ -88,10 +88,20 @@ inline std::string GenerateSerial(std::time_t now) {
|
||||
return std::string(9 - digits.size(), '0') + digits;
|
||||
}
|
||||
|
||||
// This recompilation targets the European disc. These are Dolphin's PAL boot
|
||||
// defaults; an existing setting.txt always takes precedence, in every region.
|
||||
inline std::optional<std::array<uint8_t, 256>> EncodeNew(const std::string& serial) {
|
||||
const Settings identity{{"SERNO", serial}, {"CODE", "LEH"}, {"AREA", "EUR"}, {"GAME", "EU"}};
|
||||
// Console identity for a new setting.txt; Dolphin's boot defaults for the game's region.
|
||||
struct BootDefaults {
|
||||
std::string area = "EUR";
|
||||
std::string model = "RVL-001(EUR)";
|
||||
std::string code = "LEH";
|
||||
std::string video = "PAL";
|
||||
std::string game = "EU";
|
||||
};
|
||||
|
||||
// An existing setting.txt always takes precedence, in every region.
|
||||
inline std::optional<std::array<uint8_t, 256>> EncodeNew(
|
||||
const std::string& serial,
|
||||
const BootDefaults& defaults = BootDefaults{}) {
|
||||
const Settings identity{{"SERNO", serial}, {"CODE", defaults.code}, {"AREA", defaults.area}, {"GAME", defaults.game}};
|
||||
if (!HasIdentity(identity)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -102,9 +112,15 @@ inline std::optional<std::array<uint8_t, 256>> EncodeNew(const std::string& seri
|
||||
bytes[position++] = static_cast<uint8_t>(value) ^ static_cast<uint8_t>(key);
|
||||
key = (key << 1) | (key >> 31);
|
||||
};
|
||||
for (const std::string& line : {std::string("AREA=EUR\r\n"), std::string("MODEL=RVL-001(EUR)\r\n"),
|
||||
std::string("DVD=0\r\n"), std::string("MPCH=0x7FFE\r\n"), std::string("CODE=LEH\r\n"),
|
||||
"SERNO=" + serial + "\r\n", std::string("VIDEO=PAL\r\n"), std::string("GAME=EU\r\n")}) {
|
||||
for (const std::string& line : {
|
||||
"AREA=" + defaults.area + "\r\n",
|
||||
"MODEL=" + defaults.model + "\r\n",
|
||||
std::string("DVD=0\r\n"),
|
||||
std::string("MPCH=0x7FFE\r\n"),
|
||||
"CODE=" + defaults.code + "\r\n",
|
||||
"SERNO=" + serial + "\r\n",
|
||||
"VIDEO=" + defaults.video + "\r\n",
|
||||
"GAME=" + defaults.game + "\r\n"}) {
|
||||
for (;;) {
|
||||
if (position + line.size() > bytes.size()) {
|
||||
return std::nullopt;
|
||||
@@ -146,6 +162,7 @@ inline std::optional<std::filesystem::path> CreateScratchDirectory(
|
||||
// Never replace an existing file, including an unreadable or damaged one.
|
||||
// Publish a complete file atomically so simultaneous launches use one identity.
|
||||
inline bool Ensure(const std::filesystem::path& root, std::string& error,
|
||||
const BootDefaults& defaults,
|
||||
std::time_t now = std::time(nullptr)) {
|
||||
const auto path = FilePath(root);
|
||||
std::error_code ec;
|
||||
@@ -163,7 +180,7 @@ inline bool Ensure(const std::filesystem::path& root, std::string& error,
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto bytes = EncodeNew(GenerateSerial(now));
|
||||
const auto bytes = EncodeNew(GenerateSerial(now), defaults);
|
||||
if (!bytes) {
|
||||
error = "Cannot initialize NAND settings: invalid system clock";
|
||||
return false;
|
||||
|
||||
@@ -44,6 +44,8 @@ struct RuntimeUserConfig {
|
||||
std::optional<uint32_t> frameInterpolationFps;
|
||||
std::optional<bool> metalFxSpatialUpscaling;
|
||||
std::optional<bool> skipUnreadyPipelines;
|
||||
std::optional<bool> vsync;
|
||||
std::optional<bool> gxWorker;
|
||||
std::optional<bool> disableCopyFilter;
|
||||
std::optional<bool> textureReplacements;
|
||||
std::optional<bool> textureDumps;
|
||||
@@ -57,26 +59,16 @@ struct RuntimeUserConfig {
|
||||
std::optional<bool> audioMuted;
|
||||
std::optional<bool> audioMixWorker;
|
||||
std::optional<bool> attenuateMusicWhenMediaPlays;
|
||||
// Real Wii Remotes (with or without Nunchuk / Classic Controller) and Wii U Pro
|
||||
// Controllers paired over Bluetooth, driven by SDL's HIDAPI Wii driver. The driver
|
||||
// is opt-in on SDL's side, so this decides whether the runtime turns it on.
|
||||
// Real Wii Remotes passed through to the game's own Bluetooth stack. Off hands
|
||||
// Nintendo controllers (e.g. the Wii U Pro Controller) to SDL as plain gamepads.
|
||||
std::optional<bool> wiiRemotes;
|
||||
// Keep re-enumerating Bluetooth HID devices while no Wii controller is connected
|
||||
// (Dolphin's "continuous scanning"), so a remote that dropped or was switched on
|
||||
// after launch shows up without restarting.
|
||||
std::optional<bool> wiiContinuousScan;
|
||||
// Accelerometer zero-point correction for the Bluetooth Wii Remote, in g and in
|
||||
// SDL's sensor frame (x right, y out of the button face, z towards the user).
|
||||
// SDL's Wii driver falls back to a nominal zero point when its read of the
|
||||
// remote's calibration block times out (common over Bluetooth), so this is
|
||||
// measured in the overlay with the remote at rest. Stored per remote under
|
||||
// [controller.wii_accel_offsets]; the old shared
|
||||
// wii_accel_offset_* keys are only detected, to report that they are ignored.
|
||||
std::map<std::string, std::array<double, 3>> wiiAccelOffsets;
|
||||
bool hasLegacyWiiAccelOffset = false;
|
||||
// Debugging aid: append every KPAD sample of the Bluetooth remote (raw and
|
||||
// corrected accelerometer, buttons) to wii_accel_trace.csv next to Config.toml.
|
||||
std::optional<bool> wiiAccelTrace;
|
||||
// Look for remotes in pairing mode (1+2 / SYNC) while none is connected.
|
||||
std::optional<bool> wiiAutoPair;
|
||||
// The console's Wii Remote settings (SYSCONF BT.BAR, BT.SENS, BT.SPKV, BT.MOT).
|
||||
std::optional<bool> wiiSensorBarAbove;
|
||||
std::optional<int32_t> wiiIrSensitivity;
|
||||
std::optional<int32_t> wiiSpeakerVolume;
|
||||
std::optional<bool> wiiRumble;
|
||||
std::optional<bool> networkEnabled;
|
||||
std::optional<bool> discordPresenceEnabled;
|
||||
// The application ID of the WiiCompiled Discord application. This is only
|
||||
@@ -285,6 +277,9 @@ inline void EnsureConfigFile() {
|
||||
"display_mode = \"windowed\"\n"
|
||||
"graphics_api = \"auto\"\n"
|
||||
"skip_unready_pipelines = true\n"
|
||||
"vsync = false\n"
|
||||
"# Runs GX command processing on its own thread. Read once, at startup.\n"
|
||||
"gx_worker = true\n"
|
||||
"disable_copy_filter = true\n"
|
||||
"show_fps = true\n"
|
||||
"# Dolphin-style custom textures. When enabled, the renderer indexes\n"
|
||||
@@ -382,28 +377,6 @@ inline void AppendOverlayRoots(RuntimeUserConfig& config, const std::string& roo
|
||||
}
|
||||
|
||||
// Reads every supported setting out of a parsed Config.toml document.
|
||||
// [x, y, z] of finite numbers (integers allowed), else nullopt.
|
||||
inline std::optional<std::array<double, 3>> ParseAccelOffset(const toml::value& value) {
|
||||
if (!value.is_array() || value.as_array().size() != 3) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::array<double, 3> offset{};
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
const toml::value& element = value.as_array()[i];
|
||||
if (element.is_floating()) {
|
||||
offset[i] = element.as_floating();
|
||||
} else if (element.is_integer()) {
|
||||
offset[i] = static_cast<double>(element.as_integer());
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!std::isfinite(offset[i])) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
RuntimeUserConfig config;
|
||||
|
||||
@@ -427,13 +400,6 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
config.controllerExpressions[key] = value.as_string();
|
||||
}
|
||||
}
|
||||
if (section->contains("wii_accel_offsets") && section->at("wii_accel_offsets").is_table()) {
|
||||
for (const auto& [key, value] : section->at("wii_accel_offsets").as_table()) {
|
||||
if (const auto offset = ParseAccelOffset(value)) {
|
||||
config.wiiAccelOffsets[key] = *offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.widescreen = FindConfigValue<bool>(document, "video", "widescreen");
|
||||
@@ -471,6 +437,8 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
config.metalFxSpatialUpscaling =
|
||||
FindConfigValue<bool>(document, "video", "metalfx_spatial_upscaling");
|
||||
config.skipUnreadyPipelines = FindConfigValue<bool>(document, "video", "skip_unready_pipelines");
|
||||
config.vsync = FindConfigValue<bool>(document, "video", "vsync");
|
||||
config.gxWorker = FindConfigValue<bool>(document, "video", "gx_worker");
|
||||
config.disableCopyFilter = FindConfigValue<bool>(document, "video", "disable_copy_filter");
|
||||
config.showFps = FindConfigValue<bool>(document, "video", "show_fps");
|
||||
config.textureReplacements = FindConfigValue<bool>(document, "video", "texture_replacements");
|
||||
@@ -494,11 +462,17 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
config.attenuateMusicWhenMediaPlays =
|
||||
FindConfigValue<bool>(document, "audio", "attenuate_music_when_media_plays");
|
||||
config.wiiRemotes = FindConfigValue<bool>(document, "controller", "wii_remotes");
|
||||
config.wiiContinuousScan = FindConfigValue<bool>(document, "controller", "wii_continuous_scan");
|
||||
for (const char* key : {"wii_accel_offset_x", "wii_accel_offset_y", "wii_accel_offset_z"}) {
|
||||
config.hasLegacyWiiAccelOffset |= FindConfigValue<double>(document, "controller", key).value_or(0.0) != 0.0;
|
||||
config.wiiAutoPair = FindConfigValue<bool>(document, "controller", "wii_auto_pair");
|
||||
if (const auto bar = FindConfigValue<std::string>(document, "controller", "wii_sensor_bar")) {
|
||||
if (*bar == "above" || *bar == "below") config.wiiSensorBarAbove = *bar == "above";
|
||||
}
|
||||
config.wiiAccelTrace = FindConfigValue<bool>(document, "controller", "wii_accel_trace");
|
||||
if (const auto value = FindConfigInt(document, "controller", "wii_ir_sensitivity"); value && *value >= 1 && *value <= 5) {
|
||||
config.wiiIrSensitivity = *value;
|
||||
}
|
||||
if (const auto value = FindConfigInt(document, "controller", "wii_speaker_volume"); value && *value >= 0 && *value <= 127) {
|
||||
config.wiiSpeakerVolume = *value;
|
||||
}
|
||||
config.wiiRumble = FindConfigValue<bool>(document, "controller", "wii_rumble");
|
||||
config.networkEnabled = FindConfigValue<bool>(document, "network", "enabled");
|
||||
config.discordPresenceEnabled = FindConfigValue<bool>(document, "discord", "enabled");
|
||||
config.discordClientId = FindConfigValue<std::string>(document, "discord", "client_id");
|
||||
@@ -669,6 +643,11 @@ inline bool SetSkipUnreadyPipelines(bool value) {
|
||||
return WriteSetting("video", "skip_unready_pipelines", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline bool SetVsync(bool value) {
|
||||
Mutable().vsync = value;
|
||||
return WriteSetting("video", "vsync", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline bool SetDisableCopyFilter(bool value) {
|
||||
Mutable().disableCopyFilter = value;
|
||||
return WriteSetting("video", "disable_copy_filter", value ? "true" : "false");
|
||||
@@ -844,56 +823,70 @@ inline bool AudioMixWorkerEnabled(bool fallback = true) {
|
||||
return Get().audioMixWorker.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool GxWorkerEnabled(bool fallback = true) {
|
||||
return Get().gxWorker.value_or(fallback);
|
||||
}
|
||||
|
||||
// Whether background music should duck automatically for other media playback.
|
||||
inline bool AttenuateMusicWhenMediaPlays(bool fallback = false) {
|
||||
return Get().attenuateMusicWhenMediaPlays.value_or(fallback);
|
||||
}
|
||||
|
||||
// Bluetooth Wii Remotes / Wii U Pro Controllers. Read once before SDL's joystick
|
||||
// subsystem comes up, so a change only takes effect on the next launch.
|
||||
inline bool WiiRemotesEnabled(bool fallback = true) {
|
||||
// Bluetooth Wii Remotes. Read once before SDL's joystick subsystem comes up, so a
|
||||
// change only takes effect on the next launch.
|
||||
inline bool WiiRemotesEnabled(bool fallback = false) {
|
||||
return Get().wiiRemotes.value_or(fallback);
|
||||
}
|
||||
|
||||
// Persists the Bluetooth Wii Remote driver switch.
|
||||
inline bool SetWiiRemotesEnabled(bool value) {
|
||||
Mutable().wiiRemotes = value;
|
||||
return WriteSetting("controller", "wii_remotes", value ? "true" : "false");
|
||||
}
|
||||
|
||||
// Whether to keep rescanning Bluetooth while no Wii controller is connected.
|
||||
inline bool WiiContinuousScanEnabled(bool fallback = false) {
|
||||
return Get().wiiContinuousScan.value_or(fallback);
|
||||
inline bool WiiAutoPairEnabled(bool fallback = true) {
|
||||
return Get().wiiAutoPair.value_or(fallback);
|
||||
}
|
||||
|
||||
// Persists the continuous scanning switch.
|
||||
inline bool SetWiiContinuousScanEnabled(bool value) {
|
||||
Mutable().wiiContinuousScan = value;
|
||||
return WriteSetting("controller", "wii_continuous_scan", value ? "true" : "false");
|
||||
inline bool SetWiiAutoPairEnabled(bool value) {
|
||||
Mutable().wiiAutoPair = value;
|
||||
return WriteSetting("controller", "wii_auto_pair", value ? "true" : "false");
|
||||
}
|
||||
|
||||
// Stored correction of the remote identified by `remoteKey`, if it has one.
|
||||
inline std::optional<std::array<double, 3>> WiiAccelOffsetFor(const std::string& remoteKey) {
|
||||
const auto it = Get().wiiAccelOffsets.find(remoteKey);
|
||||
if (it == Get().wiiAccelOffsets.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return it->second;
|
||||
// Defaults are the console's.
|
||||
inline bool WiiSensorBarAbove(bool fallback = false) {
|
||||
return Get().wiiSensorBarAbove.value_or(fallback);
|
||||
}
|
||||
|
||||
// Whether to write the per-frame accelerometer trace (off unless asked for).
|
||||
inline bool WiiAccelTraceEnabled(bool fallback = false) {
|
||||
return Get().wiiAccelTrace.value_or(fallback);
|
||||
inline bool SetWiiSensorBarAbove(bool value) {
|
||||
Mutable().wiiSensorBarAbove = value;
|
||||
return WriteSetting("controller", "wii_sensor_bar", value ? "\"above\"" : "\"below\"");
|
||||
}
|
||||
|
||||
// Persists the accelerometer correction of one remote. `remoteKey` must be a bare TOML key.
|
||||
inline bool SetWiiAccelOffsetFor(const std::string& remoteKey, const std::array<double, 3>& offset) {
|
||||
Mutable().wiiAccelOffsets[remoteKey] = offset;
|
||||
// Always float literals, so a whole-number offset does not come back as a TOML integer.
|
||||
std::ostringstream formatted;
|
||||
formatted << std::fixed << std::setprecision(4) << '[' << offset[0] << ", " << offset[1] << ", " << offset[2]
|
||||
<< ']';
|
||||
return WriteSetting("controller.wii_accel_offsets", remoteKey, formatted.str());
|
||||
inline int32_t WiiIrSensitivity(int32_t fallback = 3) {
|
||||
return Get().wiiIrSensitivity.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool SetWiiIrSensitivity(int32_t value) {
|
||||
Mutable().wiiIrSensitivity = value;
|
||||
return WriteSetting("controller", "wii_ir_sensitivity", std::to_string(value));
|
||||
}
|
||||
|
||||
inline int32_t WiiSpeakerVolume(int32_t fallback = 0x59) {
|
||||
return Get().wiiSpeakerVolume.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool SetWiiSpeakerVolume(int32_t value) {
|
||||
Mutable().wiiSpeakerVolume = value;
|
||||
return WriteSetting("controller", "wii_speaker_volume", std::to_string(value));
|
||||
}
|
||||
|
||||
inline bool WiiRumbleEnabled(bool fallback = true) {
|
||||
return Get().wiiRumble.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool SetWiiRumbleEnabled(bool value) {
|
||||
Mutable().wiiRumble = value;
|
||||
return WriteSetting("controller", "wii_rumble", value ? "true" : "false");
|
||||
}
|
||||
|
||||
// Target frame rate for frame interpolation, or 0 to disable it.
|
||||
@@ -906,6 +899,10 @@ inline bool SkipUnreadyPipelines(bool fallback = true) {
|
||||
return Get().skipUnreadyPipelines.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool Vsync(bool fallback = false) {
|
||||
return Get().vsync.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool DisableCopyFilter(bool fallback = true) {
|
||||
return Get().disableCopyFilter.value_or(fallback);
|
||||
}
|
||||
@@ -1018,6 +1015,9 @@ inline void LogLoadedConfig() {
|
||||
if (config.skipUnreadyPipelines) {
|
||||
std::cout << " skip_unready_pipelines=" << (*config.skipUnreadyPipelines ? "true" : "false");
|
||||
}
|
||||
if (config.vsync) {
|
||||
std::cout << " vsync=" << (*config.vsync ? "true" : "false");
|
||||
}
|
||||
if (config.disableCopyFilter) {
|
||||
std::cout << " disable_copy_filter=" << (*config.disableCopyFilter ? "true" : "false");
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ public:
|
||||
// Crash-path helpers shared by the centralized reporter in main.cpp.
|
||||
// DumpCrashHeuristics prints plain-language "likely cause" hints derived
|
||||
// from the register state; WriteGuestMemorySnapshot writes MEM1 to
|
||||
// `mem1Path` and MEM2 to `mem1Path + ".mem2"`, logging outcomes to `os`.
|
||||
// `mem1Path`, MEM2 to `mem1Path + ".mem2"` and the Kamek overlay to
|
||||
// `mem1Path + ".kamek"`, logging outcomes to `os`.
|
||||
static void DumpCrashHeuristics(std::ostream& os, const struct CpuContext* cpu,
|
||||
const uint32_t* missingGuestTarget);
|
||||
static void WriteGuestMemorySnapshot(std::ostream& os, const std::filesystem::path& mem1Path);
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
struct PADStatus;
|
||||
|
||||
// Real Wii Remotes paired over Bluetooth.
|
||||
//
|
||||
// SDL 3 ships a HIDAPI driver for them (Wii Remote alone, with Nunchuk, with a
|
||||
// Classic Controller, and the Wii U Pro Controller) that exposes each as a
|
||||
// regular SDL gamepad; it is off by default on SDL's side and ConfigureSdlHints
|
||||
// turns it on. A remote, alone or with a Nunchuk or Classic Controller, is
|
||||
// handed to the game as a real Wii Remote with that extension: the KPAD/WPAD
|
||||
// HLE builds a KPADStatus (and the WPADCLStatus behind KPADGetUnifiedWpadStatus)
|
||||
// from it every frame (see ReadKpadSample), so the game's own code does wheelies,
|
||||
// tricks, Wii Wheel steering and the Classic Controller layout, and plugging an
|
||||
// extension in or out mid-game switches control scheme like on the console. Only
|
||||
// the Wii U Pro Controller, which has no Wii-era equivalent, goes through
|
||||
// aurora's PAD layer as a GameCube pad.
|
||||
namespace WiiRemoteInput {
|
||||
|
||||
enum class Kind : uint8_t {
|
||||
NotWii,
|
||||
Remote, // Wii Remote with no extension
|
||||
RemoteWithNunchuk,
|
||||
RemoteWithClassic,
|
||||
WiiUPro,
|
||||
};
|
||||
|
||||
// Must run before SDL's joystick subsystem is initialized (aurora does that
|
||||
// inside aurora_initialize); SDL only consults the hint on its first device scan.
|
||||
void ConfigureSdlHints(bool enabled);
|
||||
|
||||
// Classifies a gamepad by the name SDL's Wii driver reports for it.
|
||||
Kind KindForName(const char* gamepadName);
|
||||
// Classification of the SDL gamepad currently assigned to a game port.
|
||||
Kind KindForPort(uint32_t port);
|
||||
const char* KindLabel(Kind kind);
|
||||
|
||||
// One frame of a Wii Remote in the units KPAD uses.
|
||||
struct KpadSample {
|
||||
uint32_t hold = 0; // WPAD button bits (WPAD_BUTTON_*), Nunchuk C/Z included
|
||||
float acc[3] = {}; // remote accelerometer in g, KPAD frame (rest: y = -1)
|
||||
bool hasNunchuk = false;
|
||||
float stick[2] = {}; // Nunchuk stick, -1..1, +y up
|
||||
float nunchukAcc[3] = {};
|
||||
bool hasClassic = false;
|
||||
uint32_t clHold = 0; // WPAD_CL_BUTTON_* bits
|
||||
float clLStick[2] = {}; // Classic sticks, -1..1, +y up
|
||||
float clRStick[2] = {};
|
||||
int16_t clLStickRaw[2] = {}; // as WPADCLStatus reports them: -512..511, centre 0, +y up
|
||||
int16_t clRStickRaw[2] = {};
|
||||
uint8_t clTriggerL = 0; // 0..255; SDL only exposes the digital click
|
||||
uint8_t clTriggerR = 0;
|
||||
};
|
||||
|
||||
// What the game should see on `chan`: the controller SDL has there right now,
|
||||
// or, for a few seconds after a remote vanished, the kind it had. SDL's driver
|
||||
// destroys and re-creates the joystick when an extension is plugged in or out,
|
||||
// and the console never disconnects for that, so the gap is papered over with
|
||||
// neutral input instead of a "communications interrupted" prompt.
|
||||
Kind EffectiveKind(uint32_t chan);
|
||||
// True when the game reads `chan` through KPAD: a Wii Remote alone, with a
|
||||
// Nunchuk or with a Classic Controller (live or within the swap grace period).
|
||||
bool IsRemoteChannel(uint32_t chan);
|
||||
// Reads the current state of the remote on `chan`; false when IsRemoteChannel
|
||||
// is false. During the swap grace period the sample is neutral.
|
||||
bool ReadKpadSample(uint32_t chan, KpadSample& sample);
|
||||
|
||||
// Remote accelerometer for the overlay readout: the SDL sample in g after the
|
||||
// zero-point correction (x right across the face, y out of the button face, z
|
||||
// towards the user) and the KPAD vector built from it. False when the port has
|
||||
// no remote or SDL has not delivered a sample yet.
|
||||
bool ReadAccelDebug(uint32_t chan, float sdlG[3], float kpadAcc[3]);
|
||||
|
||||
// Residual zero-point trim on top of SDL's factory-calibrated decode, stored per
|
||||
// physical remote under [controller.wii_accel_offsets]. Expects the remote lying
|
||||
// still, buttons up; Poll() averages about a second and stores it for that remote.
|
||||
void StartAccelCalibration(uint32_t chan);
|
||||
// Forgets the stored correction of the remote on `chan`.
|
||||
void ClearAccelCalibration(uint32_t chan);
|
||||
bool IsAccelCalibrating(uint32_t chan);
|
||||
// 0..1 while a calibration is collecting samples.
|
||||
float AccelCalibrationProgress();
|
||||
// Correction in effect for the remote on `chan` (zeros when uncalibrated), or
|
||||
// nullopt when the port has no remote.
|
||||
std::optional<std::array<double, 3>> ReadAccelOffset(uint32_t chan);
|
||||
// Outcome of the last calibration attempt on `chan` for the overlay, or nullptr.
|
||||
const char* AccelCalibrationMessage(uint32_t chan);
|
||||
|
||||
// Reports "no controller" on the GameCube side for every port served through
|
||||
// KPAD, so the game never sees the same remote twice. Runs after aurora's PADRead.
|
||||
void HideRemotesFromPad(PADStatus* statuses, uint32_t count);
|
||||
|
||||
// Dolphin-style continuous scanning. SDL's HIDAPI Wii driver drops a remote on
|
||||
// a failed Bluetooth read or an extension change and never re-adds it on its
|
||||
// own. Poll() runs once per PADRead and, while no Wii controller is present,
|
||||
// periodically forces SDL to re-enumerate HIDAPI by toggling the driver hint.
|
||||
void Poll();
|
||||
// Forces one re-enumeration right now (settings overlay "Rescan now").
|
||||
void RescanNow();
|
||||
// True while Poll() is looking for a remote (no Wii controller connected).
|
||||
bool IsScanning();
|
||||
// True where looking means periodic rescans; elsewhere Poll() waits for hotplug.
|
||||
bool PeriodicRescanEnabled();
|
||||
// Rescans issued since a Wii controller was last seen.
|
||||
uint32_t ScanCount();
|
||||
|
||||
} // namespace WiiRemoteInput
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
// The Wii's extension controller encryption: the game writes a random key to
|
||||
// the extension, which then XORs and offsets every byte it reports.
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace Wiimote::ExtCrypto {
|
||||
|
||||
struct Tables {
|
||||
std::array<uint8_t, 8> ft{};
|
||||
std::array<uint8_t, 8> sb{};
|
||||
void Encrypt(uint8_t* data, uint32_t address, size_t length) const;
|
||||
void Decrypt(uint8_t* data, uint32_t address, size_t length) const;
|
||||
};
|
||||
|
||||
// Tables for the key the game wrote to registers 0x40..0x4F, or nullopt when it
|
||||
// does not come from the first-party key schedule.
|
||||
std::optional<Tables> FromKey(const std::array<uint8_t, 16>& keyData);
|
||||
|
||||
} // namespace Wiimote::ExtCrypto
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
// Follows the game's conversation with a remote's extension (type, calibration,
|
||||
// encryption key) so its data can be put at rest while the overlay owns input.
|
||||
|
||||
#include "wiimote_ext_crypto.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace Wiimote {
|
||||
|
||||
class ExtensionState {
|
||||
public:
|
||||
void ObserveOutput(const uint8_t* report, size_t size);
|
||||
void ObserveInput(const uint8_t* report, size_t size);
|
||||
// Centres the sticks and releases the buttons of the extension data in a
|
||||
// data report, leaving its motion data alone.
|
||||
void Neutralize(std::vector<uint8_t>& report) const;
|
||||
|
||||
private:
|
||||
enum class Type : uint8_t { Unknown, Nunchuk, Classic };
|
||||
|
||||
void Reset();
|
||||
uint8_t Calibration(size_t index, uint8_t fallback) const;
|
||||
|
||||
Type m_type = Type::Unknown;
|
||||
uint8_t m_format = 0;
|
||||
bool m_encrypted = false;
|
||||
std::array<uint8_t, 16> m_key{};
|
||||
std::optional<ExtCrypto::Tables> m_tables;
|
||||
std::array<uint8_t, 16> m_calibration{};
|
||||
bool m_haveCalibration = false;
|
||||
uint32_t m_readAddress = 0;
|
||||
};
|
||||
|
||||
} // namespace Wiimote
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
// Physical Wii Remotes on the PC. Each one is attached to one of the four
|
||||
// virtual remotes of the emulated Bluetooth controller and its HID reports are
|
||||
// passed through unchanged, so the game's own WPAD/KPAD drive it (IR, speaker,
|
||||
// rumble, extensions, battery).
|
||||
|
||||
#include "bluetooth/bt_controller.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace Wiimote {
|
||||
|
||||
struct SlotInfo {
|
||||
bool attached = false;
|
||||
// The game disconnected it and the platform could not power it off; any
|
||||
// button wakes it up again.
|
||||
bool asleep = false;
|
||||
Bluetooth::LinkState link = Bluetooth::LinkState::Down;
|
||||
std::string model; // "Wii Remote" / "Wii Remote Plus"
|
||||
std::string extension; // empty when none
|
||||
int battery = -1; // 0..4 like the game's battery bars, -1 unknown
|
||||
};
|
||||
|
||||
struct PairingInfo {
|
||||
bool supported = false; // this platform can discover and pair remotes itself
|
||||
bool searching = false;
|
||||
std::string status; // last pairing outcome, for the overlay
|
||||
};
|
||||
|
||||
// Must run before SDL's joystick subsystem starts (aurora_initialize).
|
||||
void ConfigureSdlHints(bool enabled);
|
||||
// Starts device scanning when Wii Remotes are enabled.
|
||||
void Initialize();
|
||||
// Powers the remotes off, like a console shutting down, and stops the threads.
|
||||
void Shutdown();
|
||||
|
||||
Bluetooth::RemoteHost& Host();
|
||||
|
||||
SlotInfo Slot(size_t slot);
|
||||
PairingInfo Pairing();
|
||||
// The console's SYNC button: look for remotes in pairing mode for a while.
|
||||
void StartPairing();
|
||||
// Ends the game's connection to the remote on `slot` and powers it off.
|
||||
void Disconnect(size_t slot);
|
||||
// Setup problems worth showing (kernel driver, missing permissions), else empty.
|
||||
std::string PlatformNotice();
|
||||
// Connected remotes waiting for a free player slot.
|
||||
size_t WaitingRemotes();
|
||||
|
||||
} // namespace Wiimote
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
// Host Bluetooth services the Wii Remote manager needs from the OS: finding and
|
||||
// pairing remotes in pairing mode, naming a HID device, and dropping a remote's
|
||||
// connection (which powers it off, as a console does).
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
struct SDL_hid_device_info;
|
||||
|
||||
namespace Wiimote::Platform {
|
||||
|
||||
struct Capabilities {
|
||||
bool pairing = false;
|
||||
bool powerOff = false;
|
||||
};
|
||||
|
||||
Capabilities Query();
|
||||
|
||||
// One search for remotes in pairing mode (red SYNC or 1+2); pairs and connects
|
||||
// each one found. Blocks for a few seconds. Returns the number connected.
|
||||
int DiscoverAndPair(const std::atomic<bool>& cancel, std::string& status);
|
||||
|
||||
// Disconnects the remote; it switches itself off. False when not possible.
|
||||
bool PowerOff(const std::string& address);
|
||||
|
||||
// Bluetooth address of a HID device as 12 lowercase hex digits, or empty.
|
||||
std::string AddressFor(const SDL_hid_device_info& info);
|
||||
|
||||
// Bluetooth name ("Nintendo RVL-CNT-01-TR", ...) or the HID product string.
|
||||
std::string NameFor(const std::string& address, const SDL_hid_device_info& info);
|
||||
|
||||
// False only when the OS knows the remote is not connected right now.
|
||||
bool MaybeConnected(const std::string& address);
|
||||
|
||||
// False when the OS driver would fight over the device (Linux hid-wiimote).
|
||||
bool Usable(const SDL_hid_device_info& info);
|
||||
|
||||
// Setup problems worth showing in the overlay, else empty.
|
||||
std::string Notice();
|
||||
|
||||
} // namespace Wiimote::Platform
|
||||
Reference in New Issue
Block a user