1 module gbaid.gba.keypad;
2 
3 import gbaid.util;
4 
5 import gbaid.gba.io;
6 import gbaid.gba.interrupt;
7 import gbaid.gba.display : CYCLES_PER_FRAME;
8 
9 public enum Button {
10     A = 0,
11     B = 1,
12     SELECT = 2,
13     START = 3,
14     RIGHT = 4,
15     LEFT = 5,
16     UP = 6,
17     DOWN = 7,
18     R = 8,
19     L = 9
20 }
21 
22 private enum STATE_BITS_CLEARED = 0b1111111111;
23 
24 public struct KeypadState {
25     private int bits = STATE_BITS_CLEARED;
26 
27     public void clear() {
28         bits = STATE_BITS_CLEARED;
29     }
30 
31     public bool isPressed(Button button) {
32         return !bits.checkBit(button);
33     }
34 
35     public void setPressed(Button button, bool pressed = true) {
36         bits.setBit(button, !pressed);
37     }
38 
39     public KeypadState opBinary(string op)(KeypadState that) if (op == "|") {
40         KeypadState combined;
41         combined.bits = this.bits & that.bits;
42         return combined;
43     }
44 
45     public KeypadState opOpAssign(string op)(KeypadState that) if (op == "|") {
46         bits &= that.bits;
47         return this;
48     }
49 }
50 
51 public class Keypad {
52     private InterruptHandler interruptHandler;
53     private ptrdiff_t cyclesUntilNextUpdate = 0;
54     private int stateBits = STATE_BITS_CLEARED;
55     private int control = 0;
56 
57     public this(IoRegisters* ioRegisters, InterruptHandler interruptHandler) {
58         this.interruptHandler = interruptHandler;
59 
60         ioRegisters.mapAddress(0x130, &stateBits, 0x3FF, 0, true, false);
61         ioRegisters.mapAddress(0x130, &control, 0xC3FF, 16);
62     }
63 
64     public void setState(KeypadState state) {
65         stateBits = state.bits;
66     }
67 
68     public size_t emulate(size_t cycles) {
69         cyclesUntilNextUpdate -= cycles;
70         if (cyclesUntilNextUpdate > 0) {
71             return 0;
72         }
73         cyclesUntilNextUpdate += CYCLES_PER_FRAME;
74         if (control.checkBit(14)) {
75             auto pressedBits = ~stateBits & 0x3FF;
76             auto requested = control & 0x3FF;
77             if (control.checkBit(15)) {
78                 if ((pressedBits & requested) == requested) {
79                     interruptHandler.requestInterrupt(InterruptSource.KEYPAD);
80                 }
81             } else if (pressedBits & requested) {
82                 interruptHandler.requestInterrupt(InterruptSource.KEYPAD);
83             }
84         }
85         return 0;
86     }
87 }