diff options
Diffstat (limited to 'hw/misc')
-rw-r--r-- | hw/misc/mac_via.c | 15 | ||||
-rw-r--r-- | hw/misc/mos6522.c | 82 |
2 files changed, 89 insertions, 8 deletions
diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index d8b35e6..525e38c 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 c67123f..f9e6463 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; |