all repos — mgba @ fbba3efb344df982f32cbf9cf3252b193263a9d3

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