aboutsummaryrefslogtreecommitdiffstats
path: root/z80irq.c
blob: 0635d08c8662e0064ff4a80aa02a05c46d0d7385 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include "compiler.h"
#include "z80.h"
#include "z80irq.h"

atomic_uint irq_pending;      /* Quick way to poll */
unsigned int irq_mask = ~0U;
static struct z80_irq *irqs[MAX_IRQ];

void z80_register_irq(struct z80_irq *irq)
{
    unsigned int prio = irq->prio;

    assert(prio < MAX_IRQ);
    assert(!irqs[prio]);
    irqs[irq->prio] = irq;
}

/*
 * Z80 interrupt acknowledge cycle. Return the vector from the highest
 * priority pending interrupt, or -1 if spurious.
 */
int z80_intack(void)
{
    unsigned int prio;
    int vector = -1;
    unsigned int irqpend, irqmasked, thisirq;
    struct z80_irq *irq;

    do {
        /* Find the highest priority (lowest numeric) interrupt pending */
        irqpend = irq_pending;
        irqmasked = irqpend & irq_mask;

        if (unlikely(!irqmasked))
            return vector;      /* All interrupts went away... */

        prio = __builtin_ctz(irqmasked);
	thisirq = 1U << prio;
        if (unlikely(!(atomic_fetch_and(&irq_pending, ~thisirq) & thisirq)))
            continue;           /* This particular interrupt went away on us? */

        irq = irqs[prio];

        if (unlikely(irq->intack))
            vector = irq->intack(irq);
        else
            vector = irq->vector;
    } while (unlikely(vector < 0));

    /* Inside the handler for this interrupt */
    irq_mask &= ~(1U << prio);
    irq->handled = true;

    return vector;
}

/*
 * A RETI instruction was invoked, which is interpreted as an EOI.
 * In a real Z80 this is done by snooping the bus.
 * The priority chain is again used, so the EOI is directed to the
 * highest priority interrupt which is currently under service.
 */
void z80_eoi(void)
{
    unsigned int nirqmask, prio;
    struct z80_irq *irq;

    nirqmask = ~irq_mask;
    if (!nirqmask)
        return;                 /* No interrupts pending... */

    prio = __builtin_ctz(nirqmask);
    irq = irqs[prio];
    irq->handled = false;
    irq_mask |= 1U << prio;
    if (irq->eoi)
        irq->eoi(irq);
}