src/gba/gba-gpio.c (view raw)
1#include "gba.h"
2
3#include "gba-gpio.h"
4
5#include <time.h>
6
7static void _readPins(struct GBACartridgeGPIO* gpio);
8static void _outputPins(struct GBACartridgeGPIO* gpio, unsigned pins);
9
10static void _rtcReadPins(struct GBACartridgeGPIO* gpio);
11static unsigned _rtcOutput(struct GBACartridgeGPIO* gpio);
12static void _rtcProcessByte(struct GBACartridgeGPIO* gpio);
13static void _rtcUpdateClock(struct GBACartridgeGPIO* gpio);
14static unsigned _rtcBCD(unsigned value);
15
16static const int RTC_BYTES[8] = {
17 0, // Force reset
18 0, // Empty
19 7, // Date/Time
20 0, // Force IRQ
21 1, // Control register
22 0, // Empty
23 3, // Time
24 0 // Empty
25};
26
27void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* base) {
28 gpio->gpioDevices = GPIO_NONE;
29 gpio->direction = GPIO_WRITE_ONLY;
30 gpio->gpioBase = base;
31 gpio->pinState = 0;
32 gpio->direction = 0;
33}
34
35void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t value) {
36 switch (address) {
37 case GPIO_REG_DATA:
38 gpio->pinState = value;
39 _readPins(gpio);
40 break;
41 case GPIO_REG_DIRECTION:
42 gpio->direction = value;
43 break;
44 case GPIO_REG_CONTROL:
45 gpio->readWrite = value;
46 break;
47 default:
48 GBALog(0, GBA_LOG_WARN, "Invalid GPIO address");
49 }
50
51 if (gpio->readWrite) {
52 uint16_t old = gpio->gpioBase[0];
53 old &= ~gpio->direction;
54 gpio->gpioBase[0] = old | (value & gpio->direction);
55 }
56}
57
58void GBAGPIOInitRTC(struct GBACartridgeGPIO* gpio) {
59 gpio->gpioDevices |= GPIO_RTC;
60 gpio->rtc.bytesRemaining = 0;
61
62 gpio->rtc.transferStep = 0;
63
64 gpio->rtc.bitsRead = 0;
65 gpio->rtc.bits = 0;
66 gpio->rtc.commandActive = 0;
67 gpio->rtc.command.packed = 0;
68 gpio->rtc.control.packed = 0x40;
69 memset(gpio->rtc.time, 0, sizeof(gpio->rtc.time));
70}
71
72void _readPins(struct GBACartridgeGPIO* gpio) {
73 if (gpio->gpioDevices & GPIO_RTC) {
74 _rtcReadPins(gpio);
75 }
76}
77
78void _outputPins(struct GBACartridgeGPIO* gpio, unsigned pins) {
79 if (gpio->readWrite) {
80 uint16_t old = gpio->gpioBase[0];
81 old &= gpio->direction;
82 gpio->gpioBase[0] = old | (pins & ~gpio->direction & 0xF);
83 }
84};
85
86void _rtcReadPins(struct GBACartridgeGPIO* gpio) {
87 // Transfer sequence:
88 // P: 0 | 1 | 2 | 3
89 // == Initiate
90 // > HI | - | LO | -
91 // > HI | - | HI | -
92 // == Transfer bit (x8)
93 // > LO | x | HI | -
94 // > HI | - | HI | -
95 // < ?? | x | ?? | -
96 // == Terminate
97 // > - | - | LO | -
98 switch (gpio->rtc.transferStep) {
99 case 0:
100 if ((gpio->pinState & 5) == 1) {
101 gpio->rtc.transferStep = 1;
102 }
103 break;
104 case 1:
105 if ((gpio->pinState & 5) == 5) {
106 gpio->rtc.transferStep = 2;
107 }
108 break;
109 case 2:
110 if (!gpio->p0) {
111 gpio->rtc.bits &= ~(1 << gpio->rtc.bitsRead);
112 gpio->rtc.bits |= gpio->p1 << gpio->rtc.bitsRead;
113 } else {
114 if (gpio->p2) {
115 // GPIO direction should always != reading
116 if (gpio->dir1) {
117 if (gpio->rtc.command.reading) {
118 GBALog(0, GBA_LOG_GAME_ERROR, "Attempting to write to RTC while in read mode");
119 }
120 ++gpio->rtc.bitsRead;
121 if (gpio->rtc.bitsRead == 8) {
122 _rtcProcessByte(gpio);
123 }
124 } else {
125 _outputPins(gpio, 5 | (_rtcOutput(gpio) << 1));
126 ++gpio->rtc.bitsRead;
127 if (gpio->rtc.bitsRead == 8) {
128 --gpio->rtc.bytesRemaining;
129 if (gpio->rtc.bytesRemaining <= 0) {
130 gpio->rtc.commandActive = 0;
131 gpio->rtc.command.reading = 0;
132 }
133 gpio->rtc.bitsRead = 0;
134 }
135 }
136 } else {
137 gpio->rtc.bitsRead = 0;
138 gpio->rtc.bytesRemaining = 0;
139 gpio->rtc.commandActive = 0;
140 gpio->rtc.command.reading = 0;
141 gpio->rtc.transferStep = 0;
142 }
143 }
144 break;
145 }
146}
147
148void _rtcProcessByte(struct GBACartridgeGPIO* gpio) {
149 --gpio->rtc.bytesRemaining;
150 if (!gpio->rtc.commandActive) {
151 union RTCCommandData command;
152 command.packed = gpio->rtc.bits;
153 if (command.magic == 0x06) {
154 gpio->rtc.command = command;
155
156 gpio->rtc.bytesRemaining = RTC_BYTES[gpio->rtc.command.command];
157 gpio->rtc.commandActive = gpio->rtc.bytesRemaining > 0;
158 switch (command.command) {
159 case RTC_RESET:
160 gpio->rtc.control.packed = 0;
161 break;
162 case RTC_DATETIME:
163 case RTC_TIME:
164 _rtcUpdateClock(gpio);
165 break;
166 case RTC_FORCE_IRQ:
167 case RTC_CONTROL:
168 break;
169 }
170 } else {
171 GBALog(0, GBA_LOG_WARN, "Invalid RTC command byte: %02X", gpio->rtc.bits);
172 }
173 } else {
174 switch (gpio->rtc.command.command) {
175 case RTC_CONTROL:
176 gpio->rtc.control.packed = gpio->rtc.bits;
177 break;
178 case RTC_FORCE_IRQ:
179 GBALog(0, GBA_LOG_STUB, "Unimplemented RTC command %u", gpio->rtc.command.command);
180 break;
181 case RTC_RESET:
182 case RTC_DATETIME:
183 case RTC_TIME:
184 break;
185 }
186 }
187
188 gpio->rtc.bits = 0;
189 gpio->rtc.bitsRead = 0;
190 if (!gpio->rtc.bytesRemaining) {
191 gpio->rtc.commandActive = 0;
192 gpio->rtc.command.reading = 0;
193 }
194}
195
196unsigned _rtcOutput(struct GBACartridgeGPIO* gpio) {
197 uint8_t outputByte = 0;
198 switch (gpio->rtc.command.command) {
199 case RTC_CONTROL:
200 outputByte = gpio->rtc.control.packed;
201 break;
202 case RTC_DATETIME:
203 case RTC_TIME:
204 outputByte = gpio->rtc.time[7 - gpio->rtc.bytesRemaining];
205 break;
206 case RTC_FORCE_IRQ:
207 case RTC_RESET:
208 break;
209 }
210 unsigned output = (outputByte >> gpio->rtc.bitsRead) & 1;
211 return output;
212}
213
214void _rtcUpdateClock(struct GBACartridgeGPIO* gpio) {
215 time_t t = time(0);
216 struct tm date;
217 localtime_r(&t, &date);
218 gpio->rtc.time[0] = _rtcBCD(date.tm_year - 100);
219 gpio->rtc.time[1] = _rtcBCD(date.tm_mon + 1);
220 gpio->rtc.time[2] = _rtcBCD(date.tm_mday);
221 gpio->rtc.time[3] = _rtcBCD(date.tm_wday);
222 if (gpio->rtc.control.hour24) {
223 gpio->rtc.time[4] = _rtcBCD(date.tm_hour);
224 } else {
225 gpio->rtc.time[4] = _rtcBCD(date.tm_hour % 12);
226 }
227 gpio->rtc.time[5] = _rtcBCD(date.tm_min);
228 gpio->rtc.time[6] = _rtcBCD(date.tm_sec);
229}
230
231unsigned _rtcBCD(unsigned value) {
232 int counter = value % 10;
233 value /= 10;
234 counter += (value % 10) << 4;
235 return counter;
236}