1 module gbaid.gba.interrupt;
2 
3 import gbaid.util;
4 
5 import gbaid.gba.io;
6 import gbaid.gba.cpu;
7 import gbaid.gba.halt;
8 
9 public class InterruptHandler {
10     private ARM7TDMI processor;
11     private HaltHandler haltHandler;
12     private bool masterEnable = false;
13     private int enable = 0;
14     private int request = 0;
15 
16     public this(IoRegisters* ioRegisters, ARM7TDMI processor, HaltHandler haltHandler) {
17         this.processor = processor;
18         this.haltHandler = haltHandler;
19 
20         ioRegisters.mapAddress(0x208, &masterEnable, 0b1, 0).postWriteMonitor(&onMasterEnablePostWrite);
21         ioRegisters.mapAddress(0x200, &enable, 0x3FFF, 0);
22         ioRegisters.mapAddress(0x200, &request, 0x3FFF, 16)
23                 .preWriteMonitor(&onInterruptAcknowledgePreWrite)
24                 .postWriteMonitor(&onInterruptAcknowledgePostWrite);
25     }
26 
27     public void requestInterrupt(int source) {
28         request.setBit(source, 1);
29         checkForIrq();
30     }
31 
32     private void onMasterEnablePostWrite(int mask, int oldMasterEnabled, int newMasterEnabled) {
33         // Trigger any IRQ requested when disabled
34         if (!oldMasterEnabled && newMasterEnabled) {
35             checkForIrq();
36         }
37     }
38 
39     private bool onInterruptAcknowledgePreWrite(int mask, ref int acknowledged) {
40         // Clear the acknowledged interrupts and replace the value by the updated request bits
41         acknowledged = request & ~acknowledged;
42         return true;
43     }
44 
45     private void onInterruptAcknowledgePostWrite(int mask, int oldAcknowledged, int newAcknowledged) {
46         // Trigger another IRQ if any is still not acknowledged
47         checkForIrq();
48     }
49 
50     private void checkForIrq() {
51         auto irq = masterEnable && (request & enable);
52         processor.irq(irq);
53         if (irq) {
54             haltHandler.irqTriggered();
55         }
56     }
57 }
58 
59 public static enum InterruptSource {
60     LCD_VBLANK = 0,
61     LCD_HBLANK = 1,
62     LCD_VCOUNTER_MATCH = 2,
63     TIMER_0_OVERFLOW = 3,
64     TIMER_1_OVERFLOW = 4,
65     TIMER_2_OVERFLOW = 5,
66     TIMER_3_OVERFLOW = 6,
67     SERIAL_COMMUNICATION = 7,
68     DMA_0 = 8,
69     DMA_1 = 9,
70     DMA_2 = 10,
71     DMA_3 = 11,
72     KEYPAD = 12,
73     GAMEPAK = 13
74 }