aboutsummaryrefslogtreecommitdiff
path: root/hw/misc/mac_via.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/misc/mac_via.c')
-rw-r--r--hw/misc/mac_via.c393
1 files changed, 321 insertions, 72 deletions
diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c
index d1abcd97b5..db6142b5f4 100644
--- a/hw/misc/mac_via.c
+++ b/hw/misc/mac_via.c
@@ -16,7 +16,7 @@
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
+#include "exec/address-spaces.h"
#include "migration/vmstate.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
@@ -30,6 +30,7 @@
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "sysemu/block-backend.h"
+#include "sysemu/rtc.h"
#include "trace.h"
#include "qemu/log.h"
@@ -114,6 +115,9 @@
#define VIA1A_CPUID1 0x04 /* CPU id bit 0 on RBV, others */
#define VIA1A_CPUID2 0x10 /* CPU id bit 0 on RBV, others */
#define VIA1A_CPUID3 0x40 /* CPU id bit 0 on RBV, others */
+#define VIA1A_CPUID_MASK (VIA1A_CPUID0 | VIA1A_CPUID1 | \
+ VIA1A_CPUID2 | VIA1A_CPUID3)
+#define VIA1A_CPUID_Q800 (VIA1A_CPUID0 | VIA1A_CPUID2)
/*
* Info on VIA1B is from Macintosh Family Hardware & MkLinux.
@@ -130,6 +134,10 @@
* On SE/30, vertical sync interrupt enable.
* 0=enabled. This vSync interrupt shows up
* as a slot $E interrupt.
+ * On Quadra 800 this bit toggles A/UX mode which
+ * configures the glue logic to deliver some IRQs
+ * at different levels compared to a classic
+ * Mac.
*/
#define VIA1B_vADBS2 0x20 /* ADB state input bit 1 (unused on IIfx) */
#define VIA1B_vADBS1 0x10 /* ADB state input bit 0 (unused on IIfx) */
@@ -242,7 +250,7 @@
#define vT2CL 0x1000 /* [VIA only] Timer two counter low. */
#define vT2CH 0x1200 /* [VIA only] Timer two counter high. */
#define vSR 0x1400 /* [VIA only] Shift register. */
-#define vACR 0x1600 /* [VIA only] Auxilary control register. */
+#define vACR 0x1600 /* [VIA only] Auxiliary control register. */
#define vPCR 0x1800 /* [VIA only] Peripheral control register. */
/*
* CHRP sez never ever to *write* this.
@@ -321,10 +329,11 @@ static void via1_sixty_hz(void *opaque)
{
MOS6522Q800VIA1State *v1s = opaque;
MOS6522State *s = MOS6522(v1s);
- MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s);
+ qemu_irq irq = qdev_get_gpio_in(DEVICE(s), VIA1_IRQ_60HZ_BIT);
- s->ifr |= VIA1_IRQ_60HZ;
- mdc->update_irq(s);
+ /* Negative edge trigger */
+ qemu_irq_lower(irq);
+ qemu_irq_raise(irq);
via1_sixty_hz_update(v1s);
}
@@ -333,49 +342,20 @@ static void via1_one_second(void *opaque)
{
MOS6522Q800VIA1State *v1s = opaque;
MOS6522State *s = MOS6522(v1s);
- MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s);
+ qemu_irq irq = qdev_get_gpio_in(DEVICE(s), VIA1_IRQ_ONE_SECOND_BIT);
- s->ifr |= VIA1_IRQ_ONE_SECOND;
- mdc->update_irq(s);
+ /* Negative edge trigger */
+ qemu_irq_lower(irq);
+ qemu_irq_raise(irq);
via1_one_second_update(v1s);
}
-static void via1_irq_request(void *opaque, int irq, int level)
-{
- MOS6522Q800VIA1State *v1s = opaque;
- MOS6522State *s = MOS6522(v1s);
- MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s);
-
- if (level) {
- s->ifr |= 1 << irq;
- } else {
- s->ifr &= ~(1 << irq);
- }
-
- mdc->update_irq(s);
-}
-
-static void via2_irq_request(void *opaque, int irq, int level)
-{
- MOS6522Q800VIA2State *v2s = opaque;
- MOS6522State *s = MOS6522(v2s);
- MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s);
-
- if (level) {
- s->ifr |= 1 << irq;
- } else {
- s->ifr &= ~(1 << irq);
- }
-
- mdc->update_irq(s);
-}
-
static void pram_update(MOS6522Q800VIA1State *v1s)
{
if (v1s->blk) {
- if (blk_pwrite(v1s->blk, 0, v1s->PRAM, sizeof(v1s->PRAM), 0) < 0) {
+ if (blk_pwrite(v1s->blk, 0, sizeof(v1s->PRAM), v1s->PRAM, 0) < 0) {
qemu_log("pram_update: cannot write to file\n");
}
}
@@ -386,10 +366,10 @@ static void pram_update(MOS6522Q800VIA1State *v1s)
*
* Command byte Register addressed by the command
*
- * z0000001 Seconds register 0 (lowest-order byte)
- * z0000101 Seconds register 1
- * z0001001 Seconds register 2
- * z0001101 Seconds register 3 (highest-order byte)
+ * z00x0001 Seconds register 0 (lowest-order byte)
+ * z00x0101 Seconds register 1
+ * z00x1001 Seconds register 2
+ * z00x1101 Seconds register 3 (highest-order byte)
* 00110001 Test register (write-only)
* 00110101 Write-Protect Register (write-only)
* z010aa01 RAM address 100aa ($10-$13) (first 20 bytes only)
@@ -397,6 +377,7 @@ static void pram_update(MOS6522Q800VIA1State *v1s)
* z0111aaa Extended memory designator and sector number
*
* For a read request, z=1, for a write z=0
+ * The letter x indicates don't care
* The letter a indicates bits whose value depend on what parameter
* RAM byte you want to address
*/
@@ -413,7 +394,7 @@ static int via1_rtc_compact_cmd(uint8_t value)
}
if ((value & 0x03) == 0x01) {
value >>= 2;
- if ((value & 0x1c) == 0) {
+ if ((value & 0x18) == 0) {
/* seconds registers */
return read | (REG_0 + (value & 0x03));
} else if ((value == 0x0c) && !read) {
@@ -423,7 +404,7 @@ static int via1_rtc_compact_cmd(uint8_t value)
} else if ((value & 0x1c) == 0x08) {
/* RAM address 0x10 to 0x13 */
return read | (REG_PRAM_ADDR + 0x10 + (value & 0x03));
- } else if ((value & 0x43) == 0x41) {
+ } else if ((value & 0x10) == 0x10) {
/* RAM address 0x00 to 0x0f */
return read | (REG_PRAM_ADDR + (value & 0x0f));
}
@@ -611,7 +592,7 @@ static void adb_via_poll(void *opaque)
/*
* For older Linux kernels that switch to IDLE mode after sending the
* ADB command, detect if there is an existing response and return that
- * as a a "fake" autopoll reply or bus timeout accordingly
+ * as a "fake" autopoll reply or bus timeout accordingly
*/
*data = v1s->adb_data_out[0];
olen = v1s->adb_data_in_size;
@@ -721,6 +702,12 @@ static void adb_via_send(MOS6522Q800VIA1State *v1s, int state, uint8_t data)
break;
case ADB_STATE_IDLE:
+ ms->b |= VIA1B_vADBInt;
+ adb_autopoll_unblock(adb_bus);
+
+ trace_via1_adb_send("IDLE", data,
+ (ms->b & VIA1B_vADBInt) ? "+" : "-");
+
return;
}
@@ -876,13 +863,208 @@ static void via1_adb_update(MOS6522Q800VIA1State *v1s)
}
}
+static void via1_auxmode_update(MOS6522Q800VIA1State *v1s)
+{
+ MOS6522State *s = MOS6522(v1s);
+ int oldirq, irq;
+
+ oldirq = (v1s->last_b & VIA1B_vMystery) ? 1 : 0;
+ irq = (s->b & VIA1B_vMystery) ? 1 : 0;
+
+ /* Check to see if the A/UX mode bit has changed */
+ if (irq != oldirq) {
+ trace_via1_auxmode(irq);
+ qemu_set_irq(v1s->auxmode_irq, irq);
+
+ /*
+ * Clear the ADB interrupt. MacOS can leave VIA1B_vADBInt asserted
+ * (low) if a poll sequence doesn't complete before NetBSD disables
+ * interrupts upon boot. Fortunately NetBSD switches to the so-called
+ * "A/UX" interrupt mode after it initialises, so we can use this as
+ * a convenient place to clear the ADB interrupt for now.
+ */
+ s->b |= VIA1B_vADBInt;
+ }
+}
+
+/*
+ * 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;
+ }
+
+ if (addr == VIA_REG_T2CL && val == 0xf0) {
+ /* VIA_REG_T2CL: low byte of counter (A/UX) */
+ v1s->timer_hack_state = 5;
+ }
+ break;
+ case 5:
+ if (addr == VIA_REG_T2CH && val == 0x3c) {
+ /*
+ * VIA_REG_T2CH: high byte of counter (A/UX). We are now extremely
+ * likely to be in the A/UX timer calibration routine, so move to
+ * the next state where we enable the calibration hack.
+ */
+ v1s->timer_hack_state = 6;
+ } else if ((addr == VIA_REG_IER && val == 0x20) ||
+ addr == VIA_REG_T2CH) {
+ /* We're doing something else with the timer, not calibration */
+ v1s->timer_hack_state = 0;
+ }
+ break;
+ case 6:
+ if ((addr == VIA_REG_IER && val == 0x20) || addr == VIA_REG_T2CH) {
+ /* End of A/UX timer calibration routine, or another write */
+ v1s->timer_hack_state = 7;
+ } else {
+ v1s->timer_hack_state = 0;
+ }
+ break;
+ case 7:
+ /*
+ * This is the normal post-calibration timer state once both the
+ * MacOS toolbox and A/UX have been calibrated, until we see a write
+ * to VIA_REG_PCR to suggest 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);
MOS6522State *ms = MOS6522(s);
+ uint64_t ret;
+ int64_t now;
addr = (addr >> 9) & 0xf;
- return mos6522_read(ms, addr, size);
+ 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;
+ case VIA_REG_T2CH:
+ if (s->timer_hack_state == 6) {
+ /*
+ * The A/UX timer calibration loop runs continuously until 2
+ * consecutive iterations differ by at least 0x492 timer ticks.
+ * Modern hosts execute the timer calibration loop so fast that
+ * this situation never occurs causing a hang on boot. Use a
+ * similar method to Shoebill which is to randomly add 0x500 to
+ * the T2 counter value during calibration to enable it to
+ * eventually succeed.
+ */
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ if (now & 1) {
+ ret += 0x5;
+ }
+ }
+ break;
+ }
+ return ret;
}
static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val,
@@ -890,17 +1072,55 @@ static void mos6522_q800_via1_write(void *opaque, hwaddr addr, uint64_t val,
{
MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(opaque);
MOS6522State *ms = MOS6522(v1s);
+ int oldstate, state;
+ int oldsr = ms->sr;
addr = (addr >> 9) & 0xf;
+
+ via1_timer_calibration_hack(v1s, addr, val, size);
+
mos6522_write(ms, addr, val, size);
switch (addr) {
case VIA_REG_B:
via1_rtc_update(v1s);
via1_adb_update(v1s);
+ via1_auxmode_update(v1s);
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);
+ }
+ }
+ break;
}
}
@@ -918,9 +1138,26 @@ static uint64_t mos6522_q800_via2_read(void *opaque, hwaddr addr, unsigned size)
{
MOS6522Q800VIA2State *s = MOS6522_Q800_VIA2(opaque);
MOS6522State *ms = MOS6522(s);
+ uint64_t val;
addr = (addr >> 9) & 0xf;
- return mos6522_read(ms, addr, size);
+ val = mos6522_read(ms, addr, size);
+
+ switch (addr) {
+ case VIA_REG_IFR:
+ /*
+ * On a Q800 an emulated VIA2 is integrated into the onboard logic. The
+ * expectation of most OSs is that the DRQ bit is live, rather than
+ * latched as it would be on a real VIA so do the same here.
+ *
+ * Note: DRQ is negative edge triggered
+ */
+ val &= ~VIA2_IRQ_SCSI_DATA;
+ val |= (~ms->last_irq_levels & VIA2_IRQ_SCSI_DATA);
+ break;
+ }
+
+ return val;
}
static void mos6522_q800_via2_write(void *opaque, hwaddr addr, uint64_t val,
@@ -966,14 +1203,16 @@ static int via1_post_load(void *opaque, int version_id)
}
/* VIA 1 */
-static void mos6522_q800_via1_reset(DeviceState *dev)
+static void mos6522_q800_via1_reset_hold(Object *obj)
{
- MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(dev);
+ MOS6522Q800VIA1State *v1s = MOS6522_Q800_VIA1(obj);
MOS6522State *ms = MOS6522(v1s);
MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms);
ADBBusState *adb_bus = &v1s->adb_bus;
- mdc->parent_reset(dev);
+ if (mdc->parent_phases.hold) {
+ mdc->parent_phases.hold(obj);
+ }
ms->timers[0].frequency = VIA_TIMER_FREQ;
ms->timers[1].frequency = VIA_TIMER_FREQ;
@@ -984,6 +1223,9 @@ static void mos6522_q800_via1_reset(DeviceState *dev)
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)
@@ -1020,8 +1262,8 @@ static void mos6522_q800_via1_realize(DeviceState *dev, Error **errp)
return;
}
- len = blk_pread(v1s->blk, 0, v1s->PRAM, sizeof(v1s->PRAM));
- if (len != sizeof(v1s->PRAM)) {
+ ret = blk_pread(v1s->blk, 0, sizeof(v1s->PRAM), v1s->PRAM, 0);
+ if (ret < 0) {
error_setg(errp, "can't read PRAM contents");
return;
}
@@ -1038,10 +1280,11 @@ static void mos6522_q800_via1_init(Object *obj)
sysbus_init_mmio(sbd, &v1s->via_mem);
/* ADB */
- qbus_create_inplace((BusState *)&v1s->adb_bus, sizeof(v1s->adb_bus),
- TYPE_ADB_BUS, DEVICE(v1s), "adb.0");
+ qbus_init((BusState *)&v1s->adb_bus, sizeof(v1s->adb_bus),
+ TYPE_ADB_BUS, DEVICE(v1s), "adb.0");
- qdev_init_gpio_in(DEVICE(obj), via1_irq_request, VIA1_IRQ_NB);
+ /* A/UX mode */
+ qdev_init_gpio_out(DEVICE(obj), &v1s->auxmode_irq, 1);
}
static const VMStateDescription vmstate_q800_via1 = {
@@ -1049,7 +1292,7 @@ static const VMStateDescription vmstate_q800_via1 = {
.version_id = 0,
.minimum_version_id = 0,
.post_load = via1_post_load,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_STRUCT(parent_obj, MOS6522Q800VIA1State, 0, vmstate_mos6522,
MOS6522State),
VMSTATE_UINT8(last_b, MOS6522Q800VIA1State),
@@ -1075,6 +1318,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()
}
};
@@ -1087,9 +1332,12 @@ static Property mos6522_q800_via1_properties[] = {
static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ ResettableClass *rc = RESETTABLE_CLASS(oc);
+ MOS6522DeviceClass *mdc = MOS6522_CLASS(oc);
dc->realize = mos6522_q800_via1_realize;
- dc->reset = mos6522_q800_via1_reset;
+ resettable_class_set_parent_phases(rc, NULL, mos6522_q800_via1_reset_hold,
+ NULL, &mdc->parent_phases);
dc->vmsd = &vmstate_q800_via1;
device_class_set_props(dc, mos6522_q800_via1_properties);
}
@@ -1111,12 +1359,14 @@ static void mos6522_q800_via2_portB_write(MOS6522State *s)
}
}
-static void mos6522_q800_via2_reset(DeviceState *dev)
+static void mos6522_q800_via2_reset_hold(Object *obj)
{
- MOS6522State *ms = MOS6522(dev);
+ MOS6522State *ms = MOS6522(obj);
MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(ms);
- mdc->parent_reset(dev);
+ if (mdc->parent_phases.hold) {
+ mdc->parent_phases.hold(obj);
+ }
ms->timers[0].frequency = VIA_TIMER_FREQ;
ms->timers[1].frequency = VIA_TIMER_FREQ;
@@ -1127,22 +1377,21 @@ static void mos6522_q800_via2_reset(DeviceState *dev)
ms->a = 0x7f;
}
-static void via2_nubus_irq_request(void *opaque, int irq, int level)
+static void via2_nubus_irq_request(void *opaque, int n, int level)
{
MOS6522Q800VIA2State *v2s = opaque;
MOS6522State *s = MOS6522(v2s);
- MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s);
+ qemu_irq irq = qdev_get_gpio_in(DEVICE(s), VIA2_IRQ_NUBUS_BIT);
if (level) {
/* Port A nubus IRQ inputs are active LOW */
- s->a &= ~(1 << irq);
- s->ifr |= 1 << VIA2_IRQ_NUBUS_BIT;
+ s->a &= ~(1 << n);
} else {
- s->a |= (1 << irq);
- s->ifr &= ~(1 << VIA2_IRQ_NUBUS_BIT);
+ s->a |= (1 << n);
}
- mdc->update_irq(s);
+ /* Negative edge trigger */
+ qemu_set_irq(irq, !level);
}
static void mos6522_q800_via2_init(Object *obj)
@@ -1154,8 +1403,6 @@ static void mos6522_q800_via2_init(Object *obj)
"via2", VIA_SIZE);
sysbus_init_mmio(sbd, &v2s->via_mem);
- qdev_init_gpio_in(DEVICE(obj), via2_irq_request, VIA2_IRQ_NB);
-
qdev_init_gpio_in_named(DEVICE(obj), via2_nubus_irq_request, "nubus-irq",
VIA2_NUBUS_IRQ_NB);
}
@@ -1164,7 +1411,7 @@ static const VMStateDescription vmstate_q800_via2 = {
.name = "q800-via2",
.version_id = 0,
.minimum_version_id = 0,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_STRUCT(parent_obj, MOS6522Q800VIA2State, 0, vmstate_mos6522,
MOS6522State),
VMSTATE_END_OF_LIST()
@@ -1174,9 +1421,11 @@ static const VMStateDescription vmstate_q800_via2 = {
static void mos6522_q800_via2_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ ResettableClass *rc = RESETTABLE_CLASS(oc);
MOS6522DeviceClass *mdc = MOS6522_CLASS(oc);
- dc->reset = mos6522_q800_via2_reset;
+ resettable_class_set_parent_phases(rc, NULL, mos6522_q800_via2_reset_hold,
+ NULL, &mdc->parent_phases);
dc->vmsd = &vmstate_q800_via2;
mdc->portB_write = mos6522_q800_via2_portB_write;
}