Add support for macOS (#771)

* use shm_open() instead of memfd_create() on macOS

malloc.h isn't a header on macOS

* Change OpenGL headers + create ifdef for DO_PROCLIST

macOS seems to already have the OpenGL functions defined, without the ifdef, it gives "ambiguous references" errors.

* macOS doesn't have ->gregs in uc_mcontext

and it doesn't have REG_RIP either
https://github.com/gperftools/gperftools/blob/master/m4/pc_from_ucontext.m4

* use getpid() to make memory file name unique

* #ifndef __APPLE__ for AF_PACKET and linux/if_packet.h

* Add include and link directories for macOS and link the OpenGL framework

* Add macOS CI

* Use newly added libslirp package from Homebrew

https://github.com/Homebrew/homebrew-core/pull/63412

* Use Apple's Clang instead of GNU GCC on macOS

* Add macOS build instructions to README

* Try to fix macOS undefined symbol

* snprintf doesn't take null terminator into account

* Map new memory on macOS for JIT

* Only use gcc-ar if using GNU Compiler

* re-add fastmem code - whoops!

* Fix style issue - use camelCase not snake_case

* Set Minimum macOS version

* Switch Minimum OS X version to 10.9

* Add macOS libpcap library name

* fix memory leak

* Fix binding keys in macOS

* Allow getting MAC address on macOS

melonDS on Linux uses AF_PACKET, which doesn't exist on macOS. Instead, this commit uses AF_LINK on macOS to get the MAC address.

* Remove unneeded macOS CI dependencies

* Build melonDS app bundle on macOS

Now it is no longer required to install the libraries on macOS, they come with the app bundle.

* fix macOS CI not being able to find macdeployqt

* copy melonDS.app with recursive because it's a folder

* Disable fastmem checkbox on macOS

* Disable fastmem by default in config

* forgot a semicolon

* Don't bundle libraries, causes issues on macOS <10.15

* Update README + allow finding version in Finder on macOS

* Make sure fastmem checkbox stays uncheckable
This commit is contained in:
WaluigiWare64 2020-11-29 08:11:33 -08:00 committed by GitHub
parent 14be591ab8
commit 7da4550eea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 186 additions and 13 deletions

39
.github/workflows/build-macos.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: CMake Build (macOS x86-64)
on:
push:
branches:
- master
pull_request:
branches:
- master
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Install dependencies
working-directory: ${{runner.workspace}}
run: |
brew install cmake sdl2 qt5 libslirp
- name: Create build environment
run: mkdir ${{runner.workspace}}/build
- name: Configure
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
- name: Make
working-directory: ${{runner.workspace}}/build
run: |
make -j$(sysctl -n hw.ncpu)
mkdir dist
cp -r melonDS.app dist
- uses: actions/upload-artifact@v1
with:
name: melonDS.app
path: ${{runner.workspace}}/build/dist

View File

@ -8,6 +8,8 @@ if (POLICY CMP0076)
cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0076 NEW)
endif() endif()
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
project(melonDS CXX) project(melonDS CXX)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)

View File

@ -84,6 +84,21 @@ If everything went well, melonDS and the libraries it needs should now be in the
``` ```
If everything went well, melonDS should now be in the `dist` folder. If everything went well, melonDS should now be in the `dist` folder.
### macOS:
1. Install the [Homebrew Package Manager](https://brew.sh)
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt5 libslirp`
3. Compile:
```zsh
git clone https://github.com/Arisotura/melonDS.git
cd melonDS
mkdir build && cd build
cmake .. -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
make -j$(sysctl -n hw.ncpu)
mkdir dist && cp -r melonDS.app dist
```
If everything went well, melonDS.app should now be in the `dist` folder.
## TODO LIST ## TODO LIST
* DSi emulation * DSi emulation

BIN
melonDS.icns Normal file

Binary file not shown.

24
melonDS.plist Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>melonDS</string>
<key>CFBundleIconFile</key>
<string>melonDS.icns</string>
<key>CFBundleIdentifier</key>
<string>net.kuribo64.melonDS</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>0.9</string>
<key>CFBundleShortVersionString</key>
<string>0.9</string>
<key>NSHumanReadableCopyright</key>
<string>Licensed under GPLv3</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>

View File

@ -22,7 +22,9 @@
#include "NDSCart.h" #include "NDSCart.h"
#include "SPU.h" #include "SPU.h"
#ifndef __APPLE__
#include <malloc.h> #include <malloc.h>
#endif
/* /*
We're handling fastmem here. We're handling fastmem here.
@ -152,7 +154,12 @@ static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext)
u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start); u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
#ifdef __x86_64__ #ifdef __x86_64__
desc.EmulatedFaultAddr = (u8*)info->si_addr - curArea; desc.EmulatedFaultAddr = (u8*)info->si_addr - curArea;
#ifdef __APPLE__
desc.FaultPC = (u8*)context->uc_mcontext->__ss.__rip;
#else
desc.FaultPC = (u8*)context->uc_mcontext.gregs[REG_RIP]; desc.FaultPC = (u8*)context->uc_mcontext.gregs[REG_RIP];
#endif
#else #else
desc.EmulatedFaultAddr = (u8*)context->uc_mcontext.fault_address - curArea; desc.EmulatedFaultAddr = (u8*)context->uc_mcontext.fault_address - curArea;
desc.FaultPC = (u8*)context->uc_mcontext.pc; desc.FaultPC = (u8*)context->uc_mcontext.pc;
@ -161,7 +168,11 @@ static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext)
if (ARMJIT_Memory::FaultHandler(desc)) if (ARMJIT_Memory::FaultHandler(desc))
{ {
#ifdef __x86_64__ #ifdef __x86_64__
#ifdef __APPLE__
context->uc_mcontext->__ss.__rip = (u64)desc.FaultPC;
#else
context->uc_mcontext.gregs[REG_RIP] = (u64)desc.FaultPC; context->uc_mcontext.gregs[REG_RIP] = (u64)desc.FaultPC;
#endif
#else #else
context->uc_mcontext.pc = (u64)desc.FaultPC; context->uc_mcontext.pc = (u64)desc.FaultPC;
#endif #endif
@ -701,7 +712,14 @@ void Init()
FastMem7Start = MemoryBase + AddrSpaceSize; FastMem7Start = MemoryBase + AddrSpaceSize;
MemoryBase = MemoryBase + AddrSpaceSize*2; MemoryBase = MemoryBase + AddrSpaceSize*2;
#ifdef __APPLE__
char* fastmemPidName = new char[snprintf(NULL, 0, "melondsfastmem%d", getpid()) + 1];
sprintf(fastmemPidName, "melondsfastmem%d", getpid());
MemoryFile = shm_open(fastmemPidName, O_RDWR|O_CREAT, 0600);
delete[] fastmemPidName;
#else
MemoryFile = memfd_create("melondsfastmem", 0); MemoryFile = memfd_create("melondsfastmem", 0);
#endif
ftruncate(MemoryFile, MemoryTotalSize); ftruncate(MemoryFile, MemoryTotalSize);
struct sigaction sa; struct sigaction sa;
@ -736,6 +754,11 @@ void DeInit()
svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem, (u64)MemoryBase, MemoryTotalSize); svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem, (u64)MemoryBase, MemoryTotalSize);
virtmemFree(MemoryBaseCodeMem, MemoryTotalSize); virtmemFree(MemoryBaseCodeMem, MemoryTotalSize);
free(MemoryBase); free(MemoryBase);
#elif defined(__APPLE__)
char* fastmemPidName = new char[snprintf(NULL, 0, "melondsfastmem%d", getpid()) + 1];
sprintf(fastmemPidName, "melondsfastmem%d", getpid());
shm_unlink(fastmemPidName);
delete[] fastmemPidName;
#elif defined(_WIN32) #elif defined(_WIN32)
assert(UnmapViewOfFile(MemoryBase)); assert(UnmapViewOfFile(MemoryBase));
CloseHandle(MemoryFile); CloseHandle(MemoryFile);

View File

@ -221,6 +221,8 @@ Compiler::Compiler()
#ifdef _WIN32 #ifdef _WIN32
DWORD dummy; DWORD dummy;
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy); VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
#elif defined(__APPLE__)
pageAligned = (u8*)mmap(NULL, 1024*1024*32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
#else #else
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE); mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
#endif #endif

View File

@ -29,8 +29,13 @@
.p2align 4,,15 .p2align 4,,15
#ifdef __APPLE__
.global _ARM_Dispatch
_ARM_Dispatch:
#else
.global ARM_Dispatch .global ARM_Dispatch
ARM_Dispatch: ARM_Dispatch:
#endif
#ifdef WIN64 #ifdef WIN64
push rdi push rdi
push rsi push rsi
@ -54,8 +59,13 @@ ARM_Dispatch:
.p2align 4,,15 .p2align 4,,15
#ifdef __APPLE__
.global _ARM_Ret
_ARM_Ret:
#else
.global ARM_Ret .global ARM_Ret
ARM_Ret: ARM_Ret:
#endif
mov [RCPU + ARM_CPSR_offset], RCPSR mov [RCPU + ARM_CPSR_offset], RCPSR
#ifdef WIN64 #ifdef WIN64

View File

@ -100,9 +100,16 @@ if (ENABLE_JIT)
endif() endif()
endif() endif()
if (APPLE)
target_include_directories(core PUBLIC /usr/local/include)
target_link_directories(core PUBLIC /usr/local/lib)
endif()
if (ENABLE_OGLRENDERER) if (ENABLE_OGLRENDERER)
if (WIN32) if (WIN32)
target_link_libraries(core ole32 comctl32 ws2_32 opengl32) target_link_libraries(core ole32 comctl32 ws2_32 opengl32)
elseif (APPLE)
target_link_libraries(core "-framework OpenGL")
else() else()
target_link_libraries(core GL EGL) target_link_libraries(core GL EGL)
endif() endif()

View File

@ -73,8 +73,12 @@ ConfigEntry ConfigFile[] =
{"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, NULL, 0}, {"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, NULL, 0},
{"JIT_BranchOptimisations", 0, &JIT_BranchOptimisations, 1, NULL, 0}, {"JIT_BranchOptimisations", 0, &JIT_BranchOptimisations, 1, NULL, 0},
{"JIT_LiteralOptimisations", 0, &JIT_LiteralOptimisations, 1, NULL, 0}, {"JIT_LiteralOptimisations", 0, &JIT_LiteralOptimisations, 1, NULL, 0},
#ifdef __APPLE__
{"JIT_FastMemory", 0, &JIT_FastMemory, 0, NULL, 0},
#else
{"JIT_FastMemory", 0, &JIT_FastMemory, 1, NULL, 0}, {"JIT_FastMemory", 0, &JIT_FastMemory, 1, NULL, 0},
#endif #endif
#endif
{"", -1, NULL, 0, NULL, 0} {"", -1, NULL, 0, NULL, 0}
}; };

View File

@ -23,8 +23,13 @@
#include <string.h> #include <string.h>
// TODO: different includes for each platform // TODO: different includes for each platform
#ifdef __APPLE__
#include <OpenGL/gl3.h>
#include <OpenGL/gl3ext.h>
#else
#include <GL/gl.h> #include <GL/gl.h>
#include <GL/glext.h> #include <GL/glext.h>
#endif
#include "Platform.h" #include "Platform.h"
@ -61,6 +66,11 @@
#endif #endif
#ifdef __APPLE__
#define DO_PROCLIST(func)
#else
#define DO_PROCLIST(func) \ #define DO_PROCLIST(func) \
DO_PROCLIST_1_3(func) \ DO_PROCLIST_1_3(func) \
@ -128,6 +138,7 @@
\ \
func(GLGETSTRINGI, glGetStringi); \ func(GLGETSTRINGI, glGetStringi); \
#endif
namespace OpenGL namespace OpenGL
{ {

View File

@ -95,6 +95,19 @@ if (PORTABLE)
add_definitions(-DPORTABLE) add_definitions(-DPORTABLE)
endif() endif()
if (APPLE)
set_target_properties(melonDS PROPERTIES
MACOSX_BUNDLE true
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/melonDS.plist
OUTPUT_NAME melonDS
)
# Copy icon into the bundle
target_sources(melonDS PRIVATE "${CMAKE_SOURCE_DIR}/melonDS.icns")
set_source_files_properties("${CMAKE_SOURCE_DIR}/melonDS.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
endif()
install(FILES ../../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) install(FILES ../../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ../../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png) install(FILES ../../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png) install(FILES ../../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png)
@ -102,4 +115,4 @@ install(FILES ../../../icon/melon_48x48.png DESTINATION ${CMAKE_INSTALL_PREFIX}/
install(FILES ../../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png) install(FILES ../../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png) install(FILES ../../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png) install(FILES ../../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png)
install(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) install(TARGETS melonDS BUNDLE DESTINATION ${CMAKE_BINARY_DIR} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)

View File

@ -65,6 +65,9 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
ui->chkJITBranchOptimisations->setChecked(Config::JIT_BranchOptimisations != 0); ui->chkJITBranchOptimisations->setChecked(Config::JIT_BranchOptimisations != 0);
ui->chkJITLiteralOptimisations->setChecked(Config::JIT_LiteralOptimisations != 0); ui->chkJITLiteralOptimisations->setChecked(Config::JIT_LiteralOptimisations != 0);
ui->chkJITFastMemory->setChecked(Config::JIT_FastMemory != 0); ui->chkJITFastMemory->setChecked(Config::JIT_FastMemory != 0);
#ifdef __APPLE__
ui->chkJITFastMemory->setDisabled(true);
#endif
ui->spnJITMaximumBlockSize->setValue(Config::JIT_MaxBlockSize); ui->spnJITMaximumBlockSize->setValue(Config::JIT_MaxBlockSize);
#else #else
ui->chkEnableJIT->setDisabled(true); ui->chkEnableJIT->setDisabled(true);
@ -329,6 +332,8 @@ void EmuSettingsDialog::on_chkEnableJIT_toggled()
bool disabled = !ui->chkEnableJIT->isChecked(); bool disabled = !ui->chkEnableJIT->isChecked();
ui->chkJITBranchOptimisations->setDisabled(disabled); ui->chkJITBranchOptimisations->setDisabled(disabled);
ui->chkJITLiteralOptimisations->setDisabled(disabled); ui->chkJITLiteralOptimisations->setDisabled(disabled);
#ifndef __APPLE__
ui->chkJITFastMemory->setDisabled(disabled); ui->chkJITFastMemory->setDisabled(disabled);
#endif
ui->spnJITMaximumBlockSize->setDisabled(disabled); ui->spnJITMaximumBlockSize->setDisabled(disabled);
} }

View File

@ -216,6 +216,7 @@ KeyMapButton::KeyMapButton(int* mapping, bool hotkey) : QPushButton()
setCheckable(true); setCheckable(true);
setText(mappingText()); setText(mappingText());
setFocusPolicy(Qt::StrongFocus); //Fixes binding keys in macOS
connect(this, &KeyMapButton::clicked, this, &KeyMapButton::onClick); connect(this, &KeyMapButton::clicked, this, &KeyMapButton::onClick);
} }

View File

@ -33,8 +33,12 @@
#include <sys/types.h> #include <sys/types.h>
#include <ifaddrs.h> #include <ifaddrs.h>
#include <netinet/in.h> #include <netinet/in.h>
#ifdef __APPLE__
#include <net/if_dl.h>
#else
#include <linux/if_packet.h> #include <linux/if_packet.h>
#endif #endif
#endif
// welp // welp
@ -66,6 +70,9 @@ const char* PCapLibNames[] =
#ifdef __WIN32__ #ifdef __WIN32__
// TODO: name for npcap in non-WinPCap mode // TODO: name for npcap in non-WinPCap mode
"wpcap.dll", "wpcap.dll",
#elif defined(__APPLE__)
"libpcap.A.dylib",
"libpcap.dylib",
#else #else
// Linux lib names // Linux lib names
"libpcap.so.1", "libpcap.so.1",
@ -276,6 +283,16 @@ bool Init(bool open_adapter)
struct sockaddr_in* sa = (sockaddr_in*)curaddr->ifa_addr; struct sockaddr_in* sa = (sockaddr_in*)curaddr->ifa_addr;
memcpy(adata->IP_v4, &sa->sin_addr, 4); memcpy(adata->IP_v4, &sa->sin_addr, 4);
} }
#ifdef __APPLE__
else if (af == AF_LINK)
{
struct sockaddr_dl* sa = (sockaddr_dl*)curaddr->ifa_addr;
if (sa->sdl_alen != 6)
printf("weird MAC length %d for %s\n", sa->sdl_alen, curaddr->ifa_name);
else
memcpy(adata->MAC, LLADDR(sa), 6);
}
#else
else if (af == AF_PACKET) else if (af == AF_PACKET)
{ {
struct sockaddr_ll* sa = (sockaddr_ll*)curaddr->ifa_addr; struct sockaddr_ll* sa = (sockaddr_ll*)curaddr->ifa_addr;
@ -284,7 +301,7 @@ bool Init(bool open_adapter)
else else
memcpy(adata->MAC, sa->sll_addr, 6); memcpy(adata->MAC, sa->sll_addr, 6);
} }
#endif
curaddr = curaddr->ifa_next; curaddr = curaddr->ifa_next;
} }
} }