refactor: move iOS frontend to platforms/ios on single shared core

Snapshot the iOS-refresh app (React Native shell + Objective-C/Swift iOS
runtime: AppDelegate, SceneDelegate, GamepadHaptics, ARMSX2Bridge) into
platforms/ios/. Delete its vendored PCSX2 core (794 pcsx2 + 150 common files)
and its vendored 3rdparty (~11.8k files); the repo-root core is the single
source of truth.

Relocate the genuine iOS-only core additions (MacOSStubs, QAProbe, TestHarness,
SifRingBuffer.h, common/PNGStub.cpp) into the root core, guarded by an
ARMSX2_IOS (CMAKE_SYSTEM_NAME==iOS) branch in pcsx2/CMakeLists.txt. The
NEON SPU2 sources are now shared by both arm64 mobile targets (ANDROID OR iOS).
Dropped cruft: *.try/*.ref backups, stray x86/microVU_impl.cpp, leaked
common/Android/* JNI files, and the adrenotools submodule (android-only).

Rewire the iOS native CMake to source the root {common,pcsx2,3rdparty} via an
ARMSX2_ROOT var, preserving all iOS-SDK/bundle/JIT-entitlement/Metal config.

NOT yet compiled against Xcode -- build validation is CI's job.
See REFACTOR_STATUS.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
David Isztl
2026-07-08 20:32:58 +02:00
co-authored by Claude Opus 4.8
parent 2eb9ec659c
commit f17460df0d
901 changed files with 313254 additions and 6 deletions
+11
View File
@@ -0,0 +1,11 @@
// [P63] PNG stubs for macOS build (libpng not linked)
#include <TargetConditionals.h>
#if !TARGET_OS_IPHONE
#include "Image.h"
#include <cstdio>
#include <vector>
bool PNGFileLoader(RGBA8Image* img, const char* fn, FILE* fp) { return false; }
bool PNGFileSaver(const RGBA8Image& img, const char* fn, FILE* fp, u8 q) { return false; }
bool PNGBufferLoader(RGBA8Image* img, const void* buf, size_t len) { return false; }
bool PNGBufferSaver(const RGBA8Image& img, std::vector<u8>* buf, u8 q) { return false; }
#endif
+26 -6
View File
@@ -1085,12 +1085,16 @@ set(pcsx2arm64Headers
arm64/aVU_Compile.inl
)
# Android platform sources, relocated from the former android core fork
# (refresh-experimental). Source wiring only: a real NDK build is required to
# validate include paths and link deps. See REFACTOR_STATUS.md.
if(ANDROID)
list(APPEND pcsx2HostSources
Host/OboeAudioStream.cpp)
# Mobile (arm64) platform sources, relocated from the former android/iOS core
# forks (refresh-experimental / iOS-refresh). Source wiring only: real NDK /
# Xcode builds are required to validate include paths and link deps.
# See REFACTOR_STATUS.md.
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(ARMSX2_IOS TRUE)
endif()
# NEON-optimised SPU2 mixer/reverb: shared by all arm64 mobile targets.
if(ANDROID OR ARMSX2_IOS)
list(APPEND pcsx2SPU2Sources
SPU2/spu2_neon.cpp)
list(APPEND pcsx2SPU2Headers
@@ -1101,6 +1105,11 @@ if(ANDROID)
SPU2/spu2_optimize.h
SPU2/spu2_sve2_fir.h
SPU2/spu2_mt6899_tuning.h)
endif()
if(ANDROID)
list(APPEND pcsx2HostSources
Host/OboeAudioStream.cpp)
list(APPEND pcsx2GSSources
GS/Renderers/OpenGL/GLContextEGLAndroid.cpp
GS/Renderers/Common/GSGPUProfile.cpp)
@@ -1120,6 +1129,17 @@ if(ANDROID)
PS1DrvTrace.h)
endif()
if(ARMSX2_IOS)
list(APPEND pcsx2Sources
MacOSStubs.cpp
QAProbe.cpp
TestHarness.cpp)
list(APPEND pcsx2Headers
QAProbe.h
TestHarness.h
SifRingBuffer.h)
endif()
# These ones benefit a lot from LTO
set(pcsx2LTOSources
${pcsx2Sources}
+129
View File
@@ -0,0 +1,129 @@
// [P63] Stubs for macOS native build — symbols not available without full UI
#include <TargetConditionals.h>
#if !TARGET_OS_IPHONE
#include "Host.h"
#include "VMManager.h"
#include "Input/InputManager.h"
#include "DEV9/pcap_io.h"
#include "Achievements.h"
#include "common/HTTPDownloader.h"
#include "common/ProgressCallback.h"
#include "common/FileSystem.h"
#include "Host/AudioStream.h"
#include "INISettingsInterface.h"
// --- PNG (already in common/PNGStub.cpp) ---
// --- FileSystem ---
int FileSystem::OpenFDFileContent(const char* path) { return -1; }
// --- x86 emitter symbols (never called on ARM64, but referenced by JIT stubs) ---
// Non-const → external linkage; Itanium ABI mangles by name only, type doesn't matter
namespace x86Emitter {
char xmm0[16]={}, xmm1[16]={}, xmm2[16]={}, xmm3[16]={}, xmm4[16]={}, xmm5[16]={};
char xmm6[16]={}, xmm7[16]={}, xmm8[16]={}, xmm9[16]={}, xmm10[16]={};
char xmm11[16]={}, xmm12[16]={}, xmm13[16]={}, xmm14[16]={}, xmm15[16]={};
}
// --- g_xmmtypes (thread-local, referenced by iCore.o) ---
enum { _XMMT_INT = 0 };
thread_local int g_xmmtypes[16] = {_XMMT_INT};
// --- Audio: Oboe is Android-only ---
std::unique_ptr<AudioStream> AudioStream::CreateOboeAudioStream(u32 sr, const AudioStreamParameters& p, bool s, Error* e) { return nullptr; }
// --- Network: PCAP not available ---
PCAPAdapter::PCAPAdapter() : NetAdapter() {}
// --- HTTP: curl not linked ---
std::unique_ptr<HTTPDownloader> HTTPDownloader::Create(std::string ua) { return nullptr; }
// --- Input: keyboard mapping ---
std::optional<std::string> InputManager::ConvertHostKeyboardCodeToString(u32 c) { return std::nullopt; }
const char* InputManager::ConvertHostKeyboardCodeToIcon(u32 c) { return nullptr; }
std::optional<u32> InputManager::ConvertHostKeyboardStringToCode(std::string_view s) { return std::nullopt; }
// --- Settings ---
INISettingsInterface* g_p44_settings_interface = nullptr;
// --- Host functions (declared in Host.h) ---
bool Host::InNoGUIMode() { return true; }
void Host::RunOnCPUThread(std::function<void()> f, bool b) { if (f) f(); }
void Host::RequestVMShutdown(bool a, bool b, bool c) {}
bool Host::RequestResetSettings(bool a, bool b, bool c, bool d, bool e) { return false; }
void Host::CancelGameListRefresh() {}
void Host::RefreshGameListAsync(bool i) {}
void Host::CommitBaseSettingChanges() {}
bool Host::ConfirmMessage(std::string_view t, std::string_view m) { return true; }
void Host::ReportErrorAsync(std::string_view t, std::string_view m) { fprintf(stderr, "[Error] %.*s: %.*s\n", (int)t.size(), t.data(), (int)m.size(), m.data()); }
void Host::ReportInfoAsync(std::string_view t, std::string_view m) {}
void Host::OpenURL(std::string_view u) {}
bool Host::CopyTextToClipboard(std::string_view t) { return false; }
std::unique_ptr<ProgressCallback> Host::CreateHostProgressCallback() { return nullptr; }
std::string Host::TranslatePluralToString(const char* ctx, const char* msg, const char* mpl, int n) { return msg; }
s32 Host::Internal::GetTranslatedStringImpl(std::string_view ctx, std::string_view msg, char* buf, size_t sz) {
s32 len = std::min((s32)msg.size(), (s32)(sz - 1));
std::memcpy(buf, msg.data(), len);
buf[len] = 0;
return len;
}
// --- Host functions (declared in VMManager.h / other headers) ---
namespace Host {
void BeginPresentFrame() {}
// AcquireRenderWindow is in ios_main.mm (macOS section) with correct return type
void ReleaseRenderWindow() {}
void BeginTextInput() {}
void EndTextInput() {}
bool IsFullscreen() { return false; }
void SetFullscreen(bool f) {}
void SetMouseMode(bool r, bool h) {}
void OnVMStarting() {}
void OnVMStarted() {}
void OnVMDestroyed() {}
void OnVMPaused() {}
void OnVMResumed() {}
void OnGameChanged(const std::string& d, const std::string& e, const std::string& t, const std::string& s, u32 c, u32 r) {}
void OnPerformanceMetricsUpdated() {}
void OnSaveStateLoading(std::string_view f) {}
void OnSaveStateLoaded(std::string_view f, bool w) {}
void OnSaveStateSaved(std::string_view f) {}
void OnAchievementsHardcoreModeChanged(bool e) {}
void OnAchievementsLoginRequested(Achievements::LoginRequestReason r) {}
void OnAchievementsLoginSuccess(const char* u, u32 p, u32 sc, u32 us) {}
void OnAchievementsRefreshed() {}
void OnCoverDownloaderOpenRequested() {}
void OnCreateMemoryCardOpenRequested() {}
void OnInputDeviceConnected(std::string_view i, std::string_view d) {}
void OnInputDeviceDisconnected(InputBindingKey k, std::string_view i) {}
void PumpMessagesOnCPUThread() {}
void RequestExitApplication(bool a) {}
void RequestExitBigPicture() {}
void CheckForSettingsChanges(const Pcsx2Config& c) {}
void LoadSettings(SettingsInterface& si, std::unique_lock<std::mutex>& lock) {}
bool ShouldPreferHostFileSelector() { return false; }
void OpenHostFileSelectorAsync(std::string_view t, bool d, std::function<void(const std::string&)> cb, std::vector<std::string> f, std::string_view i) {}
bool LocaleCircleConfirm() { return false; }
}
// iOS-specific globals (referenced by iPSX2Bridge.mm)
#include <map>
#include <string>
int g_touchPadState = 0;
bool s_captureMode = false;
int s_capturedButton = -1;
bool s_requestVMStop = false;
extern "C" void iPSX2_SetSDLFullscreen(bool) {}
std::map<std::string, int> s_buttonMap;
// PCAPAdapter vtable
PCAPAdapter::~PCAPAdapter() {}
bool PCAPAdapter::blocks() { return false; }
bool PCAPAdapter::isInitialised() { return false; }
bool PCAPAdapter::recv(NetPacket* p) { return false; }
bool PCAPAdapter::send(NetPacket* p) { return false; }
void PCAPAdapter::reloadSettings() {}
#endif // !TARGET_OS_IPHONE
+175
View File
@@ -0,0 +1,175 @@
// QAProbe — implementation. See QAProbe.h for design.
#include "QAProbe.h"
#include "common/Console.h"
#include "MTGS.h"
#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
#include <string>
#include <vector>
extern uint32_t getVif1CmdUnpack(); // Vif_Transfer.cpp
namespace
{
// -------- env-driven config (parsed once, on first vsync) --------
// All defaults OFF: release ship 構成では QA probe を全 dormant 化、
// env で明示的に opt-in した場合のみ counter / log / SS が動作する。
bool s_inited = false;
std::set<u32> s_ss_vs;
std::string s_tag = "run";
bool s_gif_dump = false;
u64 s_gif_dump_quota = 2000000;
bool s_qa_log = false;
// -------- cumulative counters --------
std::atomic<u64> s_cum_gif_pkt{0};
std::atomic<u64> s_cum_d_prim{0};
std::atomic<u64> s_cum_d_unp{0};
std::atomic<u64> s_cum_d_bytes{0};
std::atomic<u64> s_gif_dump_emitted{0};
// per-vsync deltas (reset each VSyncEnd)
std::atomic<u64> s_vs_gif_pkt{0};
std::atomic<u64> s_vs_d_prim{0};
std::atomic<u64> s_vs_d_unp{0};
std::atomic<u64> s_vs_d_bytes{0};
void parse_csv_u32(const char* env, std::set<u32>& out)
{
if (!env || !*env) return;
const char* p = env;
while (*p)
{
char* end = nullptr;
unsigned long v = std::strtoul(p, &end, 10);
if (end == p) break;
out.insert(static_cast<u32>(v));
p = end;
while (*p == ',' || *p == ' ') ++p;
}
}
void init_once()
{
if (s_inited) return;
s_inited = true;
if (const char* e = std::getenv("iPSX2_QA_TAG"); e && *e) s_tag = e;
if (const char* e = std::getenv("iPSX2_SS_AT_VS"); e && *e) parse_csv_u32(e, s_ss_vs);
if (const char* e = std::getenv("iPSX2_GIF_DUMP"); e && *e == '1') s_gif_dump = true;
if (const char* e = std::getenv("iPSX2_GIF_DUMP_QUOTA"); e && *e)
s_gif_dump_quota = std::strtoull(e, nullptr, 10);
if (const char* e = std::getenv("iPSX2_QA_LOG"); e && *e == '1') s_qa_log = true;
// Init log only emitted when any QA feature is active (= one-time, never spams).
if (s_qa_log || s_gif_dump || !s_ss_vs.empty())
{
Console.WriteLn("@@QA_INIT@@ tag=%s ss_vs_count=%zu gif_dump=%d quota=%llu qa_log=%d",
s_tag.c_str(), s_ss_vs.size(), (int)s_gif_dump,
(unsigned long long)s_gif_dump_quota, (int)s_qa_log);
}
}
// FNV1a-32 over packet bytes (fast, deterministic)
u32 fnv1a32(const u8* p, u32 n)
{
u32 h = 0x811c9dc5u;
for (u32 i = 0; i < n; ++i) { h ^= p[i]; h *= 0x01000193u; }
return h;
}
// Best-effort RGBA dump: 8-byte header [w,h u32 LE] + W*H*4 bytes.
void dump_ss(u32 vs)
{
u32 w = 0, h = 0;
std::vector<u32> pixels;
if (!MTGS::SaveMemorySnapshot(640, 480, true, false, &w, &h, &pixels))
{
Console.WriteLn("@@QA_SS@@ vs=%u snapshot FAILED", vs);
return;
}
char path[256];
std::snprintf(path, sizeof(path), "/tmp/qa_ss_%s_vs%u.rgba", s_tag.c_str(), vs);
FILE* f = std::fopen(path, "wb");
if (!f) { Console.WriteLn("@@QA_SS@@ vs=%u fopen failed: %s", vs, path); return; }
u32 hdr[2] = {w, h};
std::fwrite(hdr, sizeof(u32), 2, f);
std::fwrite(pixels.data(), 4, (size_t)w * h, f);
std::fclose(f);
Console.WriteLn("@@QA_SS@@ vs=%u path=%s size=%ux%u", vs, path, w, h);
}
}
namespace QAProbe
{
void on_vsync_end(u32 frame_count)
{
init_once();
// Fast path: when QA log + SS dump 両方 OFF なら全 skip。
// release ship default ではこの path で常時 early return (= per-vsync ゼロ cost)。
if (!s_qa_log && s_ss_vs.empty()) return;
const u32 vs = frame_count + 1; // VSyncEnd increments g_FrameCount; align to "vs about to start"
if (s_qa_log)
{
const u64 cum_pkt = s_cum_gif_pkt.load(std::memory_order_relaxed);
const u64 cum_prim = s_cum_d_prim.load(std::memory_order_relaxed);
const u64 d_pkt = s_vs_gif_pkt.exchange(0, std::memory_order_relaxed);
const u64 d_prim = s_vs_d_prim.exchange(0, std::memory_order_relaxed);
const u64 d_unp = s_vs_d_unp.exchange(0, std::memory_order_relaxed);
const u64 d_bytes = s_vs_d_bytes.exchange(0, std::memory_order_relaxed);
const u32 vif1_unp = getVif1CmdUnpack();
(void)cum_prim;
Console.WriteLn(
"@@BL_FRAME@@ vs=%u gif_pkt=%llu d_pkt=%llu d_prim=%llu d_unp=%llu d_bytes=%llu vif1_unpack=%u",
vs,
(unsigned long long)cum_pkt,
(unsigned long long)d_pkt,
(unsigned long long)d_prim,
(unsigned long long)d_unp,
(unsigned long long)d_bytes,
vif1_unp);
}
if (!s_ss_vs.empty() && s_ss_vs.count(vs)) dump_ss(vs);
}
void on_gif_transfer(u32 tran_type, u32 path, const u8* pMem, u32 size)
{
// Fast path: env で QA log も GIF dump も off の場合は全 skip。
// release ship default ではこの path で常時 early return (= per-transfer ゼロ cost)。
if (!s_qa_log && !s_gif_dump) return;
if (s_qa_log)
{
// Cumulative + per-vsync counters (only when @@BL_FRAME@@ log consumer exists)
s_cum_gif_pkt.fetch_add(1, std::memory_order_relaxed);
s_vs_gif_pkt.fetch_add(1, std::memory_order_relaxed);
s_cum_d_bytes.fetch_add(size, std::memory_order_relaxed);
s_vs_d_bytes.fetch_add(size, std::memory_order_relaxed);
s_cum_d_prim.fetch_add(1, std::memory_order_relaxed);
s_vs_d_prim.fetch_add(1, std::memory_order_relaxed);
if (path == 1)
{
s_cum_d_unp.fetch_add(size, std::memory_order_relaxed);
s_vs_d_unp.fetch_add(size, std::memory_order_relaxed);
}
}
// Optional verbose GIF packet dump (env-gated).
if (!s_gif_dump || !pMem || !size) return;
const u64 emitted = s_gif_dump_emitted.fetch_add(1, std::memory_order_relaxed);
if (emitted >= s_gif_dump_quota) return;
const u32 crc = fnv1a32(pMem, size);
Console.WriteLn("@@GIF_PKT@@ seq=%llu path=%u tran=%u size=%u crc=%08x",
(unsigned long long)emitted, path, tran_type, size, crc);
}
}
+37
View File
@@ -0,0 +1,37 @@
// QAProbe — Quality Assurance probe module for iPSX2 (V34 baseline, 2026-05-25)
//
// Purpose: thin hook-based probe infrastructure for automated JIT vs Interpreter
// regression gates. All probes are env-gated; when env vars unset, hooks are 1-line
// no-op early-returns with negligible cost.
//
// Hook points (3 total, all 1-line calls into this module):
// 1. Counters.cpp VSyncEnd -> QAProbe::on_vsync_end()
// 2. Gif_Unit.h TransferGSPacketData entry -> QAProbe::on_gif_transfer()
// 3. (optional) QAProbe::on_gif_primitive() called per-PRIM emit
//
// Env vars consumed:
// iPSX2_SS_AT_VS=N1,N2,... : at each listed vs, dump 640x480 RGBA framebuffer
// to /tmp/qa_ss_<TAG>_vs<N>.rgba
// iPSX2_QA_TAG=<tag> : tag prefix for output filenames (default: "run")
// iPSX2_GIF_DUMP=1 : emit @@GIF_PKT@@ per transfer with CRC
// iPSX2_GIF_DUMP_QUOTA=N : cap @@GIF_PKT@@ count (default 2000000)
// iPSX2_QA_LOG=1 : emit @@BL_FRAME@@ line per vsync end (default ON)
//
// Output (consumed by Scripts/eval_bench.py, gif_diff.py, ss_compare.py):
// @@BL_FRAME@@ vs=N gif_pkt=N d_pkt=N d_prim=N d_unp=N d_bytes=N vif1_unpack=N
// @@GIF_PKT@@ vs=N seq=N path=N tran=N size=N crc=HEX
// @@QA_SS@@ vs=N path=/tmp/qa_ss_<tag>_vs<N>.rgba size=WxH
#pragma once
#include "common/Pcsx2Types.h"
namespace QAProbe
{
// Called from Counters.cpp::VSyncEnd. frame_count = pre-increment g_FrameCount.
void on_vsync_end(u32 frame_count);
// Called from Gif_Unit.h::TransferGSPacketData on every transfer.
// path: 0-2 (GIF_PATH_1/2/3 = 0/1/2). pMem may be null when size==0.
void on_gif_transfer(u32 tran_type, u32 path, const u8* pMem, u32 size);
}
+74
View File
@@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
// [P34] SIF/RPC イベント ring buffer — タイムアウト診断用
// iPSX2_SIF_RING=1 でenabled化。デフォルト OFF。
#include "common/Pcsx2Types.h"
#include <cstdlib>
#include <atomic>
namespace SifRing {
enum EventType : u8 {
BIND_REQ = 1, // EE sceSifBindRpc
CALL_REQ = 2, // EE sceSifCallRpc
RPC_REGISTER = 3, // IOP sceSifRegisterRpc
ICTRL_CHANGE = 4, // 0x1078 change
ISTAT_SET = 5, // iopIntcIrq
ISTAT_CONSUME = 6, // iopEventTest consume
PSXDMA9 = 7, // IOP→EE SIF0
PSXDMA10 = 8, // EE→IOP SIF1 (IOP side)
IOP_TAG = 9, // ProcessIOPTag
EE_XFER = 10, // HandleEETransfer
GAME_EXIT = 11, // EE → EELOAD idle
};
struct Event {
u8 type;
u8 pad;
u16 pad2;
u32 ee_cyc;
u32 iop_cyc;
u32 d0;
u32 d1;
};
static constexpr int RING_SIZE = 256;
static constexpr int RING_MASK = RING_SIZE - 1;
inline Event g_ring[RING_SIZE];
inline u32 g_idx = 0;
inline bool g_enabled = false;
inline bool g_initialized = false;
inline bool IsEnabled() {
if (!g_initialized) {
g_initialized = true;
const char* v = getenv("iPSX2_SIF_RING");
g_enabled = (v && v[0] == '1');
}
return g_enabled;
}
// 外部から ee_cyc / iop_cyc を渡す (ヘッダ依存を避けるため)
inline void Record(u8 type, u32 ee_cyc, u32 iop_cyc, u32 d0, u32 d1) {
if (!IsEnabled()) return;
u32 i = g_idx++ & RING_MASK;
g_ring[i] = {type, 0, 0, ee_cyc, iop_cyc, d0, d1};
}
// ダンプ (Console.WriteLn はcall側で行う)
inline void DumpTo(void (*emit)(const char*, u32, u32, u32, u32, u32, u32)) {
if (!IsEnabled()) return;
u32 start = (g_idx > RING_SIZE) ? (g_idx - RING_SIZE) : 0;
u32 end = g_idx;
for (u32 i = start; i < end; i++) {
const Event& e = g_ring[i & RING_MASK];
emit("@@SIF_RING@@ type=%u ee=%u iop=%u d0=%08x d1=%08x seq=%u",
(u32)e.type, e.ee_cyc, e.iop_cyc, e.d0, e.d1, i);
}
}
} // namespace SifRing
File diff suppressed because it is too large Load Diff
+64
View File
@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
// [P30] iOS Native EE Test Harness
// BIOS/SIF/IOP 非依存で EE JIT 命令精度を検証する。
// iPSX2_TEST_HARNESS=1 でenabled化。デフォルト OFF。
//
// テストコード (MIPS R5900 マシンコード) を eeMem に直接書き込み、
// cpuRegs.pc をconfigして実行。resultは EE memoryに書き込まれ、
// vsync handlerがログ出力する。
#include "common/Pcsx2Types.h"
#include <string>
namespace TestHarness
{
// テストresultmemoryレイアウト
// kseg0 address (TLB not needed、物理addressに直接mapping)
// 0x01F00000 (31MB) は BIOS/OSDSYS ワークエリア外
static constexpr u32 PHYS_CODE = 0x01F00000u; // eeMem 書き込み先 (物理)
static constexpr u32 PHYS_RESULT = 0x01FF0000u;
static constexpr u32 CODE_BASE = 0x81F00000u; // EE PC 用 kseg0 address
static constexpr u32 RESULT_BASE = 0x81FF0000u;
static constexpr u32 STACK_TOP = 0x81FE0000u;
// ヘッダ (CODE_BASE に配置)
struct Header {
u32 magic; // 0x54455354 ("TEST")
u32 test_count;
u32 pass_count;
u32 fail_count;
u32 current_test;
u32 status; // 0=running, 1=complete, 2=error
};
// 個別テストresult (RESULT_BASE + n*16)
struct Result {
u32 test_id;
u32 expected;
u32 actual;
u32 pass; // 1=pass, 0=fail
};
// テストハーネスがenabledかどうか
bool IsEnabled();
// テストコードを eeMem に注入し cpuRegs.pc をconfig
// eeloadHook n=1 のタイミングで呼ばれる
void InjectTests();
// vsync でresultを読み取りログ出力
// 戻り値: true=テスト完了 (status==1)
bool CheckResults(u32 vsync_count);
// Force inject (sets flag, actual inject happens at next vsync on CPU thread)
void ForceInject();
void ForceInjectMini(); // Mini stress test for hang investigation
bool CheckForceInject();
// Get last results as string
std::string GetResultsString();
}
@@ -0,0 +1,100 @@
name: Game Emulation Bug Report
description: Problem in a game. (ie. graphical artifacts, crashes, etc.)
title: '[GAME BUG]: '
labels:
- bug
body:
- type: markdown
attributes:
value: |
## Important: Read First
Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only.
If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/KwAChKDctz).
Do not create issues involving software piracy of BIOS or ISO files, our rules specifically prohibit this and your issue will be closed.
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched for a similar issue in this repository and did not find one.
required: true
- type: input
id: game
attributes:
label: Game Title
placeholder: 'e.g. Dragon Ball Z: Budokai 2'
validations:
required: true
- type: input
id: emu-ver
attributes:
label: ARMSX2 Version
description: Please ensure you are on the latest version before making an issue.
placeholder: e.g. v1.0.4
validations:
required: true
- type: dropdown
id: renderer
attributes:
label: Renderer (Graphics Backend)
options:
- Vulkan
- OpenGL
- Software
validations:
required: true
- type: input
id: device
attributes:
label: Device Model
placeholder: e.g. Google Pixel 4
validations:
required: true
- type: input
id: os-ver
attributes:
label: OS Version
placeholder: e.g. Android 12
validations:
required: false
- type: textarea
id: desc
attributes:
label: Describe the Bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Reproduction Steps
description: Steps to reproduce the behavior.
validations:
required: true
- type: textarea
id: expect
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
validations:
required: false
- type: textarea
id: log
attributes:
label: Log File
description: A log file will help our developers to better diagnose and fix the issue.
validations:
required: false
@@ -0,0 +1,81 @@
name: Application Bug Report
description: Problem with the application itself. (ie. bad file path handling, UX issue)
title: '[APP BUG]: '
labels:
- bug
body:
- type: markdown
attributes:
value: |
## Important: Read First
Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only.
If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/KwAChKDctz).
Do not create issues involving software piracy of BIOS or ISO files, our rules specifically prohibit this and your issue will be closed.
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched for a similar issue in this repository and did not find one.
required: true
- type: input
id: emu-ver
attributes:
label: ARMSX2 Version
description: Please ensure you are on the latest version before making an issue.
placeholder: e.g. v1.0.4
validations:
required: true
- type: input
id: device
attributes:
label: Device Model
placeholder: e.g. Google Pixel 4
validations:
required: true
- type: input
id: os-ver
attributes:
label: OS Version
placeholder: e.g. Android 12
validations:
required: true
- type: textarea
id: desc
attributes:
label: Describe the Bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Reproduction Steps
description: Steps to reproduce the behavior.
validations:
required: true
- type: textarea
id: expect
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
validations:
required: false
- type: textarea
id: log
attributes:
label: Log File
description: A log file will help our developers to better diagnose and fix the issue.
validations:
required: false
@@ -0,0 +1,57 @@
name: Feature Request
description: Suggest a new feature or improve an existing one.
title: '[Feature Request]: '
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
## Important: Read First
Please make an effort to make sure your issue isn't already reported.
Do not create issues involving software piracy of BIOS or ISO files, our rules specifically prohibit this and your issue will be closed.
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I have searched for a similar issue in this repository and did not find one.
required: true
- type: textarea
id: desc
attributes:
label: Description
description: |
A concise description of the feature you want.
Include step by step examples of how the feature should work under various circumstances.
validations:
required: true
- type: textarea
id: reason
attributes:
label: Reason
description: |
Give a reason why you want this feature.
- How will it make things easier for you?
- How does this feature help your enjoyment of the emulator?
- What does it provide that isn't being provided currently?
validations:
required: true
- type: textarea
id: examples
attributes:
label: Examples
description: |
Provide examples of the feature as implemented by other software.
Include screenshots or video if you like to help demonstrate how you'd like this feature to work.
validations:
required: true
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Discord
url: https://discord.gg/KwAChKDctz
about: Get direct support and hang out with us.
@@ -0,0 +1,124 @@
name: Android Nightly Debug Build
on:
push:
branches:
- master
paths-ignore:
- '**/*.md'
pull_request:
branches:
- master
paths-ignore:
- '**/*.md'
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
outputs:
package_name: ${{ steps.package_info.outputs.PACKAGE_NAME }}
version_name: ${{ steps.package_info.outputs.VERSION_NAME }}
release_tag: ${{ steps.release_vars.outputs.RELEASE_TAG }}
release_title: ${{ steps.release_vars.outputs.RELEASE_TITLE }}
steps:
- name: Checkout of code
uses: actions/checkout@v5
with:
submodules: recursive
- name: Java and Android SDK Setup
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
# --- Build Phase ---
- name: Running the Debug Build (only Nightly)
run: ./gradlew assembleUnrestrictedDebug
- name: Get Package Name and Version
id: package_info
run: |
PACKAGE_NAME=$(grep 'applicationId' app/build.gradle | head -n 1 | awk '{print $2}' | tr -d '"')
VERSION_NAME=$(grep 'versionName' app/build.gradle | head -n 1 | awk '{print $2}' | tr -d '"')
echo "PACKAGE_NAME=$PACKAGE_NAME" >> $GITHUB_OUTPUT
echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_OUTPUT
- name: Defining the Release Tag and Title
id: release_vars
run: |
# The release tag will be in the format: come.nanodata.armsx2-nightly-1.0.4-20251104-1411
DATE_TIME=$(date +%Y%m%d-%H%M)
FULL_TAG="${{ steps.package_info.outputs.PACKAGE_NAME }}-nightly-${{ steps.package_info.outputs.VERSION_NAME }}-${DATE_TIME}"
TITLE="Nightly Build (DEBUG - ${{ steps.package_info.outputs.VERSION_NAME }}) - ${DATE_TIME}"
echo "RELEASE_TAG=$FULL_TAG" >> $GITHUB_OUTPUT
echo "RELEASE_TITLE=$TITLE" >> $GITHUB_OUTPUT
- name: Find and Rename APK to Nightly
id: rename_apk
run: |
ORIGINAL_PATH=$(find ${{ github.workspace }}/app/build -type f -name "*debug.apk" -print -quit)
if [ -z "$ORIGINAL_PATH" ]; then
echo "CRITICAL ERROR: Debug APK non found."
exit 1
fi
NEW_FILE_PATH=$(echo "$ORIGINAL_PATH" | sed 's/-debug.apk/-nightly.apk/')
mv "$ORIGINAL_PATH" "$NEW_FILE_PATH"
echo "APK_FILE_NIGHTLY=$NEW_FILE_PATH" >> $GITHUB_OUTPUT
echo "APK renamed and saved in: $NEW_FILE_PATH"
- name: Upload artifact
uses: actions/upload-artifact@v5
with:
name: ${{ steps.package_info.outputs.PACKAGE_NAME }}
path: ${{ steps.rename_apk.outputs.APK_FILE_NIGHTLY }}
release:
permissions:
contents: write
needs: [ build ]
if: >
github.repository == 'ARMSX2/ARMSX2' &&
github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
concurrency:
group: release
cancel-in-progress: false
steps:
- name: Download artifact
uses: actions/download-artifact@v5
with:
name: ${{ needs.build.outputs.package_name }}
path: artifact
- name: Create the Release on GitHub
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.build.outputs.release_tag }}
name: ${{ needs.build.outputs.release_title }}
body: |
## Automatic Night Release (DEBUG)
Automated build for community testing. This is an **unsigned** build for public distribution.
* **Version:** ${{ needs.build.outputs.version_name }}
* **Package Name:** ${{ needs.build.outputs.package_name }}
* **Branch:** ${{ github.ref_name }}
* **Commit:** ${{ github.sha }}
**Check the attached assets for the NIGHTLY APK.**
draft: false
prerelease: true
files: artifact/*.apk
+43
View File
@@ -0,0 +1,43 @@
# Build system and IDE files
build/
.gradle
.vscode/
.idea/
.project
.settings/
local.properties
node_modules/
bun.lockb
package-lock.json
/build-ios*/
/build-maccatalyst/
/ipa-package/
*.ipa
# OS junk & editor/files
.DS_Store
app/.DS_Store
app/src/.DS_Store
app/src/main/.DS_Store
# Android/NDK/intermediate output
app/.cxx/
app/src/main/jniLibs/
app/src/main/cpp/bin
# Sensitive or key material
armsx2_release.jks
armsx2_keystore.properties
app/src/main/assets/discord_creds.env
# Temporary/log/output files
log.txt
# Third-party (vendored) native or generated sources
app/src/main/cpp/3rdparty/rcheevos/.build
app/src/main/cpp/3rdparty/discord_social_sdk/cdiscord.h
app/src/main/cpp/3rdparty/discord_social_sdk/discordpp.h
# External libs
app/libs/discord_partner_sdk.aar
macos
+3
View File
@@ -0,0 +1,3 @@
[submodule "extras/Save-Tower_Adaptive"]
path = extras/Save-Tower_Adaptive
url = https://github.com/Vivimagic/Save-Tower_Adaptive.git
+11
View File
@@ -0,0 +1,11 @@
import React from 'react';
import Router from './app_ui/Router.jsx';
import { ThemeProvider } from './app_ui/theme.jsx';
export default function App() {
return (
<ThemeProvider>
<Router />
</ThemeProvider>
);
}
+28
View File
@@ -0,0 +1,28 @@
Windows:
git clone https://github.com/ARMSX2/ARMSX2.git
git submodule init
git submodule update
cd app\src\main\cpp\3rdparty\libadrenotools
git submodule init
git submodule update
NPM Packages:
npm install --legacy-peer-deps
Build: react-native-gradle-plugin
cd node_modules/@react-native/gradle-plugin
npm install
gradlew.bat build
Change: node_modules\@assembless\react-native-material-you\android\build.gradle
android {
compileSdkVersion 36
defaultConfig {
minSdkVersion 34
targetSdkVersion 34
}
}
gradlew.bat assembleDebug -PenableRN=true
File diff suppressed because it is too large Load Diff
+76
View File
@@ -0,0 +1,76 @@
<div align="center">
![ARMSX2](app_icons/icon.png)
# ARMSX2
[![License](https://img.shields.io/github/license/ARMSX2/ARMSX2)](https://www.gnu.org/licenses/gpl-3.0.html)
[![Discord](https://img.shields.io/discord/914421153827794975?logo=discord&logoColor=white&label=ARMSX2%20Discord&color=5865F2)](https://discord.gg/6yyawTtCnX)
(https://patreon.com/ARMSX2)
[![Nightly Build](https://github.com/ARMSX2/ARMSX2/actions/workflows/android_nightly_build.yml/badge.svg)](https://github.com/ARMSX2/ARMSX2/actions/workflows/android_nightly_build.yml)
</div>
ARMSX2 is a free and open-source PlayStation 2 emulator for ARM devices, based on PCSX2, PCSX2_ARM64 for our OG versions that used x86 translation, and our own arm64jit with the new ARMSX2 refresh versions.
Its goal is to bring modern PS2 emulation to ARM platforms while staying aligned with upstream PCSX2 improvements. ARMSX2 now supports native ARM64 JIT/recompiler work alongside legacy x86-to-ARM64 translation paths, meaning it is no longer solely dependent on x86 translation. Development is ongoing, and the project continues to move toward deeper native ARM64 support as more of the emulator core is modernized.
ARMSX2 allows you to play PS2 games on Android, iOS, Linux, macOS, and Windows devices, with a focus on ARM-based mobile and desktop hardware.
## Project Details
ARMSX2 began after years of there being no open source PS2 emulator for ARM systems, and so developer [@MoonPower](https://github.com/momo-AUX1) with the support of [@jpolo1224](https://github.com/jpolo1224) decided to try their hand at porting a new PS2 emulator for Android, forking from the repository PCSX2_ARM64 by developer Pontos. Moon has and will continue doing his best to fill in the gaps and make this into a complete emulator, with the goal to have version parity with PCSX2. This project is not officially associated with PCSX2, and we are not associated with any other forks made from the original repository. This is our own attempt at continuing PS2 emulation on Android, iOS, and MacOS. The emulator no longer operates as just x86 -> arm64, we now have native arm64 support in our refresh/2.0 builds.
## System Requirements
ARMSX2 supports any ARM capable device, including Android, iOS, Linux, and Windows platforms (eventually, should work as well). Please note that performance will also depend on your devices hardware capabilities, we have done our best to optimize for low end devices and will continue to do so.
Please note that a BIOS dump from a legitimately-owned PS2 console is required to use the emulator.
## Website
→ <https://armsx2.net/>
Any other website is not affiliated with ARMSX2.
## Translation
[Help translate ARMSX2](https://crowdin.com/project/armsx2-translations/invite?h=940eaf6355b31b5fdb1771183c694ca32710218)
## Download
ARMSX2 is available on the Google Play Store once released.
[<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" alt="Get it on Google Play" height="80"/>](https://play.google.com/store/apps/details?id=come.nanodata.armsx2)
## Affiliation
We are NOT affiliated with ARM Holding LTD in any way shape or form. We chose the name ARMSX2 since it runs on ARM devices, and seek no commercial incentive from the emulator. The most we accept is voluntary donations. Thank you.
## Additional Credits
[PCSX2](https://github.com/PCSX2/pcsx2) - ARMSX2 would not be possible without the legendary work from the PCSX2 team and their patience and understanding regarding this project!
[PCSX2_ARM64](https://github.com/pontos2024/PCSX2_ARM64) - ARMSX2 originally started off as a fork of developer Pontos work.
Thank you to [@Vivimagic](https://github.com/Vivimagic) for creating and working on the logo!
Thank you to developers [@tanosshi](https://github.com/tanosshi) [@jpolo1224](https://github.com/jpolo1224) [@MoonPower](https://github.com/momo-AUX1) for working on the ARMSX2 website!
## Why are there .js and .jsx files?
Originally as a curious idea the react native screens were just an experiment i decided to keep they are extremely barebones and will either be finalized in a seperate branch (armsx2-rn) or removed altogether They do not affect performance as they are hidden by default and not executed. Any PR to them is welcome!
### To start developing with ARMSX2 RN do the following:
1. First install the deps:
```sh
(npm/pnpm/bun) install
```
2. Compile ARMSX2 With the react native core:
```sh
./gradlew assembleDebug -PenableRN=true
```
And now you will have a new button appear on the top right of the game selector screen click it and start developing with hot reload and see your changes without recompiling (note: compiling RN switches the emucore from static to shared).
+89
View File
@@ -0,0 +1,89 @@
<div align="center">
![ARMSX2](app_icons/icon.png)
# ARMSX2
[![Licença](https://img.shields.io/github/license/ARMSX2/ARMSX2)](https://www.gnu.org/licenses/gpl-3.0.html)
[![Discord](https://img.shields.io/discord/914421153827794975?logo=discord&logoColor=white&label=ARMSX2%20Discord&color=5865F2)](https://discord.gg/6yyawTtCnX)
(https://patreon.com/ARMSX2)
[![Versões Nightly ](https://github.com/ARMSX2/ARMSX2/actions/workflows/android_nightly_build.yml/badge.svg)](https://github.com/ARMSX2/ARMSX2/actions/workflows/android_nightly_build.yml)
</div>
ARMSX2 é um emulador gratuito e de código aberto de PlayStation 2 (PS2) para dispositivos ARM baseado no PCSX2 e PCSX2_ARM64. O propósito é emular o hardware do PS2 para dispositivos ARM, usando um recompilador que opera de x86 para arm64, não arm64 nativo, isso está sujeito a alterações assim que o desenvolvimento continuar. ARMSX2 permite que você jogue jogos de PS2 no seu dispositivo móvel Android, assim como iOS, Linux, e dispositivos Windows.
## Detalhes do Projeto
ARMSX2 começou após anos pela falta de um emulador de código aberto para os sistemas ARM, e então o desenvolvedor [@MoonPower](https://github.com/momo-AUX1) com o suporte de [@jpolo1224](https://github.com/jpolo1224) decidiu tentar fazer um port de um novo emulador de PS2 para Android, fazendo um fork do repositório PCSX2_ARM64 do desenvolvedor Pontos. Moon tem feito e continuará fazendo o seu melhor para preencher as lacunas e fazer do ARMSX2 um emulador completo, com o objetivo de manter uma versão de paridade com a do PCSX2. Esse projeto não é oficialmente associado com PCSX2, e nós não somos associados com nenhum outro fork feito a partir do repositório original. Esta é a nossa própria tentativa de continuar a emulação de PS2 para Android, iOS, e MacOS. O emulador atualmente opera de x86 para arm64, não nativamente arm64, então muito provavelmente a performance não vai ser tão boa como AetherSX2 no momento, porém as coisas estão sujeitas a mudanças no decorrer do desenvolvimento.
## Requisitos do Sistema
ARMSX2 suporta qualquer dispositivo ARM capaz, incluindo as plataformas Android, iOS, Linux, e Windows (futuramente, também deve funcionar). Por gentileza, saiba que a performance também irá depender das capacidades de hardware do seu dispositivo, Temos feito o nosso melhor para otimizar para dispositivos low end e vamos continuar fazendo isso.
Por gentileza, saiba que uma imagem da BIOS vinda do seu console PS2 legítimo é necessária para usar o emulador.
## Website
→ <https://armsx2.net/>
Qualquer outro website não é afiliado com ARMSX2.
## Traduções
[Ajude a traduzir o ARMSX2](https://crowdin.com/project/armsx2-translations/invite?h=940eaf6355b31b5fdb1771183c694ca32710218)
## Baixar
ARMSX2 está disponível na [Google Play Store](https://play.google.com/store/apps/details?id=come.nanodata.armsx2)
[<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" alt="Baixe pela Google Play" height="80"/>](https://play.google.com/store/apps/details?id=come.nanodata.armsx2)
## Afiliação
Nós NÃO somos afiliados de forma/maneira nenhuma com ARM Holding LTD. Nós escolhemos o nome ARMSX2 já que executa em dispositivos ARM, e não queremos nenhum incentivo comercial com o emulador. O máximo que aceitamos é doações voluntárias. Obrigado.
## Créditos Adicionais
[PCSX2](https://github.com/PCSX2/pcsx2) - ARMSX2 não seria possível sem o trabalho, paciência e compreensão lendários da equipe do PCSX2 com relação ao projeto.
[PCSX2_ARM64](https://github.com/pontos2024/PCSX2_ARM64) - ARMSX2 originalmente começou como um fork do trabalho de pontos.
Obrigado [@Vivimagic](https://github.com/Vivimagic) por criar e trabalhar na logo!
Obrigado aos desenvolvedores [@tanosshi](https://github.com/tanosshi) [@jpolo1224](https://github.com/jpolo1224) [@MoonPower](https://github.com/momo-AUX1) por trabalharem no website do ARMSX2!
## Roadmap
Aqui está o roadmap de coisas que você pode esperar do ARMSX2 no futuro:
| Tarefa | Prioridade |
| --- | --- |
| Arrumar GPUs xclipse | Alta |
| Arrumar os crashes em GPUs Mali | Alta |
| Suporte para Nintendo Switch | Média |
| Atualizar para o núcleo mais recente | Alta |
| Atualizar o design para Material expressive | Baixa |
| Migrar para Kotlin | Média |
## Por que existem arquivos .js e .jsx?
Originalmente como uma ideia curiosa, na verdade, as telas do React Native eram apenas um experimento, eu decidi manter extremamente básicos, serão finalizados em uma branch separada (armsx2-rn) ou removidos completamente, Não afetam a performance já que são escondidos por padrão e não executados. Qualquer PR para os mesmos são bem-vindas!
### Para começar a desenvolver com ARMSX2 agora faça o seguinte:
1. Primeiro instale as deps:
```sh
(npm/pnpm/bun) install
```
2. Compilar ARMSX2 com o react native core:
```sh
./gradlew assembleDebug -PenableRN=true
```
E agora você terá um novo botão aparecendo no canto superior direito da tela de carregamento de jogos, clique e comece a desenvolver com hot reload e veja suas mudanças sem recompilação(nota: compilar agora muda o emucore de static para shared).

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