aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/m68k/q800.c9
-rw-r--r--hw/misc/mac_via.c15
-rw-r--r--hw/misc/mos6522.c82
-rw-r--r--include/hw/misc/mos6522.h15
4 files changed, 109 insertions, 12 deletions
diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c
index 55dfe5036f..66ca5c0df6 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 d8b35e6ca6..525e38ce93 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 c67123f864..f9e646350e 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 babea99e06..0bc22a8395 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