all repos — mgba @ ecc7522cf66db6c5f9464d2e85f671a9fad6fd7b

mGBA Game Boy Advance Emulator

src/gba/ereader.c (view raw)

  1
  2/* Copyright (c) 2013-2020 Jeffrey Pfau
  3 *
  4 * This Source Code Form is subject to the terms of the Mozilla Public
  5 * License, v. 2.0. If a copy of the MPL was not distributed with this
  6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  7#include <mgba/internal/gba/hardware.h>
  8
  9#include <mgba/internal/arm/macros.h>
 10#include <mgba/internal/gba/gba.h>
 11#include <mgba-util/memory.h>
 12
 13#define EREADER_BLOCK_SIZE 40
 14
 15static void _eReaderReset(struct GBACartridgeHardware* hw);
 16static void _eReaderWriteControl0(struct GBACartridgeHardware* hw, uint8_t value);
 17static void _eReaderWriteControl1(struct GBACartridgeHardware* hw, uint8_t value);
 18static void _eReaderReadData(struct GBACartridgeHardware* hw);
 19static void _eReaderReedSolomon(const uint8_t* input, uint8_t* output);
 20
 21const int EREADER_NYBBLE_5BIT[16][5] = {
 22	{ 0, 0, 0, 0, 0 },
 23	{ 0, 0, 0, 0, 1 },
 24	{ 0, 0, 0, 1, 0 },
 25	{ 1, 0, 0, 1, 0 },
 26	{ 0, 0, 1, 0, 0 },
 27	{ 0, 0, 1, 0, 1 },
 28	{ 0, 0, 1, 1, 0 },
 29	{ 1, 0, 1, 1, 0 },
 30	{ 0, 1, 0, 0, 0 },
 31	{ 0, 1, 0, 0, 1 },
 32	{ 0, 1, 0, 1, 0 },
 33	{ 1, 0, 1, 0, 0 },
 34	{ 0, 1, 1, 0, 0 },
 35	{ 0, 1, 1, 0, 1 },
 36	{ 1, 0, 0, 0, 1 },
 37	{ 1, 0, 0, 0, 0 }
 38};
 39
 40const uint8_t EREADER_CALIBRATION_TEMPLATE[] = {
 41	0x43, 0x61, 0x72, 0x64, 0x2d, 0x45, 0x20, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x32, 0x30,
 42	0x30, 0x31, 0x00, 0x00, 0xcf, 0x72, 0x2f, 0x37, 0x3a, 0x3a, 0x3a, 0x38, 0x33, 0x30, 0x30, 0x37,
 43	0x3a, 0x39, 0x37, 0x35, 0x33, 0x2f, 0x2f, 0x34, 0x36, 0x36, 0x37, 0x36, 0x34, 0x31, 0x2d, 0x30,
 44	0x32, 0x34, 0x35, 0x35, 0x34, 0x30, 0x2a, 0x2d, 0x2d, 0x2f, 0x31, 0x32, 0x31, 0x2f, 0x29, 0x2a,
 45	0x2c, 0x2b, 0x2c, 0x2e, 0x2e, 0x2d, 0x18, 0x2d, 0x8f, 0x03, 0x00, 0x00, 0xc0, 0xfd, 0x77, 0x00,
 46	0x00, 0x00, 0x01
 47};
 48
 49const uint16_t EREADER_ADDRESS_CODES[] = {
 50	1023,
 51	1174,
 52	2628,
 53	3373,
 54	4233,
 55	6112,
 56	6450,
 57	7771,
 58	8826,
 59	9491,
 60	11201,
 61	11432,
 62	12556,
 63	13925,
 64	14519,
 65	16350,
 66	16629,
 67	18332,
 68	18766,
 69	20007,
 70	21379,
 71	21738,
 72	23096,
 73	23889,
 74	24944,
 75	26137,
 76	26827,
 77	28578,
 78	29190,
 79	30063,
 80	31677,
 81	31956,
 82	33410,
 83	34283,
 84	35641,
 85	35920,
 86	37364,
 87	38557,
 88	38991,
 89	40742,
 90	41735,
 91	42094,
 92	43708,
 93	44501,
 94	45169,
 95	46872,
 96	47562,
 97	48803,
 98	49544,
 99	50913,
100	51251,
101	53082,
102	54014,
103	54679
104};
105
106static const uint8_t BLOCK_HEADER[2][0x18] = {
107	{ 0x00, 0x02, 0x00, 0x01, 0x40, 0x10, 0x00, 0x1c, 0x10, 0x6f, 0x40, 0xda, 0x39, 0x25, 0x8e, 0xe0, 0x7b, 0xb5, 0x98, 0xb6, 0x5b, 0xcf, 0x7f, 0x72 },
108	{ 0x00, 0x03, 0x00, 0x19, 0x40, 0x10, 0x00, 0x2c, 0x0e, 0x88, 0xed, 0x82, 0x50, 0x67, 0xfb, 0xd1, 0x43, 0xee, 0x03, 0xc6, 0xc6, 0x2b, 0x2c, 0x93 }
109};
110
111static const uint8_t RS_POW[] = {
112	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
113	0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb, 0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd,
114	0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31, 0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67,
115	0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc, 0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b,
116	0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4, 0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26,
117	0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21, 0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba,
118	0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0,
119	0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3, 0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a,
120	0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9, 0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44,
121	0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef, 0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85,
122	0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6, 0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf,
123	0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff, 0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58,
124	0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24,
125	0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8, 0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64,
126	0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2, 0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda,
127	0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77, 0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x00,
128};
129
130static const uint8_t RS_REV[] = {
131	0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a, 0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
132	0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1, 0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3,
133	0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83, 0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4,
134	0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35, 0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38,
135	0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70, 0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48,
136	0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24, 0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15,
137	0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f, 0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10,
138	0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7, 0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b,
139	0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08, 0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a,
140	0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91, 0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb,
141	0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2, 0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf,
142	0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52, 0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86,
143	0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc, 0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc,
144	0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8, 0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44,
145	0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1, 0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97,
146	0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5, 0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7,
147};
148
149static const uint8_t RS_GG[] = {
150	0x00, 0x4b, 0xeb, 0xd5, 0xef, 0x4c, 0x71, 0x00, 0xf4, 0x00, 0x71, 0x4c, 0xef, 0xd5, 0xeb, 0x4b
151};
152
153
154void GBAHardwareInitEReader(struct GBACartridgeHardware* hw) {
155	hw->devices |= HW_EREADER;
156	_eReaderReset(hw);
157
158	if (hw->p->memory.savedata.data[0xD000] == 0xFF) {
159		memset(&hw->p->memory.savedata.data[0xD000], 0, 0x1000);
160		memcpy(&hw->p->memory.savedata.data[0xD000], EREADER_CALIBRATION_TEMPLATE, sizeof(EREADER_CALIBRATION_TEMPLATE));
161	}
162	if (hw->p->memory.savedata.data[0xE000] == 0xFF) {
163		memset(&hw->p->memory.savedata.data[0xE000], 0, 0x1000);
164		memcpy(&hw->p->memory.savedata.data[0xE000], EREADER_CALIBRATION_TEMPLATE, sizeof(EREADER_CALIBRATION_TEMPLATE));
165	}
166}
167
168void GBAHardwareEReaderWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) {
169	address &= 0x700FF;
170	switch (address >> 17) {
171	case 0:
172		hw->eReaderRegisterUnk = value & 0xF;
173		break;
174	case 1:
175		hw->eReaderRegisterReset = (value & 0x8A) | 4;
176		if (value & 2) {
177			_eReaderReset(hw);
178		}
179		break;
180	case 2:
181		mLOG(GBA_HW, GAME_ERROR, "e-Reader write to read-only registers: %05X:%04X", address, value);
182		break;
183	default:
184		mLOG(GBA_HW, STUB, "Unimplemented e-Reader write: %05X:%04X", address, value);
185	}
186}
187
188void GBAHardwareEReaderWriteFlash(struct GBACartridgeHardware* hw, uint32_t address, uint8_t value) {
189	address &= 0xFFFF;
190	switch (address) {
191	case 0xFFB0:
192		_eReaderWriteControl0(hw, value);
193		break;
194	case 0xFFB1:
195		_eReaderWriteControl1(hw, value);
196		break;
197	case 0xFFB2:
198		hw->eReaderRegisterLed &= 0xFF00;
199		hw->eReaderRegisterLed |= value;
200		break;
201	case 0xFFB3:
202		hw->eReaderRegisterLed &= 0x00FF;
203		hw->eReaderRegisterLed |= value << 8;
204		break;
205	default:
206		mLOG(GBA_HW, STUB, "Unimplemented e-Reader write to flash: %04X:%02X", address, value);
207	}
208}
209
210uint16_t GBAHardwareEReaderRead(struct GBACartridgeHardware* hw, uint32_t address) {
211	address &= 0x700FF;
212	uint16_t value;
213	switch (address >> 17) {
214	case 0:
215		return hw->eReaderRegisterUnk;
216	case 1:
217		return hw->eReaderRegisterReset;
218	case 2:
219		if (address > 0x40088) {
220			return 0;
221		}
222		LOAD_16(value, address & 0xFE, hw->eReaderData);
223		return value;
224	}
225	mLOG(GBA_HW, STUB, "Unimplemented e-Reader read: %05X", address);
226	return 0;
227}
228
229uint8_t GBAHardwareEReaderReadFlash(struct GBACartridgeHardware* hw, uint32_t address) {
230	address &= 0xFFFF;
231	switch (address) {
232	case 0xFFB0:
233		return hw->eReaderRegisterControl0;
234	case 0xFFB1:
235		return hw->eReaderRegisterControl1;
236	default:
237		mLOG(GBA_HW, STUB, "Unimplemented e-Reader read from flash: %04X", address);
238		return 0;
239	}
240}
241
242static void _eReaderAnchor(uint8_t* origin) {
243	origin[EREADER_DOTCODE_STRIDE * 0 + 1] = 1;
244	origin[EREADER_DOTCODE_STRIDE * 0 + 2] = 1;
245	origin[EREADER_DOTCODE_STRIDE * 0 + 3] = 1;
246	origin[EREADER_DOTCODE_STRIDE * 1 + 0] = 1;
247	origin[EREADER_DOTCODE_STRIDE * 1 + 1] = 1;
248	origin[EREADER_DOTCODE_STRIDE * 1 + 2] = 1;
249	origin[EREADER_DOTCODE_STRIDE * 1 + 3] = 1;
250	origin[EREADER_DOTCODE_STRIDE * 1 + 4] = 1;
251	origin[EREADER_DOTCODE_STRIDE * 2 + 0] = 1;
252	origin[EREADER_DOTCODE_STRIDE * 2 + 1] = 1;
253	origin[EREADER_DOTCODE_STRIDE * 2 + 2] = 1;
254	origin[EREADER_DOTCODE_STRIDE * 2 + 3] = 1;
255	origin[EREADER_DOTCODE_STRIDE * 2 + 4] = 1;
256	origin[EREADER_DOTCODE_STRIDE * 3 + 0] = 1;
257	origin[EREADER_DOTCODE_STRIDE * 3 + 1] = 1;
258	origin[EREADER_DOTCODE_STRIDE * 3 + 2] = 1;
259	origin[EREADER_DOTCODE_STRIDE * 3 + 3] = 1;
260	origin[EREADER_DOTCODE_STRIDE * 3 + 4] = 1;
261	origin[EREADER_DOTCODE_STRIDE * 4 + 1] = 1;
262	origin[EREADER_DOTCODE_STRIDE * 4 + 2] = 1;
263	origin[EREADER_DOTCODE_STRIDE * 4 + 3] = 1;
264}
265
266static void _eReaderAlignment(uint8_t* origin) {
267	origin[8] = 1;
268	origin[10] = 1;
269	origin[12] = 1;
270	origin[14] = 1;
271	origin[16] = 1;
272	origin[18] = 1;
273	origin[21] = 1;
274	origin[23] = 1;
275	origin[25] = 1;
276	origin[27] = 1;
277	origin[29] = 1;
278	origin[31] = 1;
279}
280
281static void _eReaderAddress(uint8_t* origin, int a) {
282	origin[EREADER_DOTCODE_STRIDE * 7 + 2] = 1;
283	uint16_t addr = EREADER_ADDRESS_CODES[a];
284	int i;
285	for (i = 0; i < 16; ++i) {
286		origin[EREADER_DOTCODE_STRIDE * (16 + i) + 2] = (addr >> (15 - i)) & 1;
287	}
288}
289
290static void _eReaderReedSolomon(const uint8_t* input, uint8_t* output) {
291	uint8_t rsBuffer[64] = { 0 };
292	int i;
293	for (i = 0; i < 48; ++i) {
294		rsBuffer[63 - i] = input[i];
295	}
296	for (i = 0; i < 48; ++i) {
297		unsigned z = RS_REV[rsBuffer[63 - i] ^ rsBuffer[15]];
298		int j;
299		for (j = 15; j >= 0; --j) {
300			unsigned x = 0;
301			if (j != 0) {
302				x = rsBuffer[j - 1];
303			}
304			if (z != 0xFF) {
305				unsigned y = RS_GG[j];
306				if (y != 0xFF) {
307					y += z;
308					if (y >= 0xFF) {
309						y -= 0xFF;
310					}
311					x ^= RS_POW[y];
312				}
313			}
314			rsBuffer[j] = x;
315		}
316	}
317	for (i = 0; i < 16; ++i) {
318		output[15 - i] = ~rsBuffer[i];
319	}
320}
321
322void GBAHardwareEReaderScan(struct GBACartridgeHardware* hw, const void* data, size_t size) {
323	if (!hw->eReaderDots) {
324		hw->eReaderDots = anonymousMemoryMap(EREADER_DOTCODE_SIZE);
325	}
326	hw->eReaderX = -24;
327	memset(hw->eReaderDots, 0, EREADER_DOTCODE_SIZE);
328
329	uint8_t blockRS[44][0x10];
330	bool parsed = false;
331	bool bitmap = false;
332	size_t blocks;
333	int base;
334	switch (size) {
335	// Raw sizes
336	case 2112:
337		parsed = true;
338		// Fallthrough
339	case 2912:
340		base = 25;
341		blocks = 28;
342		break;
343	case 1344:
344		parsed = true;
345		// Fallthrough
346	case 1872:
347		base = 1;
348		blocks = 18;
349		break;
350	// Bitmap sizes
351	case 5456:
352		bitmap = true;
353		break;
354	default:
355		return;
356	}
357
358	size_t i;
359	if (bitmap) {
360		size_t x;
361		for (i = 0; i < 40; ++i) {
362			const uint8_t* line = &((const uint8_t*) data)[(i + 2) * 124];
363			uint8_t* origin = &hw->eReaderDots[EREADER_DOTCODE_STRIDE * i + 200];
364			for (x = 0; x < 124; ++x) {
365				uint8_t byte = line[x];
366				if (x == 123) {
367					byte &= 0xE0;
368				}
369				origin[x * 8 + 0] = (byte >> 7) & 1;
370				origin[x * 8 + 1] = (byte >> 6) & 1;
371				origin[x * 8 + 2] = (byte >> 5) & 1;
372				origin[x * 8 + 3] = (byte >> 4) & 1;
373				origin[x * 8 + 4] = (byte >> 3) & 1;
374				origin[x * 8 + 5] = (byte >> 2) & 1;
375				origin[x * 8 + 6] = (byte >> 1) & 1;
376				origin[x * 8 + 7] = byte & 1;
377			}
378		}		
379		return;
380	}
381
382	for (i = 0; i < blocks + 1; ++i) {
383		uint8_t* origin = &hw->eReaderDots[35 * i + 200];
384		_eReaderAnchor(&origin[EREADER_DOTCODE_STRIDE * 0]);
385		_eReaderAnchor(&origin[EREADER_DOTCODE_STRIDE * 35]);
386		_eReaderAddress(origin, base + i);
387	}
388	if (parsed) {
389		for (i = 0; i < size / 48; ++i) {
390			_eReaderReedSolomon(&((const uint8_t*) data)[i * 48], blockRS[i]);
391		}
392	}
393	size_t blockId = 0;
394	size_t byteOffset = 0;
395	for (i = 0; i < blocks; ++i) {
396		uint8_t block[1040];
397		uint8_t* origin = &hw->eReaderDots[35 * i + 200];
398		_eReaderAlignment(&origin[EREADER_DOTCODE_STRIDE * 2]);
399		_eReaderAlignment(&origin[EREADER_DOTCODE_STRIDE * 37]);
400
401		const uint8_t* blockData;
402		uint8_t parsedBlockData[104];
403		if (parsed) {
404			memset(parsedBlockData, 0, sizeof(*parsedBlockData));
405			const uint8_t* header = BLOCK_HEADER[size == 1344 ? 0 : 1];
406			parsedBlockData[0] = header[(2 * i) % 0x18];
407			parsedBlockData[1] = header[(2 * i) % 0x18 + 1];
408			int j;
409			for (j = 2; j < 104; ++j) {
410			    if (byteOffset >= 0x40) {
411			        break;
412			    }
413			    if (byteOffset >= 0x30) {
414			        parsedBlockData[j] = blockRS[blockId][byteOffset - 0x30];
415			    } else {
416			        parsedBlockData[j] = ((const uint8_t*) data)[blockId * 0x30 + byteOffset];
417			    }
418			    ++blockId;
419			    if (blockId * 0x30 >= size) {
420			        blockId = 0;
421			        ++byteOffset;
422			    }
423			}
424			blockData = parsedBlockData;
425		} else {
426			blockData = &((const uint8_t*) data)[i * 104];
427		}
428		int b;
429		for (b = 0; b < 104; ++b) {
430			const int* nybble5;
431			nybble5 = EREADER_NYBBLE_5BIT[blockData[b] >> 4];
432			block[b * 10 + 0] = nybble5[0];
433			block[b * 10 + 1] = nybble5[1];
434			block[b * 10 + 2] = nybble5[2];
435			block[b * 10 + 3] = nybble5[3];
436			block[b * 10 + 4] = nybble5[4];
437			nybble5 = EREADER_NYBBLE_5BIT[blockData[b] & 0xF];
438			block[b * 10 + 5] = nybble5[0];
439			block[b * 10 + 6] = nybble5[1];
440			block[b * 10 + 7] = nybble5[2];
441			block[b * 10 + 8] = nybble5[3];
442			block[b * 10 + 9] = nybble5[4];
443		}
444
445		b = 0;
446		int y;
447		for (y = 0; y < 3; ++y) {
448			memcpy(&origin[EREADER_DOTCODE_STRIDE * (4 + y) + 7], &block[b], 26);
449			b += 26;
450		}
451		for (y = 0; y < 26; ++y) {
452			memcpy(&origin[EREADER_DOTCODE_STRIDE * (7 + y) + 3], &block[b], 34);
453			b += 34;
454		}
455		for (y = 0; y < 3; ++y) {
456			memcpy(&origin[EREADER_DOTCODE_STRIDE * (33 + y) + 7], &block[b], 26);
457			b += 26;
458		}
459	}
460}
461
462void _eReaderReset(struct GBACartridgeHardware* hw) {
463	memset(hw->eReaderData, 0, sizeof(hw->eReaderData));
464	hw->eReaderRegisterUnk = 0;
465	hw->eReaderRegisterReset = 4;
466	hw->eReaderRegisterControl0 = 0;
467	hw->eReaderRegisterControl1 = 0x80;
468	hw->eReaderRegisterLed = 0;
469	hw->eReaderState = 0;
470	hw->eReaderActiveRegister = 0;
471}
472
473void _eReaderWriteControl0(struct GBACartridgeHardware* hw, uint8_t value) {
474	EReaderControl0 control = value & 0x7F;
475	EReaderControl0 oldControl = hw->eReaderRegisterControl0;
476	if (hw->eReaderState == EREADER_SERIAL_INACTIVE) {
477		if (EReaderControl0IsClock(oldControl) && EReaderControl0IsData(oldControl) && !EReaderControl0IsData(control)) {
478			hw->eReaderState = EREADER_SERIAL_STARTING;
479		}
480	} else if (EReaderControl0IsClock(oldControl) && !EReaderControl0IsData(oldControl) && EReaderControl0IsData(control)) {
481		hw->eReaderState = EREADER_SERIAL_INACTIVE;
482
483	} else if (hw->eReaderState == EREADER_SERIAL_STARTING) {
484		if (EReaderControl0IsClock(oldControl) && !EReaderControl0IsData(oldControl) && !EReaderControl0IsClock(control)) {
485			hw->eReaderState = EREADER_SERIAL_BIT_0;
486			hw->eReaderCommand = EREADER_COMMAND_IDLE;
487		}
488	} else if (EReaderControl0IsClock(oldControl) && !EReaderControl0IsClock(control)) {
489		mLOG(GBA_HW, DEBUG, "[e-Reader] Serial falling edge: %c %i", EReaderControl0IsDirection(control) ? '>' : '<', EReaderControl0GetData(control));
490		// TODO: Improve direction control
491		if (EReaderControl0IsDirection(control)) {
492			hw->eReaderByte |= EReaderControl0GetData(control) << (7 - (hw->eReaderState - EREADER_SERIAL_BIT_0));
493			++hw->eReaderState;
494			if (hw->eReaderState == EREADER_SERIAL_END_BIT) {
495				mLOG(GBA_HW, DEBUG, "[e-Reader] Wrote serial byte: %02x", hw->eReaderByte);
496				switch (hw->eReaderCommand) {
497				case EREADER_COMMAND_IDLE:
498					hw->eReaderCommand = hw->eReaderByte;
499					break;
500				case EREADER_COMMAND_SET_INDEX:
501					hw->eReaderActiveRegister = hw->eReaderByte;
502					hw->eReaderCommand = EREADER_COMMAND_WRITE_DATA;
503					break;
504				case EREADER_COMMAND_WRITE_DATA:
505					switch (hw->eReaderActiveRegister & 0x7F) {
506					case 0:
507					case 0x57:
508					case 0x58:
509					case 0x59:
510					case 0x5A:
511						// Read-only
512						mLOG(GBA_HW, GAME_ERROR, "Writing to read-only e-Reader serial register: %02X", hw->eReaderActiveRegister);
513						break;
514					default:
515						if ((hw->eReaderActiveRegister & 0x7F) > 0x5A) {
516							mLOG(GBA_HW, GAME_ERROR, "Writing to non-existent e-Reader serial register: %02X", hw->eReaderActiveRegister);
517							break;
518						}
519						hw->eReaderSerial[hw->eReaderActiveRegister & 0x7F] = hw->eReaderByte;
520						break;
521					}
522					++hw->eReaderActiveRegister;
523					break;
524				default:
525					mLOG(GBA_HW, ERROR, "Hit undefined state %02X in e-Reader state machine", hw->eReaderCommand);
526					break;
527				}
528				hw->eReaderState = EREADER_SERIAL_BIT_0;
529				hw->eReaderByte = 0;
530			}
531		} else if (hw->eReaderCommand == EREADER_COMMAND_READ_DATA) {
532			int bit = hw->eReaderSerial[hw->eReaderActiveRegister & 0x7F] >> (7 - (hw->eReaderState - EREADER_SERIAL_BIT_0));
533			control = EReaderControl0SetData(control, bit);
534			++hw->eReaderState;
535			if (hw->eReaderState == EREADER_SERIAL_END_BIT) {
536				++hw->eReaderActiveRegister;
537				mLOG(GBA_HW, DEBUG, "[e-Reader] Read serial byte: %02x", hw->eReaderSerial[hw->eReaderActiveRegister & 0x7F]);
538			}
539		}
540	} else if (!EReaderControl0IsDirection(control)) {
541		// Clear the error bit
542		control = EReaderControl0ClearData(control);
543	}
544	hw->eReaderRegisterControl0 = control;
545	if (!EReaderControl0IsScan(oldControl) && EReaderControl0IsScan(control)) {
546		if (hw->eReaderX > 1000) {
547			int i;
548			for (i = 0; i < EREADER_CARDS_MAX; ++i) {
549				if (!hw->eReaderCards[i].data) {
550					continue;
551				}
552				GBAHardwareEReaderScan(hw, hw->eReaderCards[i].data, hw->eReaderCards[i].size);
553				free(hw->eReaderCards[i].data);
554				hw->eReaderCards[i].data = NULL;
555				hw->eReaderCards[i].size = 0;
556				break;
557			}
558		}
559		hw->eReaderX = 0;
560		hw->eReaderY = 0;
561	} else if (EReaderControl0IsLedEnable(control) && EReaderControl0IsScan(control) && !EReaderControl1IsScanline(hw->eReaderRegisterControl1)) {
562		_eReaderReadData(hw);
563	}
564	mLOG(GBA_HW, STUB, "Unimplemented e-Reader Control0 write: %02X", value);
565}
566
567void _eReaderWriteControl1(struct GBACartridgeHardware* hw, uint8_t value) {
568	EReaderControl1 control = (value & 0x32) | 0x80;
569	hw->eReaderRegisterControl1 = control;
570	if (EReaderControl0IsScan(hw->eReaderRegisterControl0) && !EReaderControl1IsScanline(control)) {
571		++hw->eReaderY;
572		if (hw->eReaderY == (hw->eReaderSerial[0x15] | (hw->eReaderSerial[0x14] << 8))) {
573			hw->eReaderY = 0;
574			if (hw->eReaderX < 3400) {
575				hw->eReaderX += 210;
576			}
577		}
578		_eReaderReadData(hw);
579	}
580	mLOG(GBA_HW, STUB, "Unimplemented e-Reader Control1 write: %02X", value);
581}
582
583void _eReaderReadData(struct GBACartridgeHardware* hw) {
584	memset(hw->eReaderData, 0, EREADER_BLOCK_SIZE);
585	if (!hw->eReaderDots) {
586		int i;
587		for (i = 0; i < EREADER_CARDS_MAX; ++i) {
588			if (!hw->eReaderCards[i].data) {
589				continue;
590			}
591			GBAHardwareEReaderScan(hw, hw->eReaderCards[i].data, hw->eReaderCards[i].size);
592			free(hw->eReaderCards[i].data);
593			hw->eReaderCards[i].data = NULL;
594			hw->eReaderCards[i].size = 0;
595			break;
596		}
597	}
598	if (hw->eReaderDots) {
599		int y = hw->eReaderY - 10;
600		if (y < 0 || y >= 120) {
601			memset(hw->eReaderData, 0, EREADER_BLOCK_SIZE);
602		} else {
603			int i;
604			uint8_t* origin = &hw->eReaderDots[EREADER_DOTCODE_STRIDE * (y / 3) + 16];
605			for (i = 0; i < 20; ++i) {
606				uint16_t word = 0;
607				int x = hw->eReaderX + i * 16;
608				word |= origin[(x +  0) / 3] << 8;
609				word |= origin[(x +  1) / 3] << 9;
610				word |= origin[(x +  2) / 3] << 10;
611				word |= origin[(x +  3) / 3] << 11;
612				word |= origin[(x +  4) / 3] << 12;
613				word |= origin[(x +  5) / 3] << 13;
614				word |= origin[(x +  6) / 3] << 14;
615				word |= origin[(x +  7) / 3] << 15;
616				word |= origin[(x +  8) / 3];
617				word |= origin[(x +  9) / 3] << 1;
618				word |= origin[(x + 10) / 3] << 2;
619				word |= origin[(x + 11) / 3] << 3;
620				word |= origin[(x + 12) / 3] << 4;
621				word |= origin[(x + 13) / 3] << 5;
622				word |= origin[(x + 14) / 3] << 6;
623				word |= origin[(x + 15) / 3] << 7;
624				STORE_16(word, (19 - i) << 1, hw->eReaderData);
625			}
626		}
627	}
628	hw->eReaderRegisterControl1 = EReaderControl1FillScanline(hw->eReaderRegisterControl1);
629	if (EReaderControl0IsLedEnable(hw->eReaderRegisterControl0)) {
630		uint16_t led = hw->eReaderRegisterLed * 2;
631		if (led > 0x4000) {
632			led = 0x4000;
633		}
634		GBARaiseIRQ(hw->p, IRQ_GAMEPAK, -led);
635	}
636}
637
638void GBAEReaderQueueCard(struct GBA* gba, const void* data, size_t size) {	
639	int i;
640	for (i = 0; i < EREADER_CARDS_MAX; ++i) {
641		if (gba->memory.hw.eReaderCards[i].data) {
642			continue;
643		}
644		gba->memory.hw.eReaderCards[i].data = malloc(size);
645		memcpy(gba->memory.hw.eReaderCards[i].data, data, size);
646		gba->memory.hw.eReaderCards[i].size = size;
647		return;
648	}
649}