diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c
index 87665c6407..eb2f11234a 100644
--- a/hw/m68k/q800.c
+++ b/hw/m68k/q800.c
@@ -198,6 +198,8 @@ static const MemoryRegionOps macio_alias_ops = {
     .valid = {
         .min_access_size = 1,
         .max_access_size = 4,
+        /* VIA1 unaligned access for A/UX timer calibration */
+        .unaligned = true,
     },
 };
 
diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c
index 5d1adf5863..94b4f5cebc 100644
--- a/hw/misc/mac_via.c
+++ b/hw/misc/mac_via.c
@@ -995,20 +995,33 @@ static void via1_timer_calibration_hack(MOS6522Q800VIA1State *v1s, int addr,
 
 static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned size)
 {
-    MOS6522Q800VIA1State *s = MOS6522_Q800_VIA1(opaque);
-    MOS6522State *ms = MOS6522(s);
-    uint64_t ret;
+    MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque);
+    MOS6522State *ms = MOS6522(v1s);
+    int64_t now;
+    uint8_t ret;
+    uint64_t val = 0;
+    int i;
+    hwaddr addr1;
+
+    /* Handle unaligned read used by A/UX timer calibration code */
+    addr1 = (addr >> 9) & 0xf;
+    for (i = 0; i < size; i++, addr1++) {
+        ret = mos6522_read(ms, addr1, size);
+        switch (addr1) {
+        case VIA_REG_A:
+        case VIA_REG_ANH:
+            /* Quadra 800 Id */
+            ret = (ret & ~VIA1A_CPUID_MASK) | VIA1A_CPUID_Q800;
+            break;
+        }
+        val |= ret << ((size - i - 1) << 3);
+    }
 
-    addr = (addr >> 9) & 0xf;
-    ret = mos6522_read(ms, addr, size);
-    switch (addr) {
-    case VIA_REG_A:
-    case VIA_REG_ANH:
-        /* Quadra 800 Id */
-        ret = (ret & ~VIA1A_CPUID_MASK) | VIA1A_CPUID_Q800;
-        break;
+    if ((addr >> 9) != ((addr + size) >> 9)) {
+        trace_via1_unaligned_read(addr, val, size);
     }
-    return ret;
+
+    return val;
 }
 
 static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val,
@@ -1018,53 +1031,63 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val,
     MOS6522State *ms = MOS6522(v1s);
     int oldstate, state;
     int oldsr = ms->sr;
+    hwaddr addr1;
+    uint8_t v;
+    int i;
 
-    addr = (addr >> 9) & 0xf;
+    if ((addr >> 9) != ((addr + size) >> 9)) {
+        trace_via1_unaligned_write(addr, val, size);
+    }
 
-    via1_timer_calibration_hack(v1s, addr, val, size);
+    addr1 = (addr >> 9) & 0xf;
+    via1_timer_calibration_hack(v1s, addr1, val, size);
 
-    mos6522_write(ms, addr, val, size);
+    /* Handle unaligned write used by A/UX timer calibration code */
+    for (i = 0; i < size; i++, addr1++) {
+        v = val >> ((size - i - 1) << 3);
+        mos6522_write(ms, addr1, v, size);
 
-    switch (addr) {
-    case VIA_REG_B:
-        via1_rtc_update(v1s);
-        via1_adb_update(v1s);
-        via1_auxmode_update(v1s);
+        switch (addr1) {
+        case VIA_REG_B:
+            via1_rtc_update(v1s);
+            via1_adb_update(v1s);
+            via1_auxmode_update(v1s);
 
-        v1s->last_b = ms->b;
-        break;
+            v1s->last_b = ms->b;
+            break;
 
-    case VIA_REG_SR:
-        {
-            /*
-             * NetBSD assumes it can send its first ADB command after sending
-             * the ADB_BUSRESET command in ADB_STATE_NEW without changing the
-             * state back to ADB_STATE_IDLE first as detailed in the ADB
-             * protocol.
-             *
-             * Add a workaround to detect this condition at the start of ADB
-             * enumeration and send the next command written to SR after a
-             * ADB_BUSRESET onto the bus regardless, even if we don't detect a
-             * state transition to ADB_STATE_NEW.
-             *
-             * Note that in my tests the NetBSD state machine takes one ADB
-             * operation to recover which means the probe for an ADB device at
-             * address 1 always fails. However since the first device is at
-             * address 2 then this will work fine, without having to come up
-             * with a more complicated and invasive solution.
-             */
-            oldstate = (v1s->last_b & VIA1B_vADB_StateMask) >>
-                       VIA1B_vADB_StateShift;
-            state = (ms->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift;
-
-            if (oldstate == ADB_STATE_NEW && state == ADB_STATE_NEW &&
-                    (ms->acr & VIA1ACR_vShiftOut) &&
-                    oldsr == 0 /* ADB_BUSRESET */) {
-                trace_via1_adb_netbsd_enum_hack();
-                adb_via_send(v1s, state, ms->sr);
+        case VIA_REG_SR:
+            {
+                /*
+                 * NetBSD assumes it can send its first ADB command after
+                 * sending the ADB_BUSRESET command in ADB_STATE_NEW without
+                 * changing the state back to ADB_STATE_IDLE first as detailed
+                 * in the ADB protocol.
+                 *
+                 * Add a workaround to detect this condition at the start of
+                 * ADB enumeration and send the next command written to SR
+                 * after a ADB_BUSRESET onto the bus regardless, even if we
+                 * don't detect a state transition to ADB_STATE_NEW.
+                 *
+                 * Note that in my tests the NetBSD state machine takes one ADB
+                 * operation to recover which means the probe for an ADB device
+                 * at address 1 always fails. However since the first device is
+                 * at address 2 then this will work fine, without having to
+                 * come up with a more complicated and invasive solution.
+                 */
+                oldstate = (v1s->last_b & VIA1B_vADB_StateMask) >>
+                           VIA1B_vADB_StateShift;
+                state = (ms->b & VIA1B_vADB_StateMask) >> VIA1B_vADB_StateShift;
+
+                if (oldstate == ADB_STATE_NEW && state == ADB_STATE_NEW &&
+                        (ms->acr & VIA1ACR_vShiftOut) &&
+                        oldsr == 0 /* ADB_BUSRESET */) {
+                    trace_via1_adb_netbsd_enum_hack();
+                    adb_via_send(v1s, state, ms->sr);
+                }
             }
+            break;
         }
-        break;
     }
 }
 
@@ -1075,6 +1098,8 @@ static const MemoryRegionOps mos6522_q800_via1_ops = {
     .valid = {
         .min_access_size = 1,
         .max_access_size = 4,
+        /* VIA1 unaligned access for A/UX timer calibration */
+        .unaligned = true,
     },
 };
 
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 3d14e1db09..81bb16cea2 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -270,6 +270,8 @@ via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int size
 via1_adb_netbsd_enum_hack(void) "using NetBSD enum hack"
 via1_auxmode(int mode) "setting auxmode to %d"
 via1_timer_hack_state(int state) "setting timer_hack_state to %d"
+via1_unaligned_read(uint64_t addr, uint64_t value, unsigned size) "addr=0x%"PRIx64" value=0x%"PRIx64" size=%u"
+via1_unaligned_write(uint64_t addr, uint64_t value, unsigned size) "addr=0x%"PRIx64" value=0x%"PRIx64" size=%u"
 
 # grlib_ahb_apb_pnp.c
 grlib_ahb_pnp_read(uint64_t addr, unsigned size, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" size:%u data:0x%08x"
