[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v3 14/20] mac_via: work around underflow in TimeDBRA timing loop
From: |
Mark Cave-Ayland |
Subject: |
[PATCH v3 14/20] mac_via: work around underflow in TimeDBRA timing loop in SETUPTIMEK |
Date: |
Fri, 29 Sep 2023 18:59:11 +0100 |
The MacOS toolbox ROM calculates the number of branches that can be executed
per millisecond as part of its timer calibration. Since modern hosts are
considerably quicker than original hardware, the negative counter reaches zero
before the calibration completes leading to division by zero later in
CALCULATESLOD.
Instead of trying to fudge the timing loop (which won't work for
TimeDBRA/TimeSCCDB
anyhow), use the pattern of access to the VIA1 registers to detect when
SETUPTIMEK
has finished executing and write some well-known good timer values to TimeDBRA
and TimeSCCDB taken from real hardware with a suitable scaling factor.
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
---
hw/misc/mac_via.c | 115 ++++++++++++++++++++++++++++++++++++++
hw/misc/trace-events | 1 +
include/hw/misc/mac_via.h | 3 +
3 files changed, 119 insertions(+)
diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c
index e87a1b82d8..f2ccb12d64 100644
--- a/hw/misc/mac_via.c
+++ b/hw/misc/mac_via.c
@@ -16,6 +16,7 @@
*/
#include "qemu/osdep.h"
+#include "exec/address-spaces.h"
#include "migration/vmstate.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
@@ -871,6 +872,112 @@ static void via1_auxmode_update(MOS6522Q800VIA1State *v1s)
}
}
+/*
+ * Addresses and real values for TimeDBRA/TimeSCCB to allow timer calibration
+ * to succeed (NOTE: both values have been multiplied by 3 to cope with the
+ * speed of QEMU execution on a modern host
+ */
+#define MACOS_TIMEDBRA 0xd00
+#define MACOS_TIMESCCB 0xd02
+
+#define MACOS_TIMEDBRA_VALUE (0x2a00 * 3)
+#define MACOS_TIMESCCB_VALUE (0x079d * 3)
+
+static bool via1_is_toolbox_timer_calibrated(void)
+{
+ /*
+ * Indicate whether the MacOS toolbox has been calibrated by checking
+ * for the value of our magic constants
+ */
+ uint16_t timedbra = lduw_be_phys(&address_space_memory, MACOS_TIMEDBRA);
+ uint16_t timesccdb = lduw_be_phys(&address_space_memory, MACOS_TIMESCCB);
+
+ return (timedbra == MACOS_TIMEDBRA_VALUE &&
+ timesccdb == MACOS_TIMESCCB_VALUE);
+}
+
+static void via1_timer_calibration_hack(MOS6522Q800VIA1State *v1s, int addr,
+ uint64_t val, int size)
+{
+ /*
+ * Work around timer calibration to ensure we that we have non-zero and
+ * known good values for TIMEDRBA and TIMESCCDB.
+ *
+ * This works by attempting to detect the reset and calibration sequence
+ * of writes to VIA1
+ */
+ int old_timer_hack_state = v1s->timer_hack_state;
+
+ switch (v1s->timer_hack_state) {
+ case 0:
+ if (addr == VIA_REG_PCR && val == 0x22) {
+ /* VIA_REG_PCR: configure VIA1 edge triggering */
+ v1s->timer_hack_state = 1;
+ }
+ break;
+ case 1:
+ if (addr == VIA_REG_T2CL && val == 0xc) {
+ /* VIA_REG_T2CL: low byte of 1ms counter */
+ if (!via1_is_toolbox_timer_calibrated()) {
+ v1s->timer_hack_state = 2;
+ } else {
+ v1s->timer_hack_state = 0;
+ }
+ }
+ break;
+ case 2:
+ if (addr == VIA_REG_T2CH && val == 0x3) {
+ /*
+ * VIA_REG_T2CH: high byte of 1ms counter (very likely at the
+ * start of SETUPTIMEK)
+ */
+ if (!via1_is_toolbox_timer_calibrated()) {
+ v1s->timer_hack_state = 3;
+ } else {
+ v1s->timer_hack_state = 0;
+ }
+ }
+ break;
+ case 3:
+ if (addr == VIA_REG_IER && val == 0x20) {
+ /*
+ * VIA_REG_IER: update at end of SETUPTIMEK
+ *
+ * Timer calibration has finished: unfortunately the values in
+ * TIMEDBRA (0xd00) and TIMESCCDB (0xd02) are so far out they
+ * cause divide by zero errors.
+ *
+ * Update them with values obtained from a real Q800 but with
+ * a x3 scaling factor which seems to work well
+ */
+ stw_be_phys(&address_space_memory, MACOS_TIMEDBRA,
+ MACOS_TIMEDBRA_VALUE);
+ stw_be_phys(&address_space_memory, MACOS_TIMESCCB,
+ MACOS_TIMESCCB_VALUE);
+
+ v1s->timer_hack_state = 4;
+ }
+ break;
+ case 4:
+ /*
+ * This is the normal post-calibration timer state: we should
+ * generally remain here unless we detect the A/UX calibration
+ * loop, or a write to VIA_REG_PCR suggesting a reset
+ */
+ if (addr == VIA_REG_PCR && val == 0x22) {
+ /* Looks like there has been a reset? */
+ v1s->timer_hack_state = 1;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (old_timer_hack_state != v1s->timer_hack_state) {
+ trace_via1_timer_hack_state(v1s->timer_hack_state);
+ }
+}
+
static uint64_t mos6522_q800_via1_read(void *opaque, hwaddr addr, unsigned
size)
{
MOS6522Q800VIA1State *s = MOS6522_Q800_VIA1(opaque);
@@ -896,6 +1003,9 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr
addr, uint64_t val,
MOS6522State *ms = MOS6522(v1s);
addr = (addr >> 9) & 0xf;
+
+ via1_timer_calibration_hack(v1s, addr, val, size);
+
mos6522_write(ms, addr, val, size);
switch (addr) {
@@ -1008,6 +1118,9 @@ static void mos6522_q800_via1_reset_hold(Object *obj)
adb_set_autopoll_enabled(adb_bus, true);
v1s->cmd = REG_EMPTY;
v1s->alt = REG_EMPTY;
+
+ /* Timer calibration hack */
+ v1s->timer_hack_state = 0;
}
static void mos6522_q800_via1_realize(DeviceState *dev, Error **errp)
@@ -1100,6 +1213,8 @@ static const VMStateDescription vmstate_q800_via1 = {
VMSTATE_INT64(next_second, MOS6522Q800VIA1State),
VMSTATE_TIMER_PTR(sixty_hz_timer, MOS6522Q800VIA1State),
VMSTATE_INT64(next_sixty_hz, MOS6522Q800VIA1State),
+ /* Timer hack */
+ VMSTATE_INT32(timer_hack_state, MOS6522Q800VIA1State),
VMSTATE_END_OF_LIST()
}
};
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 29bc531d4d..5a998f5e4e 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -272,6 +272,7 @@ via1_adb_send(const char *state, uint8_t data, const char
*vadbint) "state %s da
via1_adb_receive(const char *state, uint8_t data, const char *vadbint, int
status, int index, int size) "state %s data=0x%02x vADBInt=%s status=0x%x
index=%d size=%d"
via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int
size) "data=0x%02x vADBInt=%s status=0x%x index=%d size=%d"
via1_auxmode(int mode) "setting auxmode to %d"
+via1_timer_hack_state(int state) "setting timer_hack_state to %d"
# 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"
diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h
index 422da43bf9..63cdcf7c69 100644
--- a/include/hw/misc/mac_via.h
+++ b/include/hw/misc/mac_via.h
@@ -74,6 +74,9 @@ struct MOS6522Q800VIA1State {
int64_t next_second;
QEMUTimer *sixty_hz_timer;
int64_t next_sixty_hz;
+
+ /* SETUPTIMEK hack */
+ int timer_hack_state;
};
--
2.39.2
- [PATCH v3 03/20] q800: add machine id register, (continued)
- [PATCH v3 03/20] q800: add machine id register, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 04/20] q800: implement additional machine id bits on VIA1 port A, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 05/20] q800: add IOSB subsystem, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 06/20] q800: allow accesses to RAM area even if less memory is available, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 08/20] asc: generate silence if FIFO empty but engine still running, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 07/20] audio: add Apple Sound Chip (ASC) emulation, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 11/20] swim: add trace events for IWM and ISM registers, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 09/20] q800: add Apple Sound Chip (ASC) audio to machine, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 10/20] q800: add easc bool machine class property to switch between ASC and EASC, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 12/20] swim: split into separate IWM and ISM register blocks, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 14/20] mac_via: work around underflow in TimeDBRA timing loop in SETUPTIMEK,
Mark Cave-Ayland <=
- [PATCH v3 13/20] swim: update IWM/ISM register block decoding, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 17/20] mac_via: always clear ADB interrupt when switching to A/UX mode, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 18/20] q800: add ESCC alias at 0xc000, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 19/20] q800: add alias for MacOS toolbox ROM at 0x40000000, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 15/20] mac_via: workaround NetBSD ADB bus enumeration issue, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 16/20] mac_via: implement ADB_STATE_IDLE state if shift register in input mode, Mark Cave-Ayland, 2023/09/29
- [PATCH v3 20/20] mac_via: extend timer calibration hack to work with A/UX, Mark Cave-Ayland, 2023/09/29