From http://www.riscos.info/pipermail/rpcemu/2020-May/002887.html

--- original/ArmDynarec.c	2020-05-06 20:19:23.000000000 +0100
+++ src/ArmDynarec.c	2020-05-07 21:14:11.000000000 +0100
@@ -580,12 +580,39 @@
 {
 	const long page_size = sysconf(_SC_PAGESIZE);
 	const long page_mask = ~(page_size - 1);
-	void *start;
+	void *start, *addr;
 	long end;
+    int mmap_flags = 0;
 
 	start = (void *) ((long) ptr & page_mask);
 	end = ((long) ptr + len + page_size - 1) & page_mask;
 	len = (size_t) (end - (long) start);
+    
+#if __APPLE__
+    // More up-to-date versions of OS X require "mmap" to be called prior to "mprotect".
+    // Certain versions also require the MAP_JIT flag as well.
+    // Try without first, and if that fails, add the flag in.    
+    mmap_flags = MAP_PRIVATE | MAP_ANON | MAP_FIXED;
+    
+    addr = mmap(NULL, page_size, PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (addr == MAP_FAILED)
+    {
+        mmap_flags |= MAP_JIT;
+    }
+    else
+    {
+        munmap(addr, page_size);
+    }
+    
+    addr = mmap(start, len, PROT_READ | PROT_WRITE | PROT_EXEC, mmap_flags, -1, 0);
+    
+    if (addr == MAP_FAILED)
+    {
+        perror("mmap");
+        exit(1);
+    }
+
+#endif
 
 	if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
 		perror("mprotect");
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/qt5/choose_dialog.cpp	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,107 @@
+/*
+ RPCEmu - An Acorn system emulator
+ 
+ Copyright (C) 2016-2017 Matthew Howkins
+ 
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <QFileDialog>
+#include "choose_dialog.h"
+#include "preferences-macosx.h"
+
+ChooseDialog::ChooseDialog(QWidget *parent) : QDialog(parent)
+{
+  setWindowTitle("RPCEmu - Choose Data Directory");
+  
+  buttons_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+  
+  // Create preamble label.
+  QString str = QString("<p>Before using RPCEmu for the first time, you must select the directory <br/>"
+                        "that contains the folders and files required by the emulator, such as <br>"
+                        "ROMs, hard drive images and the HostFS share.</p>"
+                        "<p>You can show this dialogue again by holding down the Command key <br/>"
+                        "whilst the application is loading.</p>");
+
+  preamble_label = new QLabel(str);
+  
+  // Create choose label.
+  choose_label = new QLabel();
+  choose_label->setText("Please choose a directory below:");
+  
+  // Create directory line edit.
+  directory_edit = new QLineEdit();
+  directory_edit->setMaxLength(511);
+  directory_edit->setReadOnly(true);
+  
+  // Create directory button.
+  directory_button = new QPushButton("Select...", this);
+  
+  // Create box for line edit and button.
+  directory_hbox = new QHBoxLayout();
+  directory_hbox->setSpacing(16);
+  directory_hbox->addWidget(directory_edit);
+  directory_hbox->addWidget(directory_button);
+  
+  grid = new QGridLayout(this);
+  grid->addWidget(preamble_label, 0, 0);
+  grid->addWidget(choose_label, 1, 0);
+  grid->addLayout(directory_hbox, 2, 0);
+  grid->addWidget(buttons_box, 3, 0);
+  
+  // Connect actions to widgets.
+  connect(directory_button, &QPushButton::pressed, this, &ChooseDialog::directory_button_pressed);
+  
+  connect(buttons_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
+  connect(buttons_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
+  
+  connect(this, &QDialog::accepted, this, &ChooseDialog::dialog_accepted);
+  connect(this, &QDialog::accepted, this, &ChooseDialog::dialog_rejected);
+
+  this->setFixedSize(this->sizeHint());
+}
+
+ChooseDialog::~ChooseDialog()
+{
+}
+
+void ChooseDialog::directory_button_pressed()
+{
+  QFileDialog folderDialog;
+  folderDialog.setWindowTitle("Choose Data Directory");
+  folderDialog.setFileMode(QFileDialog::Directory);
+  
+  if (folderDialog.exec())
+  {
+    QStringList selection = folderDialog.selectedFiles();
+    QString folderName = selection.at(0);
+    
+    directory_edit->setText(folderName);
+  }
+}
+
+void ChooseDialog::dialog_accepted()
+{
+  QString selectedFolder = directory_edit->text();
+  QByteArray ba = selectedFolder.toUtf8();
+  
+  char *ptr = ba.data();
+  preferences_set_data_directory(ptr);
+}
+
+void ChooseDialog::dialog_rejected()
+{
+}
+
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/qt5/choose_dialog.h	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,64 @@
+/*
+ RPCEmu - An Acorn system emulator
+ 
+ Copyright (C) 2016-2017 Matthew Howkins
+ 
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef CHOOSE_DIALOG_H
+#define CHOOSE_DIALOG_H
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+
+#include "rpc-qt5.h"
+#include "rpcemu.h"
+
+class ChooseDialog : public QDialog
+{
+  
+  Q_OBJECT
+  
+public:
+  ChooseDialog(QWidget *parent = 0);
+  virtual ~ChooseDialog();
+  
+private slots:
+  void directory_button_pressed();
+  
+  void dialog_accepted();
+  void dialog_rejected();
+  
+private:
+  
+  QLabel *preamble_label;
+  QLabel *choose_label;
+  
+  QHBoxLayout *directory_hbox;
+  QLineEdit *directory_edit;
+  QPushButton *directory_button;
+  
+  QDialogButtonBox *buttons_box;
+  
+  QGridLayout *grid;
+  
+};
+
+#endif
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/macosx/events-macosx.h	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,45 @@
+/*
+ RPCEmu - An Acorn system emulator
+ 
+ Copyright (C) 2017 Peter Howkins
+ 
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __EVENTS_MACOSX_H__
+#define __EVENTS_MACOSX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+typedef enum {
+    nativeEventTypeModifiersChanged = 1
+} NativeEventType;
+    
+typedef struct
+{
+    bool processed;
+    int eventType;
+    uint modifierMask;
+} NativeEvent;
+    
+extern NativeEvent* handle_native_event(void *message);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __EVENTS_MACOSX_H__
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/macosx/events-macosx.m	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,56 @@
+/*
+ RPCEmu - An Acorn system emulator
+ 
+ Copyright (C) 2017 Matthew Howkins
+ 
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define UNUSED(x) (void)(x)
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <Cocoa/Cocoa.h>
+#include <Carbon/Carbon.h>
+#include <IOKit/hid/IOHIDLib.h>
+
+#include "rpcemu.h"
+
+#include "events-macosx.h"
+
+
+NativeEvent* handle_native_event(void *message)
+{
+    // This extracts information from the Cocoa event and passes it back up the chain to C++.
+    NSEvent *event = (NSEvent *) message;
+    
+    NativeEvent *result = (NativeEvent *) malloc(sizeof(NativeEvent));
+    
+    // Only handle flags changed events, which are raised for modifier key changes.
+    if (event.type == NSEventTypeFlagsChanged)
+    {
+        result->eventType = nativeEventTypeModifiersChanged;
+        result->modifierMask = event.modifierFlags;
+        result->processed = 1;
+    }
+    else
+    {
+        result->processed = 0;
+    }
+    
+    // Return zero if the event is not handled here.
+    return result;
+}
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/macosx/hid-macosx.h	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,34 @@
+///*
+// RPCEmu - An Acorn system emulator
+//
+// Copyright (C) 2017 Matthew Howkins
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+// */
+
+#ifndef __HID_MACOSX_H__
+#define __HID_MACOSX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void init_hid_manager(void);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __HID_MACOSX_H__
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/macosx/hid-macosx.m	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,165 @@
+///*
+// RPCEmu - An Acorn system emulator
+//
+// Copyright (C) 2017 Matthew Howkins
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+// */
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <Cocoa/Cocoa.h>
+#include <Carbon/Carbon.h>
+#include <IOKit/hid/IOHIDLib.h>
+
+#include "keyboard.h"
+#include "keyboard_macosx.h"
+
+#define UNUSED(x) (void)(x)
+
+static IOHIDManagerRef hidManager = NULL;
+
+CFDictionaryRef createHIDDeviceMatchingDictionary(uint32 usagePage, uint32 usage)
+{
+    CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+    if (dictionary)
+    {
+        CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage);
+        if (number)
+        {
+            CFDictionarySetValue(dictionary, CFSTR(kIOHIDDeviceUsagePageKey), number);
+            CFRelease(number);
+
+            number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
+            if (number)
+            {
+                CFDictionarySetValue(dictionary, CFSTR(kIOHIDDeviceUsageKey), number);
+                CFRelease(number);
+
+                return dictionary;
+            }
+        }
+
+        CFRelease(dictionary);
+    }
+
+    return NULL;
+}
+
+void processHIDCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value)
+{
+    UNUSED(result);
+    UNUSED(sender);
+
+    if (context != hidManager) return;
+
+    IOHIDElementRef element = IOHIDValueGetElement(value);
+    if (IOHIDElementGetUsagePage(element) != kHIDPage_KeyboardOrKeypad || IOHIDElementGetUsage(element) != kHIDUsage_KeyboardCapsLock) return;
+
+    CFIndex pressed = IOHIDValueGetIntegerValue(value);
+
+    uint8 scanCodes[] = { 0x58 };
+
+    if (pressed == 0)
+    {
+        keyboard_key_release(scanCodes);
+    }
+    else
+    {
+        keyboard_key_press(scanCodes);
+    }
+}
+
+const char *getCurrentKeyboardLayoutName()
+{
+    TISInputSourceRef currentSource = TISCopyCurrentKeyboardInputSource();
+    NSString *inputSource = (__bridge NSString *)(TISGetInputSourceProperty(currentSource, kTISPropertyInputSourceID));
+    NSUInteger lastIndex = [inputSource rangeOfString:@"." options:NSBackwardsSearch].location;
+    
+    NSString *layoutName = [inputSource substringFromIndex: lastIndex + 1];
+    lastIndex = [layoutName rangeOfString:@" - "].location;
+    
+    if (lastIndex != NSNotFound) layoutName = [layoutName substringToIndex: lastIndex];
+   
+    return [layoutName UTF8String];
+}
+
+void terminate_hid_manager(void)
+{
+    if (!hidManager) return;
+    
+    IOHIDManagerUnscheduleFromRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+    IOHIDManagerRegisterInputValueCallback(hidManager, NULL, NULL);
+    IOHIDManagerClose(hidManager, 0);
+    
+    CFRelease(hidManager);
+    
+    hidManager = NULL;
+}
+
+void init_hid_manager(void)
+{
+    const char *layoutName = getCurrentKeyboardLayoutName();
+    keyboard_configure_layout(layoutName);
+
+    hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+    if (!hidManager) return;
+
+    CFDictionaryRef keyboard = NULL, keypad = NULL;
+    CFArrayRef matches = NULL;
+
+    keyboard = createHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
+    if (!keyboard)
+    {
+        IOHIDManagerClose(hidManager, 0);
+        return;
+    }
+
+    keypad = createHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad);
+    if (!keypad)
+    {
+        CFRelease(keyboard);
+        IOHIDManagerClose(hidManager, 0);
+
+        return;
+    }
+
+    CFDictionaryRef matchesList[] = {keyboard, keypad};
+    matches = CFArrayCreate(kCFAllocatorDefault, (const void**) matchesList, 2, NULL);
+    if (!matches)
+    {
+        CFRelease(keypad);
+        CFRelease(keyboard);
+        IOHIDManagerClose(hidManager, 0);
+
+        return;
+    }
+
+    IOHIDManagerSetDeviceMatchingMultiple(hidManager, matches);
+    IOHIDManagerRegisterInputValueCallback(hidManager, processHIDCallback, hidManager);
+    IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
+    if (IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone) != kIOReturnSuccess)
+    {
+        terminate_hid_manager();
+    }
+
+    CFRelease(matches);
+    CFRelease(keypad);
+    CFRelease(keyboard);
+}
+
+
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/hostfs-macosx.c	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,121 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <utime.h>
+#include <sys/stat.h>
+
+#include "hostfs_internal.h"
+
+/**
+ * Convert ADFS time-stamped Load-Exec addresses to the equivalent time_t.
+ *
+ * @param load RISC OS load address (assumed to be time-stamped)
+ * @param exec RISC OS exec address (assumed to be time-stamped)
+ * @return Time converted to time_t format
+ *
+ * Code adapted from fs/adfs/inode.c from Linux licensed under GPL2.
+ * Copyright (C) 1997-1999 Russell King
+ */
+static time_t
+hostfs_adfs2host_time(uint32_t load, uint32_t exec)
+{
+	uint32_t high = load << 24;
+	uint32_t low  = exec;
+
+	high |= low >> 8;
+	low &= 0xff;
+
+	if (high < 0x3363996a) {
+		/* Too early */
+		return 0;
+	} else if (high >= 0x656e9969) {
+		/* Too late */
+		return 0x7ffffffd;
+	}
+
+	high -= 0x336e996a;
+	return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
+}
+
+/**
+ * Read information about an object.
+ *
+ * @param host_pathname Full Host path to object
+ * @param object_info   Return object info (filled-in)
+ */
+void
+hostfs_read_object_info_platform(const char *host_pathname,
+                                 risc_os_object_info *object_info)
+{
+	struct stat info;
+	uint32_t low, high;
+
+	assert(host_pathname != NULL);
+	assert(object_info != NULL);
+    
+  // Ignore DS_Store files.
+  if (strcasestr(host_pathname, ".DS_Store") != NULL)
+  {
+      object_info->type = OBJECT_TYPE_NOT_FOUND;
+      return;
+  }
+    
+	if (stat(host_pathname, &info)) {
+		/* Error reading info about the object */
+		switch (errno) {
+		case ENOENT: /* Object not found */
+		case ENOTDIR: /* A path component is not a directory */
+			object_info->type = OBJECT_TYPE_NOT_FOUND;
+			break;
+
+		default:
+			/* Other error */
+			fprintf(stderr,
+			        "hostfs_read_object_info_platform() could not stat() \'%s\': %s %d\n",
+			        host_pathname, strerror(errno), errno);
+			object_info->type = OBJECT_TYPE_NOT_FOUND;
+			break;
+		}
+
+		return;
+	}
+
+	/* We were able to read about the object */
+	if (S_ISREG(info.st_mode)) {
+		object_info->type = OBJECT_TYPE_FILE;
+	} else if (S_ISDIR(info.st_mode)) {
+		object_info->type = OBJECT_TYPE_DIRECTORY;
+	} else {
+		/* Treat types other than file or directory as not found */
+		object_info->type = OBJECT_TYPE_NOT_FOUND;
+		return;
+	}
+
+	low  = (uint32_t) ((info.st_mtime & 255) * 100);
+	high = (uint32_t) ((info.st_mtime / 256) * 100 + (low >> 8) + 0x336e996a);
+
+	/* If the file has filetype and timestamp, additional values will need to be filled in later */
+	object_info->load = (high >> 24);
+	object_info->exec = (low & 0xff) | (high << 8);
+
+	object_info->length = info.st_size;
+}
+
+/**
+ * Apply the timestamp to the supplied host object
+ *
+ * @param host_path Full path to object (file or dir) in host format
+ * @param load      RISC OS load address (must contain time-stamp)
+ * @param exec      RISC OS exec address (must contain time-stamp)
+ */
+void
+hostfs_object_set_timestamp_platform(const char *host_path, uint32_t load, uint32_t exec)
+{
+	struct utimbuf t;
+
+	t.actime = t.modtime = hostfs_adfs2host_time(load, exec);
+	utime(host_path, &t);
+	/* TODO handle error in utime() */
+}
--- original/hostfs.c	2020-05-06 20:19:23.000000000 +0100
+++ src/hostfs.c	2020-05-07 21:05:20.000000000 +0100
@@ -273,6 +273,9 @@
     case '>':
       *host_path++ = '^';
       break;
+    case (char) 160:
+      *host_path++ = ' ';
+      break;
     default:
       *host_path++ = *path;
       break;
@@ -539,7 +542,7 @@
   while ((entry = readdir(d)) != NULL) {
     char entry_path[PATH_MAX], ro_leaf[PATH_MAX];
 
-    /* Ignore the current directory and it's parent */
+    /* Ignore the current directory and its parent */
     if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
       continue;
     }
@@ -1650,7 +1653,7 @@
     char entry_path[PATH_MAX], ro_leaf[PATH_MAX];
     unsigned string_space;
 
-    /* Ignore the current directory and it's parent */
+    /* Ignore the current directory and its parent */
     if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
       continue;
     }
--- original/iomd.c	2020-05-06 20:19:23.000000000 +0100
+++ src/iomd.c	2020-05-07 21:05:20.000000000 +0100
@@ -840,19 +840,28 @@
 	}
 	/* Middle */
 	if (mouse_buttons & 4) {
+        
+#ifdef __APPLE__
+        temp |= 0x20;
+#else
 		if (config.mousetwobutton) {
 			temp |= 0x10; // bit 4
 		} else {
 			temp |= 0x20; // bit 5
 		}
+#endif
 	}
 	/* Right */
 	if (mouse_buttons & 2) {
+#ifdef __APPLE__
+        temp |= 0x10;
+#else
 		if (config.mousetwobutton) {
 			temp |= 0x20; // bit 5
 		} else {
 			temp |= 0x10; // bit 4
 		}
+#endif
 	}
 
 	/* bit 0 contains the monitor id bit, 0 for VGA, 1 for TV type monitors.
--- original/keyboard.c	2020-05-06 20:19:23.000000000 +0100
+++ src/keyboard.c	2020-05-07 21:05:20.000000000 +0100
@@ -44,6 +44,10 @@
 #include "arm.h"
 #include "i8042.h"
 
+#ifdef __APPLE__
+#include "keyboard_macosx.h"
+#endif
+
 /* Keyboard Commands */
 #define KBD_CMD_ENABLE		0xf4
 #define KBD_CMD_RESET		0xff
@@ -254,6 +258,10 @@
 	/* Mousehack reset */
 	mouse_hack.pointer = 0;
 	mouse_hack.cursor_linked = 1;
+    
+#ifdef __APPLE__
+    keyboard_reset_modifiers(0);
+#endif
 }
 
 static uint8_t
@@ -731,6 +739,7 @@
 	{
 		uint8_t tmp;
 
+#ifndef __APPLE__
 		if (config.mousetwobutton) {
 			/* To help people with only two buttons on their mouse,
 			   swap the behaviour of middle and right buttons */
@@ -740,6 +749,7 @@
 
 			mouseb = mousel | (mousem << 1) | (mouser << 2);
 		}
+#endif
 
 		tmp = (mouseb & 7) | 8;
 
@@ -1192,6 +1202,15 @@
 	if (mouse.buttons & 1) { 
 		buttons |= 4;			/* Left button */
 	}
+    
+#ifdef __APPLE__
+    if (mouse.buttons & 2) {
+        buttons |= 1;        /* Right button */
+    }
+    if (mouse.buttons & 4) {
+        buttons |= 2;         /* Middle button */
+    }
+#else
 	if (config.mousetwobutton) {
 		/* To help people with only two buttons on their mouse, swap
 		   the behaviour of middle and right buttons */
@@ -1209,6 +1228,8 @@
 			buttons |= 2; 		/* Middle button */
 		}
 	}
+#endif
+    
 	arm.reg[2] = buttons;
 
 	arm.reg[3] = 0; /* R3 = time of button change */
@@ -1258,4 +1279,4 @@
 	mouse_osunits_to_host(osx, osy, &x, &y);
 
 	rpcemu_move_host_mouse(x, y);
-}
\ No newline at end of file
+}
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/qt5/keyboard_macosx.c	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,351 @@
+/*
+ RPCEmu - An Acorn system emulator
+ 
+ Copyright (C) 2017 Matthew Howkins
+ 
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "rpcemu.h"
+#include "keyboard.h"
+
+#include <Carbon/Carbon.h>
+
+#define UNUSED(x) (void)(x)
+
+const int MAX_KEYBOARD_LAYOUTS = 20;
+
+typedef enum
+{
+    keyboardLayoutUndefined = 0,
+    keyboardLayoutBritish = 1,
+    keyboardLayoutFrench = 2
+} KeyboardLayoutType;
+
+static int keyboardType;
+
+typedef struct {
+    uint32_t    virtual_key[MAX_KEYBOARD_LAYOUTS];      // Cocoa virtual keys
+    uint8_t     set_2[8];                               // PS/2 Set 2 make code
+} KeyMapInfo;
+
+// Mac virtual keys can be found in the following file:
+//
+// /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h.
+//
+// Key mappings are defined as follows:
+//
+// The first member is an array of virtual key codes.  There will be at least three elements in the array for each key.
+//
+// The first element indicates whether or not there are different mappings for different keyboard layouts for this key code.
+// If the value is 0, each keyboard layout uses the same mapping.  Where the value is 1, there are different mappings for different layouts.
+// An example of the former is "0" and of the latter, "Z" (in French, this is "Y").
+//
+// The second element in the array is the virtual key to use for the default language, British.
+// If additional, non-British languages are defined in the 'KeyboardLayoutType' enumeration (above) and in the
+// 'configureKeyboardLayout' function (below), virtual keys for these languages can be specified.
+// For example, on a French keyboard, 'Y' and 'Z' are transposed.  Therefore, for each of the mappings for these keys,
+// two virtual keys are listed.
+//
+// The list of virtual key codes must be terminated with an 0xFFFF element.
+//
+// The second member is an array of PS/2 set 2 codes.
+
+static const KeyMapInfo key_map[] = {
+    { { 0, kVK_Escape, 0xFFFF }, { 0x76 } },                       // Escape
+
+    { { 0, kVK_ISO_Section, 0xFFFF }, { 0x0e } },                  // `
+    { { 0, kVK_ANSI_1, 0xFFFF}, { 0x16 } },                        // 1
+    { { 0, kVK_ANSI_2, 0xFFFF }, { 0x1e } },                       // 2
+    { { 0, kVK_ANSI_3, 0xFFFF}, { 0x26 } },                        // 3
+    { { 0, kVK_ANSI_4, 0xFFFF }, { 0x25 } },                       // 4
+    { { 0, kVK_ANSI_5, 0xFFFF }, { 0x2e } },                       // 5
+    { { 0, kVK_ANSI_6, 0xFFFF }, { 0x36 } },                       // 6
+    { { 0, kVK_ANSI_7, 0xFFFF }, { 0x3d } },                       // 7
+    { { 0, kVK_ANSI_8, 0xFFFF }, { 0x3e } },                       // 8
+    { { 0, kVK_ANSI_9, 0xFFFF }, { 0x46 } },                       // 9
+    { { 0, kVK_ANSI_0, 0xFFFF }, { 0x45 } },                       // 0
+    { { 0, kVK_ANSI_Minus, 0xFFFF }, { 0x4e } },                   // -
+    { { 0, kVK_ANSI_Equal, 0xFFFF }, { 0x55 } },                   // =
+    { { 0, kVK_Delete, 0xFFFF }, { 0x66 } },                       // Backspace
+
+    { { 0, kVK_Tab, 0xFFFF }, { 0x0d } },                          // Tab
+    { { 0, kVK_ANSI_Q, 0xFFFF }, { 0x15 } },                       // Q
+    { { 0, kVK_ANSI_W, 0xFFFF }, { 0x1d } },                       // W
+    { { 0, kVK_ANSI_E, 0xFFFF }, { 0x24 } },                       // E
+    { { 0, kVK_ANSI_R, 0xFFFF }, { 0x2d } },                       // R
+    { { 0, kVK_ANSI_T, 0xFFFF}, { 0x2c } },                        // T
+    { { 1, kVK_ANSI_Y, kVK_ANSI_Z, 0xFFFF }, { 0x35 } },           // Y
+    { { 0, kVK_ANSI_U, 0xFFFF }, { 0x3c } },                       // U
+    { { 0, kVK_ANSI_I, 0xFFFF }, { 0x43 } },                       // I
+    { { 0, kVK_ANSI_O, 0xFFFF }, { 0x44 } },                       // O
+    { { 0, kVK_ANSI_P, 0xFFFF }, { 0x4d } },                       // P
+    { { 0, kVK_ANSI_LeftBracket, 0xFFFF }, { 0x54 } },             // [
+    { { 0, kVK_ANSI_RightBracket, 0xFFFF }, { 0x5b } },            // ]
+    { { 0, kVK_Return, 0xFFFF }, { 0x5a } },                       // Return
+
+    { { 0, kVK_Control, 0xFFFF }, { 0x14 } },                      // Left Ctrl
+    { { 0, kVK_ANSI_A, 0xFFFF }, { 0x1c } },                       // A
+    { { 0, kVK_ANSI_S, 0xFFFF }, { 0x1b } },                       // S
+    { { 0, kVK_ANSI_D, 0xFFFF }, { 0x23 } },                       // D
+    { { 0, kVK_ANSI_F, 0xFFFF }, { 0x2b } },                       // F
+    { { 0, kVK_ANSI_G, 0xFFFF }, { 0x34 } },                       // G
+    { { 1, kVK_ANSI_H, 0xFFFF }, { 0x33 } },                       // H
+    { { 0, kVK_ANSI_J, 0xFFFF }, { 0x3b } },                       // J
+    { { 0, kVK_ANSI_K, 0xFFFF }, { 0x42 } },                       // K
+    { { 0, kVK_ANSI_L, 0xFFFF }, { 0x4b } },                       // L
+    { { 0, kVK_ANSI_Semicolon, 0xFFFF }, { 0x4c } },               // ;
+    { { 0, kVK_ANSI_Quote, 0xFFFF }, { 0x52 } },                   // '
+    { { 0, kVK_ANSI_Backslash, 0xFFFF }, { 0x5d } },               // # (International only)
+
+    { { 0, kVK_ANSI_Grave, 0xFFFF }, { 0x61 } },                   // `
+    { { 1, kVK_ANSI_Z, kVK_ANSI_Y, 0xFFFF }, { 0x1a } },           // Z
+    { { 0, kVK_ANSI_X, 0xFFFF }, { 0x22 } },                       // X
+    { { 0, kVK_ANSI_C, 0xFFFF }, { 0x21 } },                       // C
+    { { 0, kVK_ANSI_V, 0xFFFF }, { 0x2a } },                       // V
+    { { 0, kVK_ANSI_B, 0xFFFF }, { 0x32 } },                       // B
+    { { 0, kVK_ANSI_N, 0xFFFF }, { 0x31 } },                       // N
+    { { 0, kVK_ANSI_M, 0xFFFF }, { 0x3a } },                       // M
+    { { 0, kVK_ANSI_Comma, 0xFFFF }, { 0x41 } },                   // ,
+    { { 0, kVK_ANSI_Period, 0xFFFF }, { 0x49 } },                  // .
+    { { 0, kVK_ANSI_Slash, 0xFFFF }, { 0x4a } },                   // /
+
+    { { 0, kVK_Space, 0xFFFF }, { 0x29 } },                        // Space
+
+    { { 0, kVK_F1, 0xFFFF }, { 0x05 } },                           // F1
+    { { 0, kVK_F2, 0xFFFF }, { 0x06 } },                           // F2
+    { { 0, kVK_F3, 0xFFFF }, { 0x04 } },                           // F3
+    { { 0, kVK_F4, 0xFFFF }, { 0x0c } },                           // F4
+    { { 0, kVK_F5, 0xFFFF }, { 0x03 } },                           // F5
+    { { 0, kVK_F6, 0xFFFF }, { 0x0b } },                           // F6
+    { { 0, kVK_F7, 0xFFFF }, { 0x83 } },                           // F7
+    { { 0, kVK_F8, 0xFFFF }, { 0x0a } },                           // F8
+    { { 0, kVK_F9, 0xFFFF }, { 0x01 } },                           // F9
+    { { 0, kVK_F10, 0xFFFF }, { 0x09 } },                          // F10
+    { { 0, kVK_F11, 0xFFFF }, { 0x78 } },                          // F11
+    { { 0, kVK_F12, 0xFFFF }, { 0x07 } },                          // F12
+
+    { { 0, kVK_F13, 0xFFFF }, { 0xe0, 0x7c } },                    // Print Screen/SysRq
+    { { 0, kVK_F14, 0xFFFF }, { 0x7e } },                          // Scroll Lock
+    { { 0, kVK_F15, 0xFFFF }, { 0xe1, 0x14, 0x77, 0xe1, 0xf0, 0x14, 0xf0, 0x77 } },    // Break
+
+    { { 0, kVK_ANSI_KeypadClear, 0xFFFF }, { 0x77 } },             // Keypad Num Lock
+    { { 0, kVK_ANSI_KeypadDivide, 0xFFFF }, { 0xe0, 0x4a } },      // Keypad /
+    { { 0, kVK_ANSI_KeypadMultiply, 0xFFFF }, { 0x7c } },          // Keypad *
+    { { 0, kVK_ANSI_Keypad7, 0xFFFF }, { 0x6c } },                 // Keypad 7
+    { { 0, kVK_ANSI_Keypad8, 0xFFFF }, { 0x75 } },                 // Keypad 8
+    { { 0, kVK_ANSI_Keypad9, 0xFFFF }, { 0x7d } },                 // Keypad 9
+    { { 0, kVK_ANSI_KeypadMinus, 0xFFFF }, { 0x7b } },             // Keypad -
+    { { 0, kVK_ANSI_Keypad4, 0xFFFF }, { 0x6b } },                 // Keypad 4
+    { { 0, kVK_ANSI_Keypad5, 0xFFFF }, { 0x73 } },                 // Keypad 5
+    { { 0, kVK_ANSI_Keypad6, 0xFFFF }, { 0x74 } },                 // Keypad 6
+    { { 0, kVK_ANSI_KeypadPlus, 0xFFFF }, { 0x79 } },              // Keypad +
+    { { 0, kVK_ANSI_Keypad1, 0xFFFF }, { 0x69 } },                 // Keypad 1
+    { { 0, kVK_ANSI_Keypad2, 0xFFFF }, { 0x72 } },                 // Keypad 2
+    { { 0, kVK_ANSI_Keypad3, 0xFFFF }, { 0x7a } },                 // Keypad 3
+    { { 0, kVK_ANSI_Keypad0, 0xFFFF }, { 0x70 } },                 // Keypad 0
+    { { 0, kVK_ANSI_KeypadDecimal, 0xFFFF }, { 0x71 } },           // Keypad .
+    { { 0, kVK_ANSI_KeypadEnter, 0xFFFF }, { 0xe0, 0x5a } },       // Keypad Enter
+
+    { { 0, kVK_Function, 0xFFFF }, { 0xe0, 0x70 } },               // Insert
+    { { 0, kVK_ForwardDelete, 0xFFFF }, { 0xe0, 0x71 } },          // Delete
+    { { 0, kVK_Home, 0xFFFF }, { 0xe0, 0x6c } },                   // Home
+    { { 0, kVK_End, 0xFFFF }, { 0xe0, 0x69 } },                    // End
+    { { 0, kVK_UpArrow, 0xFFFF }, { 0xe0, 0x75 } },                // Up
+    { { 0, kVK_DownArrow, 0xFFFF }, { 0xe0, 0x72 } },              // Down
+    { { 0, kVK_LeftArrow, 0xFFFF }, { 0xe0, 0x6b } },              // Left
+    { { 0, kVK_RightArrow, 0xFFFF }, { 0xe0, 0x74 } },             // Right
+    { { 0, kVK_PageUp, 0xFFFF }, { 0xe0, 0x7d } },                 // Page Up
+    { { 0, kVK_PageDown, 0xFFFF }, { 0xe0, 0x7a } },               // Page Down
+
+    { { 0, kVK_F16, 0xFFFF }, { 0xe0, 0x2f } },                    // Application (Win Menu)
+
+    { { 0xFFFF }, { 0, 0 } },
+};
+
+typedef enum
+{
+    modifierKeyStateShift = 0,
+    modifierKeyStateControl = 1,
+    modifierKeyStateAlt = 2,
+    modifierKeyStateCapsLock = 3,
+    modifierKeyStateCommand = 4
+} ModifierKeyCode;
+
+typedef struct
+{
+    int keyState[5];
+} ModifierState;
+
+ModifierState modifierState;
+
+typedef struct {
+    uint32_t modifierMask;
+    int checkMask;
+    uint maskLeft;
+    uint maskRight;
+    uint8_t set_2_left[8];
+    uint8_t set_2_right[8];
+    int simulateMenuButton;
+} ModifierMapInfo;
+
+// The following are from the "NSEventModifierFlagOption" enumeration.
+typedef enum
+{
+    nativeModifierFlagShift = (1 << 17),
+    nativeModifierFlagControl = (1<< 18),
+    nativeModifierFlagOption = (1 << 19),
+    nativeModifierFlagCommand = (1 << 20)
+} NativeModifierFlag;
+
+static const ModifierMapInfo modifier_map[] = {
+    {nativeModifierFlagShift, modifierKeyStateShift, 0x102, 0x104, {0x12}, {0x59}, 0 },                      // Shift.
+    {nativeModifierFlagControl, modifierKeyStateControl, 0x101, 0x2100, {0x14}, {0xe0, 0x14}, 0},            // Control.
+    {nativeModifierFlagOption, modifierKeyStateAlt, 0x120, 0x140, {0x11}, {0xe0, 0x11}, 0},                  // Alt.
+    {nativeModifierFlagCommand, modifierKeyStateCommand, 0x100108, 0x100110, {0xe0, 0x1f}, {0xe0, 0x27}, 1}, // Command.
+    {0x1<<31, 0, 0, 0, {0}, {0}, 0 },
+};
+
+int get_virtual_key_index(size_t k)
+{
+    if (key_map[k].virtual_key[0] == 0) return 1;
+    
+    for (int i = 1; i < MAX_KEYBOARD_LAYOUTS; i++)
+    {
+        if (key_map[k].virtual_key[i] == 0xFFFF) break;
+        if (i == keyboardType) return i;
+    }
+
+    return 0;
+}
+
+const uint8_t *
+keyboard_map_key(uint32_t native_scancode)
+{
+    size_t k;
+    int index;
+    
+    for (k = 0; key_map[k].virtual_key[0] != 0xFFFF; k++) {
+        index = get_virtual_key_index(k);
+
+        if (key_map[k].virtual_key[index] == native_scancode) {
+            return key_map[k].set_2;
+        }
+    }
+    return NULL;
+}
+
+void keyboard_handle_modifier_keys(uint mask)
+{
+    size_t k;
+    
+    for (k = 0; modifier_map[k].modifierMask != (1U << 31); k++)
+    {
+        int state = modifierState.keyState[modifier_map[k].checkMask];
+        uint modifierMask = modifier_map[k].modifierMask;
+      
+        if ((mask & modifierMask) != 0)
+        {
+            if (modifier_map[k].simulateMenuButton && config.mousetwobutton && state == 0)
+            {
+                state = 3;
+                mouse_mouse_press(4);
+            }
+            else
+            {
+                if ((mask & modifier_map[k].maskLeft) == modifier_map[k].maskLeft && (state & 1) == 0)
+                {
+                    state |= 1;
+                    keyboard_key_press(modifier_map[k].set_2_left);
+                }
+
+                if ((mask & modifier_map[k].maskRight) == modifier_map[k].maskRight && (state & 2) == 0)
+                {
+                    state |= 2;
+                    keyboard_key_press(modifier_map[k].set_2_right);
+                }
+            }
+        }
+        else if ((mask & modifierMask) == 0 && state != 0)
+        {
+            if (config.mousetwobutton && modifier_map[k].simulateMenuButton)
+            {
+                state = 0;
+                mouse_mouse_release(4);
+            }
+            else
+            {
+                if (state & 1)
+                {
+                    state &= ~1;
+                    keyboard_key_release(modifier_map[k].set_2_left);
+                }
+                if (state & 2)
+                {
+                    state &= ~2;
+                    keyboard_key_release(modifier_map[k].set_2_right);
+                }
+            }
+        }
+
+        modifierState.keyState[modifier_map[k].checkMask] = state;
+    }
+}
+
+void keyboard_reset_modifiers(int sendReleaseEvent)
+{
+    size_t k;
+
+    for (k = 0; modifier_map[k].modifierMask != (1U << 31); k++)
+    {
+        int state = modifierState.keyState[modifier_map[k].checkMask];
+        
+        if (sendReleaseEvent)
+        {
+            if (state & 1)
+            {
+                keyboard_key_release(modifier_map[k].set_2_left);
+            }
+            if (state & 2)
+            {
+                keyboard_key_release(modifier_map[k].set_2_right);
+            }
+        }
+        
+        modifierState.keyState[modifier_map[k].checkMask] = 0;
+    }
+}
+
+void keyboard_configure_layout(const char *layoutName)
+{
+    if (!strcmp(layoutName, "British")) keyboardType = keyboardLayoutBritish;
+    else if (!strcasecmp(layoutName, "French")) keyboardType = keyboardLayoutFrench;
+    else keyboardType = keyboardLayoutUndefined;
+    
+    if (keyboardType == keyboardLayoutUndefined)
+    {
+        fprintf(stderr, "Unsupported keyboard layout '%s' - reverting to 'British' (0).\n", layoutName);
+        keyboardType = keyboardLayoutBritish;
+    }
+    else
+    {
+        fprintf(stderr, "Using keyboard layout '%s' (%d).\n", layoutName, keyboardType);
+    }
+}
+
+int keyboard_check_special_keys()
+{
+    return (modifierState.keyState[modifierKeyStateControl] != 0 && modifierState.keyState[modifierKeyStateCommand] != 0);
+}
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/qt5/keyboard_macosx.h	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,40 @@
+/*
+ RPCEmu - An Acorn system emulator
+ 
+ Copyright (C) 2017 Matthew Howkins
+ 
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __KEYBOARD_MACOSX_H__
+#define __KEYBOARD_MACOSX_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+    
+extern void keyboard_handle_modifier_keys(uint32_t mask);
+extern void keyboard_reset_modifiers(int sendReleaseEvent);
+extern void keyboard_configure_layout(const char *layoutName);
+extern int keyboard_check_special_keys();
+    
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+        
+#endif
+
--- original/qt5/main_window.cpp	2020-05-06 20:19:23.000000000 +0100
+++ src/qt5/main_window.cpp	2020-05-07 21:14:11.000000000 +0100
@@ -31,7 +31,12 @@
 
 #if defined(Q_OS_WIN32)
 #include "Windows.h"
-#endif /* Q_OS_WIN32 */ 
+#endif /* Q_OS_WIN32 */
+
+#if defined(Q_OS_MACOS)
+#include "macosx/events-macosx.h"
+#include "keyboard_macosx.h"
+#endif /* Q_OS_MACOS */
 
 #include "rpcemu.h"
 #include "keyboard.h"
@@ -66,6 +71,9 @@
 void
 MainDisplay::mouseMoveEvent(QMouseEvent *event)
 {
+    // Ignore mouse events if the application is terminating.
+    if (quited) return;
+    
 	if((!pconfig_copy->mousehackon && mouse_captured) || full_screen) {
 		QPoint middle;
 
@@ -90,6 +98,9 @@
 void
 MainDisplay::mousePressEvent(QMouseEvent *event)
 {
+    // Ignore mouse events if the application is terminating.
+    if (quited) return;
+    
 	// Handle turning on mouse capture in capture mouse mode
 	if(!pconfig_copy->mousehackon) {
 		if(!mouse_captured) {
@@ -110,6 +121,9 @@
 void
 MainDisplay::mouseReleaseEvent(QMouseEvent *event)
 {
+    // Ignore mouse events if the application is terminating.
+    if (quited) return;
+    
 	if (event->button() & 7) {
 		emit this->emulator.mouse_release_signal(event->button() & 7);
 	}
@@ -443,6 +457,11 @@
 
 	// Clear the list of keys considered to be held in the host
 	held_keys.clear();
+    
+#if defined(Q_OS_MACOS)
+    emit this->emulator.modifier_keys_reset_signal();
+#endif /* Q_OS_MACOS */
+    
 }
 
 /**
@@ -505,53 +524,18 @@
 	}
 
 	// Special case, check for Ctrl-End, our multi purpose do clever things key
-	if((Qt::Key_End == event->key()) && (event->modifiers() & Qt::ControlModifier)) {
-		if(full_screen) {
-			// Change Full Screen -> Windowed
-
-			display->set_full_screen(false);
-
-			int host_xsize, host_ysize;
-			display->get_host_size(host_xsize, host_ysize);
-			display->setFixedSize(host_xsize, host_ysize);
-
-			menuBar()->setVisible(true);
-			this->showNormal();
-			this->setFixedSize(this->sizeHint());
-
-			full_screen = false;
-
-			// Request redraw of display
-			display->update();
-			
-			// If we were in mousehack mode before entering fullscreen
-			// return to it now
-			if(reenable_mousehack) {
-				emit this->emulator.mouse_hack_signal();	
-			}
-			reenable_mousehack = false;
-			
-			// If we were in mouse capture mode before entering fullscreen
-			// and we hadn't captured the mouse, display the host cursor now
-			if(!config_copy.mousehackon && !mouse_captured) {
-				this->display->setCursor(Qt::ArrowCursor);
-			}
-			
-			return;
-		} else if(!pconfig_copy->mousehackon && mouse_captured) {
-			// Turn off mouse capture
-			mouse_captured = 0;
-
-			// show pointer in mouse capture mode when it's not been captured
-			this->display->setCursor(Qt::ArrowCursor);
-
-			return;
-		}
+    if((Qt::Key_End == event->key()) && (event->modifiers() & Qt::ControlModifier))
+    {
+        processMagicKeys();
 	}
 
 	// Regular case pass key press onto the emulator
 	if (!event->isAutoRepeat()) {
-		native_keypress_event(event->nativeScanCode());
+        #if defined(Q_OS_MACOS)
+                native_keypress_event(event->nativeVirtualKey(), event->nativeModifiers());
+        #else
+                native_keypress_event(event->nativeScanCode(), event->nativeModifiers());
+        #endif    /* Q_OS_MACOS */
 	}
 }
 
@@ -571,7 +555,11 @@
 
 	// Regular case pass key release onto the emulator
 	if (!event->isAutoRepeat()) {
-		native_keyrelease_event(event->nativeScanCode());
+#if defined(Q_OS_MACOS)
+        native_keyrelease_event(event->nativeVirtualKey(), event->nativeModifiers());
+#else
+        native_keyrelease_event(event->nativeScanCode(), event->nativeModifiers());
+#endif    /* Q_OS_MACOS */
 	}
 }
 
@@ -581,8 +569,25 @@
  * @param scan_code Native scan code of key
  */
 void
-MainWindow::native_keypress_event(unsigned scan_code)
+MainWindow::native_keypress_event(unsigned scan_code, unsigned modifiers)
 {
+#if defined(Q_OS_MACOS)
+    if (!(scan_code == 0 && modifiers == 0))
+    {
+        // Check the key isn't already marked as held down (else ignore)
+        // (to deal with potentially inconsistent host messages)
+        bool found = (std::find(held_keys.begin(), held_keys.end(), scan_code) != held_keys.end());
+
+        if (!found) {
+            // Add the key to the list of held_keys, that will be released
+            // when the window loses the focus
+            held_keys.insert(held_keys.end(), scan_code);
+
+            emit this->emulator.key_press_signal(scan_code);
+        }
+    }
+#else
+    
 	// Check the key isn't already marked as held down (else ignore)
 	// (to deal with potentially inconsistent host messages)
 	bool found = (std::find(held_keys.begin(), held_keys.end(), scan_code) != held_keys.end());
@@ -592,8 +597,9 @@
 		// when the window loses the focus
 		held_keys.insert(held_keys.end(), scan_code);
 
-		emit this->emulator.key_press_signal(scan_code);
+
 	}
+#endif
 }
 
 /**
@@ -602,8 +608,26 @@
  * @param scan_code Native scan code of key
  */
 void
-MainWindow::native_keyrelease_event(unsigned scan_code)
+MainWindow::native_keyrelease_event(unsigned scan_code, unsigned modifiers)
 {
+#if defined(Q_OS_MACOS)
+    
+    if (!(scan_code == 0 && modifiers == 0))
+    {
+        // Check the key is marked as held down (else ignore)
+        // (to deal with potentially inconsistent host messages)
+        bool found = (std::find(held_keys.begin(), held_keys.end(), scan_code) != held_keys.end());
+
+        if (found) {
+            // Remove the key from the list of held_keys, that will be released
+            // when the window loses the focus
+            held_keys.remove(scan_code);
+
+            emit this->emulator.key_release_signal(scan_code);
+        }
+    }
+
+#else
 	// Check the key is marked as held down (else ignore)
 	// (to deal with potentially inconsistent host messages)
 	bool found = (std::find(held_keys.begin(), held_keys.end(), scan_code) != held_keys.end());
@@ -615,6 +639,7 @@
 
 		emit this->emulator.key_release_signal(scan_code);
 	}
+#endif
 }
 
 void
@@ -1312,7 +1337,13 @@
 
 	if(!pconfig_copy->mousehackon) {
 		if(mouse_captured) {
+            
+#if defined(Q_OS_MACOS)
+            capture_text = " Press CTRL-COMMAND to release mouse";
+#else
 			capture_text = " Press CTRL-END to release mouse";
+#endif
+            
 		} else {
 			capture_text = " Click to capture mouse";
 		}
@@ -1415,6 +1446,52 @@
 	}
 }
 
+void
+MainWindow::processMagicKeys()
+{
+    if(full_screen) {
+        // Change Full Screen -> Windowed
+
+        display->set_full_screen(false);
+
+        int host_xsize, host_ysize;
+        display->get_host_size(host_xsize, host_ysize);
+        display->setFixedSize(host_xsize, host_ysize);
+
+        menuBar()->setVisible(true);
+        this->showNormal();
+        this->setFixedSize(this->sizeHint());
+
+        full_screen = false;
+
+        // Request redraw of display
+        display->update();
+        
+        // If we were in mousehack mode before entering fullscreen
+        // return to it now
+        if(reenable_mousehack) {
+            emit this->emulator.mouse_hack_signal();
+        }
+        reenable_mousehack = false;
+        
+        // If we were in mouse capture mode before entering fullscreen
+        // and we hadn't captured the mouse, display the host cursor now
+        if(!config_copy.mousehackon && !mouse_captured) {
+            this->display->setCursor(Qt::ArrowCursor);
+        }
+        
+        return;
+    } else if(!pconfig_copy->mousehackon && mouse_captured) {
+        // Turn off mouse capture
+        mouse_captured = 0;
+
+        // show pointer in mouse capture mode when it's not been captured
+        this->display->setCursor(Qt::ArrowCursor);
+
+        return;
+    }
+}
+
 #if defined(Q_OS_WIN32)
 /**
  * windows pre event handler used by us to modify some default behaviour
@@ -1473,3 +1550,46 @@
 	return false;
 }
 #endif // Q_OS_WIN32
+
+#if defined(Q_OS_MACOS)
+/**
+ * On OS X, handle additional events for modifier keys.  The normal key press/release
+ * events do not differentiate between left and right.
+ *
+ * @param eventType unused
+ * @param message window event NSEvent data
+ * @param result unused
+ * @return bool of whether we've handled the event (true) or OS X/QT should deal with it (false)
+ */
+bool
+MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
+{
+    Q_UNUSED(eventType);
+    Q_UNUSED(result);
+    
+    NativeEvent *event = handle_native_event(message);
+    if (!event->processed)
+    {
+        free(event);
+        return false;
+    }
+    
+    if (event->eventType == nativeEventTypeModifiersChanged)
+    {
+        // Modifier key state has changed.
+        emit this->emulator.modifier_keys_changed_signal(event->modifierMask);
+        
+        if (keyboard_check_special_keys())
+        {
+            // Magic key combination to release mouse capture.
+            processMagicKeys();
+        }
+        
+        free(event);
+    }
+    
+    return true;
+}
+
+#endif /* Q_OS_MACOS */
+
--- original/qt5/main_window.h	2020-05-06 20:19:23.000000000 +0100
+++ src/qt5/main_window.h	2020-05-07 21:14:11.000000000 +0100
@@ -109,7 +109,7 @@
 	void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE;
 	void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
 	void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
-#if defined(Q_OS_WIN32)
+#if defined(Q_OS_WIN32) || defined(Q_OS_MACOS)
 	bool nativeEvent(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE;
 #endif /* Q_OS_WIN32 */
 	
@@ -165,9 +165,11 @@
 
 	void cdrom_menu_selection_update(const QAction *cdrom_action);
 
-	void native_keypress_event(unsigned scan_code);
-	void native_keyrelease_event(unsigned scan_code);
+	void native_keypress_event(unsigned scan_code, unsigned modifiers);
+	void native_keyrelease_event(unsigned scan_code, unsigned modifiers);
 	void release_held_keys();
+    
+    void processMagicKeys();
 
 	bool full_screen;
 	bool reenable_mousehack; ///< Did we disable mousehack entering fullscreen and have to reenable it on leaving fullscreen?
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/macosx/network-macosx.c	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,97 @@
+/*
+  RPCEmu - An Acorn system emulator
+
+  Copyright (C) 2005-2010 Sarah Walker
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* RPCemu networking */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <pwd.h>
+#include <grp.h>
+#include <signal.h>
+
+#include "rpcemu.h"
+#include "mem.h"
+#include "podules.h"
+#include "network.h"
+
+int
+network_plt_init(void)
+{
+    // Do nothing on a Mac, as TUN/TAP is not supported.
+    return 0;
+}
+
+/**
+ * Shutdown any running network components.
+ *
+ * Called on program shutdown and program reset after
+ * configuration has changed.
+ */
+void
+network_plt_reset(void)
+{
+    // Do nothing on a Mac, as TUN/TAP is not supported.
+}
+
+uint32_t
+network_plt_rx(uint32_t errbuf, uint32_t mbuf, uint32_t rxhdr, uint32_t *dataavail)
+{
+    NOT_USED(errbuf);
+    NOT_USED(mbuf);
+    NOT_USED(rxhdr);
+    NOT_USED(dataavail);
+    
+    // Do nothing on a Mac, as TUN/TAP is not supported.
+    return 0;
+}
+
+uint32_t
+network_plt_tx(uint32_t errbuf, uint32_t mbufs, uint32_t dest, uint32_t src, uint32_t frametype)
+{
+    NOT_USED(errbuf);
+    NOT_USED(mbufs);
+    NOT_USED(dest);
+    NOT_USED(src);
+    NOT_USED(frametype);
+
+    // Do nothing on a Mac, as TUN/TAP is not supported.
+    return 0;
+}
+
+void
+network_plt_setirqstatus(uint32_t address)
+{
+    NOT_USED(address);
+    
+    // Do nothing on a Mac, as TUN/TAP is not supported.
+}
--- original/network.c	2020-05-06 20:19:23.000000000 +0100
+++ src/network.c	2020-05-07 21:14:11.000000000 +0100
@@ -80,8 +80,13 @@
 	filebase = chunkbase + (8 * 2) + 4; // required size for two entries
 	poduleromsize = filebase + ((sizeof(description) + 3) & ~3u); // Word align description string
 
-	// Add on size for driver module if it can be opened successfully
-	f = fopen("netroms/EtherRPCEm,ffa", "rb");
+    char filename[512];
+    snprintf(filename,sizeof(filename), "%snetroms/EtherRPCEm,ffa", rpcemu_get_datadir());
+    
+    rpclog("network_rom_init: Attempting to load Ethernet ROM from '%s'\n", filename);
+    
+    // Add on size for driver module if it can be opened successfully
+    f = fopen(filename, "rb");
 	if (f != NULL) {
 		long len;
 
@@ -124,6 +129,8 @@
 		if (len == module_file_size) { // Load was OK
 			len = (len + 3) & ~3u;
 			makechunk(0x81, filebase, (uint32_t) len); // 0x81 = RISC OS, ROM
+            
+            rpclog("network_rom_init: Successfuly loaded 'EtherRPCEm,ffa' into podulerom\n");
 		}
 	}
 }
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/macosx/preferences-macosx.h	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,38 @@
+///*
+// RPCEmu - An Acorn system emulator
+//
+// Copyright (C) 2017 Matthew Howkins
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+// */
+
+#ifndef __PREFERENCES_MACOSX_H__
+#define __PREFERENCES_MACOSX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void init_preferences(void);
+extern void preferences_set_data_directory(const char *path);
+extern const char *preferences_get_data_directory();
+
+extern bool promptForDataDirectory;
+  
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __PREFERENCES_MACOSX_H__
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/macosx/preferences-macosx.m	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,86 @@
+/*
+ RPCEmu - An Acorn system emulator
+ 
+ Copyright (C) 2017 Matthew Howkins
+ 
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ 
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ 
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define UNUSED(x) (void)(x)
+
+#include <stddef.h>
+#include <stdint.h>
+#include <dirent.h>
+
+#include <Cocoa/Cocoa.h>
+#include <Carbon/Carbon.h>
+#include <IOKit/hid/IOHIDLib.h>
+
+#include "rpcemu.h"
+
+bool promptForDataDirectory;
+NSString* const KeyDataDirectory = @"DataDirectory";
+
+void init_preferences(void)
+{
+  NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary];
+  [defaultValues setObject: @"" forKey:KeyDataDirectory];
+  
+  [[NSUserDefaults standardUserDefaults] registerDefaults: defaultValues];
+  
+  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+  
+  // Check to see if there is a proper path for the data directory.
+  // If not, prompt for one.
+  NSString *dataDirectory = [defaults stringForKey: KeyDataDirectory];
+  if (dataDirectory == nil || [dataDirectory length] == 0)
+  {
+    promptForDataDirectory = true;
+  }
+  else
+  {
+    const char *str = [dataDirectory UTF8String];
+
+    // Check the folder exists.
+    DIR *ptr = opendir(str);
+    if (ptr)
+    {
+      closedir(ptr);
+      rpcemu_set_datadir(str);
+      
+      promptForDataDirectory = false;
+    }
+    else
+    {
+      promptForDataDirectory = true;
+    }
+  }
+}
+
+void preferences_set_data_directory(const char *path)
+{
+  NSString *dataDirectory = [NSString stringWithUTF8String: path];
+  
+  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+  [defaults setObject:dataDirectory forKey:KeyDataDirectory];
+}
+
+const char* preferences_get_data_directory()
+{
+  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+  NSString *path = [defaults stringForKey: KeyDataDirectory];
+  
+  return [path UTF8String];
+}
--- original/rpc-machdep.c	2020-05-06 20:19:23.000000000 +0100
+++ src/rpc-machdep.c	2020-05-07 21:05:20.000000000 +0100
@@ -26,7 +26,39 @@
    be, but currently this version is used by Linux, all the other autoconf
    based builds and Windows. Only Mac OS X GUI version needs to override */
 
+#ifdef __APPLE__
+#include <dirent.h>
+
+static char datadir[512] = "";
+
+int rpcemu_set_datadir(const char *path)
+{
+  int len = strlen(path);
+  if (len == 0) return 0;
+  
+  if (path[len - 1] != '/')
+  {
+    snprintf(datadir, 512, "%s/", path);
+  }
+  else
+  {
+    strncpy(datadir, path, 512);
+  }
+  
+  DIR *ptr = opendir(datadir);
+  if (ptr)
+  {
+    closedir(ptr);
+    return 1;
+  }
+  
+  return 0;
+}
+
+#else
 static char datadir[512] = "./";
+#endif
+
 static char logpath[1024] = "";
 
 /**
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/rpc-macosx.c	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,87 @@
+/*
+  RPCEmu - An Acorn system emulator
+
+  Copyright (C) 2005-2010 Sarah Walker
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+
+#include <pthread.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+
+#include "rpcemu.h"
+#include "mem.h"
+#include "sound.h"
+#include "vidc20.h"
+
+
+
+
+/**
+ * Return disk space information about a file system.
+ *
+ * @param path Pathname of object within file system
+ * @param d    Pointer to disk_info structure that will be filled in
+ * @return     On success 1 is returned, on error 0 is returned
+ */
+int
+path_disk_info(const char *path, disk_info *d)
+{
+    struct statvfs s;
+    int ret;
+
+    assert(path != NULL);
+    assert(d != NULL);
+
+    if ((ret = statvfs(path, &s)) != 0) {
+        return 0;
+    }
+
+    d->size = (uint64_t) s.f_blocks * (uint64_t) s.f_frsize;
+    d->free = (uint64_t) s.f_bavail * (uint64_t) s.f_frsize;
+
+    return 1;
+}
+
+/**
+ * Log details about the current Operating System version.
+ *
+ * This function should work on all Unix and Unix-like systems.
+ *
+ * Called during program start-up.
+ */
+void
+rpcemu_log_os(void)
+{
+    struct utsname u;
+
+    if (uname(&u) == -1) {
+        rpclog("OS: Could not determine: %s\n", strerror(errno));
+        return;
+    }
+
+    rpclog("OS: SysName = %s\n", u.sysname);
+    rpclog("OS: Release = %s\n", u.release);
+    rpclog("OS: Version = %s\n", u.version);
+    rpclog("OS: Machine = %s\n", u.machine);
+}
--- original/qt5/rpc-qt5.cpp	2020-05-06 20:19:23.000000000 +0100
+++ src/qt5/rpc-qt5.cpp	2020-05-08 14:36:13.000000000 +0100
@@ -47,6 +47,15 @@
 #include "network.h"
 #include "network-nat.h"
 
+#if defined(Q_OS_MACOS)
+#include "choose_dialog.h"
+
+#include "macosx/preferences-macosx.h"
+#include "macosx/hid-macosx.h"
+
+#include "keyboard_macosx.h"
+#endif /* Q_OS_MACOS */
+
 #if defined(Q_OS_WIN32)
 #include "cdrom-ioctl.h"
 
@@ -398,6 +407,23 @@
 
 } // extern "C"
 
+#if defined(Q_OS_MACOS)
+
+int rpcemu_choose_datadirectory()
+{
+  ChooseDialog dialog;
+  if (dialog.exec() == QDialog::Accepted)
+  {
+    const char *path = preferences_get_data_directory();
+
+    return rpcemu_set_datadir(path);
+  }
+
+  return 0;
+}
+
+#endif
+
 /**
  * Program entry point
  *
@@ -416,6 +442,20 @@
 
 	// Add a program icon
 	QApplication::setWindowIcon(QIcon(":/rpcemu_icon.png"));
+    
+#if defined(Q_OS_MACOS)
+    init_preferences();
+    
+    // If there is not a data directory in the application preferences, prompt for one.
+    // This will also prompt if the "Command" key is held down while the application loads.
+    if (promptForDataDirectory || (QApplication::queryKeyboardModifiers() & Qt::ShiftModifier) != 0)
+    {
+        if (!rpcemu_choose_datadirectory())
+        {
+            return 0;
+        }
+    }
+#endif
 	
 	// start enough of the emulator system to allow
 	// the GUI to initialise (e.g. load the config to init
@@ -438,6 +478,11 @@
 	QThread::connect(emulator, &Emulator::finished, emu_thread, &QThread::quit);
 	QThread::connect(emulator, &Emulator::finished, emulator, &Emulator::deleteLater);
 	QThread::connect(emu_thread, &QThread::finished, emu_thread, &QThread::deleteLater);
+    
+#if defined(Q_OS_MACOS)
+    // Initialise the HID manager for CAPS LOCK key events.
+    init_hid_manager();
+#endif
 
 	// Create Main Window
 	MainWindow main_window(*emulator);
@@ -473,6 +518,14 @@
 
 	connect(this, &Emulator::key_release_signal,
 	        this, &Emulator::key_release);
+    
+#if defined(Q_OS_MACOS)
+    // Modifier keys on a Mac must be handled separately, as there is no way of telling
+    // left or right from the key press and key release events due to a lack of scan codes.
+
+    connect(this, &Emulator::modifier_keys_changed_signal, this, &Emulator::modifier_keys_changed);
+    connect(this, &Emulator::modifier_keys_reset_signal, this, &Emulator::modifier_keys_reset);
+#endif /* Q_OS_MACOS */
 
 	connect(this, &Emulator::mouse_move_signal, this, &Emulator::mouse_move);
 	connect(this, &Emulator::mouse_move_relative_signal, this, &Emulator::mouse_move_relative);
@@ -630,6 +683,27 @@
 	keyboard_key_release(scan_codes);
 }
 
+#if defined(Q_OS_MACOS)
+    
+/**
+ * Modifier keys changed
+ * @param mask The modifier key mask from the original NSEvent
+ */
+void Emulator::modifier_keys_changed(unsigned mask)
+{
+    keyboard_handle_modifier_keys(mask);
+}
+
+/**
+ * Modifier keys reset
+ */
+void Emulator::modifier_keys_reset()
+{
+    keyboard_reset_modifiers(true);
+}
+    
+#endif /* Q_OS_MACOS */
+
 /**
  * Mouse has moved in absolute position (mousehack mode)
  * 
--- original/qt5/rpc-qt5.h	2020-05-06 20:19:23.000000000 +0100
+++ src/qt5/rpc-qt5.h	2020-05-07 21:14:11.000000000 +0100
@@ -49,6 +49,11 @@
 	void key_press_signal(unsigned scan_code);
 
 	void key_release_signal(unsigned scan_code);
+    
+#if defined(Q_OS_MACOS)
+    void modifier_keys_changed_signal(unsigned mask);
+    void modifier_keys_reset_signal();
+#endif /* Q_OS_MACOS */
 
 	void mouse_move_signal(int x, int y);
 	void mouse_move_relative_signal(int dx, int dy);
@@ -78,6 +83,11 @@
 	void key_press(unsigned scan_code);
 
 	void key_release(unsigned scan_code);
+    
+#if defined(Q_OS_MACOS)
+    void modifier_keys_changed(unsigned mask);
+    void modifier_keys_reset();
+#endif /* Q_OS_MACOS */
 
 	void mouse_move(int x, int y);
 	void mouse_move_relative(int dx, int dy);
--- original/rpcemu.h	2020-05-06 20:19:23.000000000 +0100
+++ src/rpcemu.h	2020-05-07 21:14:11.000000000 +0100
@@ -72,7 +72,7 @@
 /* Note that networking is currently supported on Mac OS X with the Cocoa GUI
    version but not with the Allegro GUI. */
 #if defined __linux || defined __linux__ || defined WIN32 || defined _WIN32 || \
-    defined RPCEMU_COCOA_GUI
+    defined RPCEMU_COCOA_GUI || __APPLE__
 #define RPCEMU_NETWORKING
 #endif
 
@@ -169,6 +169,10 @@
 extern uint32_t inscount;
 extern int cyccount;
 
+#ifdef __APPLE__
+extern int rpcemu_set_datadir(const char *path);
+#endif
+
 /* These functions can optionally be overridden by a platform. If not
    needed to be overridden, there is a generic version in rpc-machdep.c */
 extern const char *rpcemu_get_datadir(void);
--- original/qt5/rpcemu.pro	2020-05-06 20:19:23.000000000 +0100
+++ src/qt5/rpcemu.pro	2020-05-14 17:33:58.000000000 +0100
@@ -6,6 +6,10 @@
 QT += core widgets gui multimedia
 INCLUDEPATH += ../
 
+macx {
+	INCLUDEPATH += ../macosx
+}
+
 # This ensures that using switch with enum requires every value to be handled
 QMAKE_CFLAGS += -Werror=switch
 QMAKE_CXXFLAGS += -Werror=switch
@@ -61,7 +65,7 @@
 		plt_sound.cpp
 
 # NAT Networking
-linux | win32 {
+linux | win32 | macx {
 	HEADERS +=	../network-nat.h
 	SOURCES += 	../network-nat.c
 
@@ -141,10 +145,38 @@
 			network_dialog.h
 }
 
-unix {
-	SOURCES +=	keyboard_x.c \
-			../hostfs-unix.c \
-			../rpc-linux.c
+!macx {
+	unix {
+		SOURCES +=	keyboard_x.c \
+				../hostfs-unix.c \
+				../rpc-linux.c
+	}
+}
+
+macx {
+	SOURCES +=	../network.c \
+			network_dialog.cpp \
+			keyboard_macosx.c \
+			../hostfs-macosx.c \
+			../rpc-macosx.c \
+			../macosx/hid-macosx.m \
+			../macosx/events-macosx.m \
+			../macosx/preferences-macosx.m \
+			../macosx/network-macosx.c \
+			../macosx/system-macosx.m \
+			choose_dialog.cpp
+
+	HEADERS +=	../network.h \
+			network_dialog.h \
+			keyboard_macosx.h \
+			../macosx/hid-macosx.h \
+			../macosx/events-macosx.h \
+			../macosx/preferences-macosx.h \
+			../macosx/system-macosx.h \
+			choose_dialog.h
+
+	ICON =		../macosx/rpcemu.icns
+			
 }
 
 # Place exes in top level directory
@@ -189,4 +221,13 @@
 	TARGET = $$join(TARGET, , , -debug)
 }
 
-LIBS +=
+!macx {
+	LIBS +=
+}
+
+macx {
+	LIBS += -framework coreFoundation -framework IOKit -framework Foundation -framework Carbon
+
+	QMAKE_INFO_PLIST = ../macosx/Info.plist
+}
+
--- original/qt5/settings.cpp	2020-05-06 20:19:23.000000000 +0100
+++ src/qt5/settings.cpp	2020-05-07 21:14:11.000000000 +0100
@@ -42,7 +42,7 @@
 
 	snprintf(filename, sizeof(filename), "%srpc.cfg", rpcemu_get_datadir());
 
-	QSettings settings("rpc.cfg", QSettings::IniFormat);
+	QSettings settings(filename, QSettings::IniFormat);
 
 	/* Copy the contents of the configfile to the log */
 	QStringList keys = settings.childKeys();
@@ -199,7 +199,7 @@
 
 	snprintf(filename, sizeof(filename), "%srpc.cfg", rpcemu_get_datadir());
 
-	QSettings settings("rpc.cfg", QSettings::IniFormat);
+	QSettings settings(filename, QSettings::IniFormat);
 
 	char s[256];
 
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/macosx/system-macosx.h	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,34 @@
+/*
+RPCEmu - An Acorn system emulator
+
+Copyright (C) 2017 Peter Howkins
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __SYSTEM_MACOSX_H__
+#define __SYSTEM_MACOSX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern unsigned int get_macosx_version(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	2020-05-14 17:35:51.000000000 +0100
+++ src/macosx/system-macosx.m	2020-05-07 21:05:20.000000000 +0100
@@ -0,0 +1,35 @@
+/*
+RPCEmu - An Acorn system emulator
+
+Copyright (C) 2017 Matthew Howkins
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <Cocoa/Cocoa.h>
+#include <Carbon/Carbon.h>
+
+unsigned int get_macosx_version(void)
+{
+    NSOperatingSystemVersion version;
+    
+    version = [[NSProcessInfo processInfo] operatingSystemVersion];
+    
+    return (version.majorVersion << 16) | (version.minorVersion << 8) | (version.patchVersion);
+}
+
