diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 55dfe5036f40..66ca5c0df6ef 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -533,10 +533,11 @@ static void q800_init(MachineState *machine) sysbus = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(via2_dev, - VIA2_IRQ_SCSI_BIT)); - sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(via2_dev, - VIA2_IRQ_SCSI_DATA_BIT)); + /* SCSI and SCSI data IRQs are negative edge triggered */ + sysbus_connect_irq(sysbus, 0, qemu_irq_invert(qdev_get_gpio_in(via2_dev, + VIA2_IRQ_SCSI_BIT))); + sysbus_connect_irq(sysbus, 1, qemu_irq_invert(qdev_get_gpio_in(via2_dev, + VIA2_IRQ_SCSI_DATA_BIT))); sysbus_mmio_map(sysbus, 0, ESP_BASE); sysbus_mmio_map(sysbus, 1, ESP_PDMA); diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index d8b35e6ca611..525e38ce9322 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -327,7 +327,9 @@ static void via1_sixty_hz(void *opaque) MOS6522State *s = MOS6522(v1s); qemu_irq irq = qdev_get_gpio_in(DEVICE(s), VIA1_IRQ_60HZ_BIT); - qemu_set_irq(irq, 1); + /* Negative edge trigger */ + qemu_irq_lower(irq); + qemu_irq_raise(irq); via1_sixty_hz_update(v1s); } @@ -338,7 +340,9 @@ static void via1_one_second(void *opaque) MOS6522State *s = MOS6522(v1s); qemu_irq irq = qdev_get_gpio_in(DEVICE(s), VIA1_IRQ_ONE_SECOND_BIT); - qemu_set_irq(irq, 1); + /* Negative edge trigger */ + qemu_irq_lower(irq); + qemu_irq_raise(irq); via1_one_second_update(v1s); } @@ -917,9 +921,11 @@ static uint64_t mos6522_q800_via2_read(void *opaque, hwaddr addr, unsigned size) * 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); + val |= (~ms->last_irq_levels & VIA2_IRQ_SCSI_DATA); break; } @@ -1146,7 +1152,8 @@ static void via2_nubus_irq_request(void *opaque, int n, int level) s->a |= (1 << n); } - qemu_set_irq(irq, level); + /* Negative edge trigger */ + qemu_set_irq(irq, !level); } static void mos6522_q800_via2_init(Object *obj) diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index c67123f86442..f9e646350e67 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -64,14 +64,62 @@ static void mos6522_update_irq(MOS6522State *s) static void mos6522_set_irq(void *opaque, int n, int level) { MOS6522State *s = MOS6522(opaque); + int last_level = !!(s->last_irq_levels & (1 << n)); + uint8_t last_ifr = s->ifr; + bool positive_edge = true; + int ctrl; + + /* + * SR_INT is managed by mos6522 instances and cleared upon SR + * read. It is only the external CA1/2 and CB1/2 lines that + * are edge-triggered and latched in IFR + */ + if (n != SR_INT_BIT && level == last_level) { + return; + } - if (level) { + /* Detect negative edge trigger */ + if (last_level == 1 && level == 0) { + positive_edge = false; + } + + switch (n) { + case CA2_INT_BIT: + ctrl = (s->pcr & CA2_CTRL_MASK) >> CA2_CTRL_SHIFT; + if ((positive_edge && (ctrl & C2_POS)) || + (!positive_edge && !(ctrl & C2_POS))) { + s->ifr |= 1 << n; + } + break; + case CA1_INT_BIT: + ctrl = (s->pcr & CA1_CTRL_MASK) >> CA1_CTRL_SHIFT; + if ((positive_edge && (ctrl & C1_POS)) || + (!positive_edge && !(ctrl & C1_POS))) { + s->ifr |= 1 << n; + } + break; + case SR_INT_BIT: s->ifr |= 1 << n; - } else { - s->ifr &= ~(1 << n); + break; + case CB2_INT_BIT: + ctrl = (s->pcr & CB2_CTRL_MASK) >> CB2_CTRL_SHIFT; + if ((positive_edge && (ctrl & C2_POS)) || + (!positive_edge && !(ctrl & C2_POS))) { + s->ifr |= 1 << n; + } + break; + case CB1_INT_BIT: + ctrl = (s->pcr & CB1_CTRL_MASK) >> CB1_CTRL_SHIFT; + if ((positive_edge && (ctrl & C1_POS)) || + (!positive_edge && !(ctrl & C1_POS))) { + s->ifr |= 1 << n; + } + break; } - mos6522_update_irq(s); + if (s->ifr != last_ifr) { + mos6522_update_irq(s); + } if (level) { s->last_irq_levels |= 1 << n; @@ -250,6 +298,7 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) { MOS6522State *s = opaque; uint32_t val; + int ctrl; int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (now >= s->timers[0].next_irq_time) { @@ -263,12 +312,24 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) switch (addr) { case VIA_REG_B: val = s->b; + ctrl = (s->pcr & CB2_CTRL_MASK) >> CB2_CTRL_SHIFT; + if (!(ctrl & C2_IND)) { + s->ifr &= ~CB2_INT; + } + s->ifr &= ~CB1_INT; + mos6522_update_irq(s); break; case VIA_REG_A: qemu_log_mask(LOG_UNIMP, "Read access to register A with handshake"); /* fall through */ case VIA_REG_ANH: val = s->a; + ctrl = (s->pcr & CA2_CTRL_MASK) >> CA2_CTRL_SHIFT; + if (!(ctrl & C2_IND)) { + s->ifr &= ~CA2_INT; + } + s->ifr &= ~CA1_INT; + mos6522_update_irq(s); break; case VIA_REG_DIRB: val = s->dirb; @@ -335,6 +396,7 @@ void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { MOS6522State *s = opaque; MOS6522DeviceClass *mdc = MOS6522_GET_CLASS(s); + int ctrl; trace_mos6522_write(addr, mos6522_reg_names[addr], val); @@ -342,6 +404,12 @@ void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) case VIA_REG_B: s->b = (s->b & ~s->dirb) | (val & s->dirb); mdc->portB_write(s); + ctrl = (s->pcr & CB2_CTRL_MASK) >> CB2_CTRL_SHIFT; + if (!(ctrl & C2_IND)) { + s->ifr &= ~CB2_INT; + } + s->ifr &= ~CB1_INT; + mos6522_update_irq(s); break; case VIA_REG_A: qemu_log_mask(LOG_UNIMP, "Write access to register A with handshake"); @@ -349,6 +417,12 @@ void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) case VIA_REG_ANH: s->a = (s->a & ~s->dira) | (val & s->dira); mdc->portA_write(s); + ctrl = (s->pcr & CA2_CTRL_MASK) >> CA2_CTRL_SHIFT; + if (!(ctrl & C2_IND)) { + s->ifr &= ~CA2_INT; + } + s->ifr &= ~CA1_INT; + mos6522_update_irq(s); break; case VIA_REG_DIRB: s->dirb = val; diff --git a/include/hw/misc/mos6522.h b/include/hw/misc/mos6522.h index babea99e0626..0bc22a839576 100644 --- a/include/hw/misc/mos6522.h +++ b/include/hw/misc/mos6522.h @@ -65,6 +65,21 @@ #define T1MODE 0xc0 /* Timer 1 mode */ #define T1MODE_CONT 0x40 /* continuous interrupts */ +/* Bits in PCR */ +#define CB2_CTRL_MASK 0xe0 +#define CB2_CTRL_SHIFT 5 +#define CB1_CTRL_MASK 0x10 +#define CB1_CTRL_SHIFT 4 +#define CA2_CTRL_MASK 0x0e +#define CA2_CTRL_SHIFT 1 +#define CA1_CTRL_MASK 0x1 +#define CA1_CTRL_SHIFT 0 + +#define C2_POS 0x2 +#define C2_IND 0x1 + +#define C1_POS 0x1 + /* VIA registers */ #define VIA_REG_B 0x00 #define VIA_REG_A 0x01