all repos — mgba @ fcaa0eb0662c66b293c26034261a9489da81e345

mGBA Game Boy Advance Emulator

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}