c87406ff |
1 | #include <stddef.h> |
6d75addf |
2 | #include <stdio.h> |
9a0a61d2 |
3 | #include "r3000a.h" |
4 | #include "cdrom.h" |
5 | #include "psxdma.h" |
6 | #include "mdec.h" |
7 | #include "psxevents.h" |
6d75addf |
8 | |
6d75addf |
9 | //#define evprintf printf |
10 | #define evprintf(...) |
11 | |
c87406ff |
12 | static psxRegisters *cp0TOpsxRegs(psxCP0Regs *cp0) |
6d75addf |
13 | { |
c87406ff |
14 | #ifndef LIGHTREC |
15 | return (void *)((char *)cp0 - offsetof(psxRegisters, CP0)); |
16 | #else |
17 | // lightrec has it's own cp0 |
18 | return &psxRegs; |
19 | #endif |
20 | } |
21 | |
22 | u32 schedule_timeslice(psxRegisters *regs) |
23 | { |
24 | u32 i, c = regs->cycle; |
25 | u32 irqs = regs->interrupt; |
6d75addf |
26 | s32 min, dif; |
27 | |
28 | min = PSXCLK; |
29 | for (i = 0; irqs != 0; i++, irqs >>= 1) { |
30 | if (!(irqs & 1)) |
31 | continue; |
92dc6b2f |
32 | dif = regs->event_cycles[i] - c; |
6d75addf |
33 | //evprintf(" ev %d\n", dif); |
34 | if (0 < dif && dif < min) |
35 | min = dif; |
36 | } |
c87406ff |
37 | regs->next_interupt = c + min; |
38 | return regs->next_interupt; |
6d75addf |
39 | } |
40 | |
9a0a61d2 |
41 | static void irqNoOp() { |
42 | } |
43 | |
6d75addf |
44 | typedef void (irq_func)(); |
45 | |
46 | static irq_func * const irq_funcs[] = { |
47 | [PSXINT_SIO] = sioInterrupt, |
48 | [PSXINT_CDR] = cdrInterrupt, |
49 | [PSXINT_CDREAD] = cdrPlayReadInterrupt, |
50 | [PSXINT_GPUDMA] = gpuInterrupt, |
51 | [PSXINT_MDECOUTDMA] = mdec1Interrupt, |
52 | [PSXINT_SPUDMA] = spuInterrupt, |
53 | [PSXINT_MDECINDMA] = mdec0Interrupt, |
54 | [PSXINT_GPUOTCDMA] = gpuotcInterrupt, |
55 | [PSXINT_CDRDMA] = cdrDmaInterrupt, |
9a0a61d2 |
56 | [PSXINT_NEWDRC_CHECK] = irqNoOp, |
6d75addf |
57 | [PSXINT_CDRLID] = cdrLidSeekInterrupt, |
11d23573 |
58 | [PSXINT_IRQ10] = irq10Interrupt, |
6d75addf |
59 | [PSXINT_SPU_UPDATE] = spuUpdate, |
c2eee46b |
60 | [PSXINT_SPU_IRQ] = spuDelayedIrq, |
6d75addf |
61 | [PSXINT_RCNT] = psxRcntUpdate, |
62 | }; |
63 | |
9a0a61d2 |
64 | void irq_test(psxCP0Regs *cp0) |
6d75addf |
65 | { |
c87406ff |
66 | psxRegisters *regs = cp0TOpsxRegs(cp0); |
67 | u32 cycle = regs->cycle; |
6d75addf |
68 | u32 irq, irq_bits; |
69 | |
c87406ff |
70 | for (irq = 0, irq_bits = regs->interrupt; irq_bits != 0; irq++, irq_bits >>= 1) { |
6d75addf |
71 | if (!(irq_bits & 1)) |
72 | continue; |
92dc6b2f |
73 | if ((s32)(cycle - regs->event_cycles[irq]) >= 0) { |
c87406ff |
74 | // note: irq_funcs() also modify regs->interrupt |
75 | regs->interrupt &= ~(1u << irq); |
6d75addf |
76 | irq_funcs[irq](); |
77 | } |
78 | } |
79 | |
0b1da491 |
80 | cp0->n.Cause &= ~0x400; |
81 | if (psxHu32(0x1070) & psxHu32(0x1074)) |
82 | cp0->n.Cause |= 0x400; |
e7172b26 |
83 | if (((cp0->n.Cause | 1) & cp0->n.SR & 0x401) == 0x401) |
0b1da491 |
84 | psxException(0, 0, cp0); |
6d75addf |
85 | } |
86 | |
87 | void gen_interupt(psxCP0Regs *cp0) |
88 | { |
c87406ff |
89 | psxRegisters *regs = cp0TOpsxRegs(cp0); |
6d75addf |
90 | |
c87406ff |
91 | evprintf(" +ge %08x, %u->%u (%d)\n", regs->pc, regs->cycle, |
92 | regs->next_interupt, regs->next_interupt - regs->cycle); |
6d75addf |
93 | |
c87406ff |
94 | irq_test(cp0); |
95 | schedule_timeslice(regs); |
6d75addf |
96 | |
c87406ff |
97 | evprintf(" -ge %08x, %u->%u (%d)\n", regs->pc, regs->cycle, |
98 | regs->next_interupt, regs->next_interupt - regs->cycle); |
6d75addf |
99 | } |
100 | |
9a0a61d2 |
101 | void events_restore(void) |
102 | { |
103 | int i; |
104 | for (i = 0; i < PSXINT_COUNT; i++) |
92dc6b2f |
105 | psxRegs.event_cycles[i] = psxRegs.intCycle[i].sCycle + psxRegs.intCycle[i].cycle; |
6d75addf |
106 | |
92dc6b2f |
107 | psxRegs.event_cycles[PSXINT_RCNT] = psxRegs.psxNextsCounter + psxRegs.psxNextCounter; |
9a0a61d2 |
108 | psxRegs.interrupt |= 1 << PSXINT_RCNT; |
109 | psxRegs.interrupt &= (1 << PSXINT_COUNT) - 1; |
110 | } |