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}