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}