1 module gbaid.gba.sio;
2 
3 import gbaid.util;
4 
5 import gbaid.gba.io;
6 import gbaid.gba.interrupt;
7 
8 public enum CommunicationState {
9     IDLE,
10     WRITE_DONE,
11     READ_DONE,
12     FINALIZE_DONE
13 }
14 
15 public interface Communication {
16     public CommunicationState getState();
17     public void setReady(uint index, bool ready);
18     public bool allReady();
19 
20     public void init();
21 
22     public void readDone(uint index, bool done);
23     public bool readDone(uint index);
24 
25     public void finalizeDone(uint index, bool done);
26     public bool finalizeDone(uint index);
27 
28     public void deinit();
29 
30     public uint read(uint index);
31     public void write(uint index, uint data);
32 }
33 
34 private enum IoMode {
35     NORMAL_8BIT, NORMAL_32BIT, MULTIPLAYER, UART, JOYBUS, GENERAL_PURPOSE
36 }
37 
38 private struct MultiplayerControl {
39     private byte baudRate = 0;
40     private bool child = false;
41     private bool allReady = false;
42     private byte id = 0;
43     private bool error = false;
44     private bool active = false;
45     private bool interrupt = false;
46 }
47 
48 public class SerialPort {
49     private InterruptHandler interruptHandler;
50     private Communication _communication;
51     private MultiplayerControl control;
52     private int data1 = 0;
53     private int data2 = 0;
54     private short data3 = 0;
55     private bool stateSc = false, stateSd = false, stateSi = false, stateSo = false;
56     private bool dirSc = false, dirSd = false, dirSi = false, dirSo = false;
57     private bool interruptSi = false;
58     private byte mode1 = 0;
59     private byte mode2 = 0;
60     private uint index = 0;
61     private size_t waitCycles = 0;
62 
63     public this(IoRegisters* ioRegisters, InterruptHandler interruptHandler, uint index) {
64         this.interruptHandler = interruptHandler;
65 
66         this.index = index;
67         control.child = index != 0;
68 
69         _communication = new NullCommunication();
70 
71         ioRegisters.mapAddress(0x128, &control.baudRate, 0b11, 0);
72         ioRegisters.mapAddress(0x128, &control.child, 0b1, 2, true, false);
73         ioRegisters.mapAddress(0x128, &control.allReady, 0b1, 3, true, false);
74         ioRegisters.mapAddress(0x128, &control.id, 0b11, 4, true, false);
75         ioRegisters.mapAddress(0x128, &control.error, 0b1, 6, true, false);
76         ioRegisters.mapAddress(0x128, &control.active, 0b1, 7, true, index == 0).postWriteMonitor(&onPostWriteActive);
77         ioRegisters.mapAddress(0x128, &control.interrupt, 0b1, 14);
78 
79         ioRegisters.mapAddress(0x120, &data1, 0xFFFFFFFF, 0);
80         ioRegisters.mapAddress(0x124, &data2, 0xFFFFFFFF, 0);
81         ioRegisters.mapAddress(0x128, &data3, 0xFFFF, 16).postWriteMonitor(&onPostWriteOutData);
82 
83         ioRegisters.mapAddress(0x134, &stateSc, 0b1, 0);
84         ioRegisters.mapAddress(0x134, &stateSd, 0b1, 1);
85         ioRegisters.mapAddress(0x134, &stateSi, 0b1, 2);
86         ioRegisters.mapAddress(0x134, &stateSo, 0b1, 3);
87         ioRegisters.mapAddress(0x134, &dirSc, 0b1, 4);
88         ioRegisters.mapAddress(0x134, &dirSd, 0b1, 5);
89         ioRegisters.mapAddress(0x134, &dirSi, 0b1, 6);
90         ioRegisters.mapAddress(0x134, &dirSo, 0b1, 7);
91         ioRegisters.mapAddress(0x134, &interruptSi, 0b1, 8);
92 
93         ioRegisters.mapAddress(0x128, &mode1, 0b11, 12).postWriteMonitor(&onPostWriteMode);
94         ioRegisters.mapAddress(0x134, &mode2, 0b11, 14).postWriteMonitor(&onPostWriteMode);
95     }
96 
97     @property public void communication(Communication communication) {
98         _communication = communication;
99     }
100 
101     private void onPostWriteMode(int mask, int oldValue, int newValue) {
102         if (oldValue == newValue) {
103             return;
104         }
105         _communication.setReady(index, ioMode == IoMode.MULTIPLAYER);
106     }
107 
108     private void onPostWriteActive(int mask, int oldValue, int newValue) {
109         if (oldValue || !newValue) {
110             return;
111         }
112         if (ioMode == IoMode.MULTIPLAYER && index == 0) {
113             _communication.init();
114         }
115     }
116 
117     private void onPostWriteOutData(int mask, int oldValue, int newValue) {
118         _communication.write(index, data3);
119     }
120 
121     public size_t emulate(size_t cycles) {
122         waitCycles += cycles;
123 
124         if (ioMode != IoMode.MULTIPLAYER) {
125             return 0;
126         }
127 
128         control.allReady = _communication.allReady();
129 
130         final switch (_communication.getState()) with (CommunicationState) {
131             case IDLE:
132                 break;
133             case WRITE_DONE: {
134                 if (!_communication.readDone(index)) {
135                     data1.setBits(0, 15, _communication.read(0));
136                     data1.setBits(16, 31, _communication.read(1));
137                     data2.setBits(0, 15, _communication.read(2));
138                     data2.setBits(16, 31, _communication.read(3));
139 
140                     _communication.readDone(index, true);
141 
142                     waitCycles = 0;
143                     control.active = true;
144                 }
145                 break;
146             }
147             case READ_DONE: {
148                 if (!_communication.finalizeDone(index) && waitCycles >= 1024) {
149                     control.active = false;
150                     control.id = cast(byte) index;
151                     control.error = false;
152 
153                     if (control.interrupt) {
154                         interruptHandler.requestInterrupt(InterruptSource.SERIAL_COMMUNICATION);
155                     }
156 
157                     _communication.finalizeDone(index, true);
158                 }
159                 break;
160             }
161             case FINALIZE_DONE: {
162                 if (index == 0) {
163                     _communication.deinit();
164                 }
165                 break;
166             }
167         }
168 
169         return 0;
170     }
171 
172     @property private IoMode ioMode() {
173         final switch (mode2) {
174             case 0b00:
175             case 0b01: {
176                 final switch (mode1) {
177                     case 0b00:
178                         return IoMode.NORMAL_8BIT;
179                     case 0b01:
180                         return IoMode.NORMAL_32BIT;
181                     case 0b10:
182                         return IoMode.MULTIPLAYER;
183                     case 0b11:
184                         return IoMode.UART;
185                 }
186             }
187             case 0b10:
188                 return IoMode.GENERAL_PURPOSE;
189             case 0b11:
190                 return IoMode.JOYBUS;
191         }
192     }
193 }
194 
195 private class NullCommunication : Communication {
196     public override CommunicationState getState() {
197         return CommunicationState.IDLE;
198     }
199 
200     public override void setReady(uint index, bool ready) {
201     }
202 
203     public override bool allReady() {
204         return false;
205     }
206 
207     public override void init() {
208     }
209 
210     public override void readDone(uint index, bool done) {
211     }
212 
213     public override bool readDone(uint index) {
214         return true;
215     }
216 
217     public override void finalizeDone(uint index, bool done) {
218     }
219 
220     public override bool finalizeDone(uint index) {
221         return true;
222     }
223 
224     public void deinit() {
225     }
226 
227     public override uint read(uint index) {
228         return 0xFFFFFFFF;
229     }
230 
231     public override void write(uint index, uint data) {
232     }
233 }