451ab91e |
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | * Mupen64plus - pif.c * |
3 | * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * |
4 | * Copyright (C) 2002 Hacktarux * |
5 | * * |
6 | * This program is free software; you can redistribute it and/or modify * |
7 | * it under the terms of the GNU General Public License as published by * |
8 | * the Free Software Foundation; either version 2 of the License, or * |
9 | * (at your option) any later version. * |
10 | * * |
11 | * This program is distributed in the hope that it will be useful, * |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
14 | * GNU General Public License for more details. * |
15 | * * |
16 | * You should have received a copy of the GNU General Public License * |
17 | * along with this program; if not, write to the * |
18 | * Free Software Foundation, Inc., * |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
20 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
21 | |
22 | #include <stdio.h> |
23 | #include <string.h> |
24 | #include <stdlib.h> |
25 | #include <time.h> |
26 | |
27 | #include "memory.h" |
28 | #include "pif.h" |
29 | #include "n64_cic_nus_6105.h" |
30 | |
31 | #include "r4300/r4300.h" |
32 | #include "r4300/interupt.h" |
33 | |
34 | #include "api/m64p_types.h" |
35 | #include "api/callbacks.h" |
36 | #include "api/debugger.h" |
37 | #include "main/main.h" |
38 | #include "main/rom.h" |
39 | #include "main/util.h" |
40 | #include "plugin/plugin.h" |
41 | |
42 | static unsigned char eeprom[0x800]; |
43 | static unsigned char mempack[4][0x8000]; |
44 | |
45 | static char *get_eeprom_path(void) |
46 | { |
47 | return formatstr("%s%s.eep", get_savesrampath(), ROM_SETTINGS.goodname); |
48 | } |
49 | |
50 | static void eeprom_format(void) |
51 | { |
52 | memset(eeprom, 0, sizeof(eeprom)); |
53 | } |
54 | |
55 | static void eeprom_read_file(void) |
56 | { |
57 | char *filename = get_eeprom_path(); |
58 | |
59 | switch (read_from_file(filename, eeprom, sizeof(eeprom))) |
60 | { |
61 | case file_open_error: |
62 | DebugMessage(M64MSG_VERBOSE, "couldn't open eeprom file '%s' for reading", filename); |
63 | eeprom_format(); |
64 | break; |
65 | case file_read_error: |
66 | DebugMessage(M64MSG_WARNING, "fread() failed for 2kb eeprom file '%s'", filename); |
67 | break; |
68 | default: break; |
69 | } |
70 | |
71 | free(filename); |
72 | } |
73 | |
74 | static void eeprom_write_file(void) |
75 | { |
76 | char *filename = get_eeprom_path(); |
77 | |
78 | switch (write_to_file(filename, eeprom, sizeof(eeprom))) |
79 | { |
80 | case file_open_error: |
81 | DebugMessage(M64MSG_WARNING, "couldn't open eeprom file '%s' for writing", filename); |
82 | break; |
83 | case file_write_error: |
84 | DebugMessage(M64MSG_WARNING, "fwrite() failed for 2kb eeprom file '%s'", filename); |
85 | break; |
86 | default: break; |
87 | } |
88 | |
89 | free(filename); |
90 | } |
91 | |
92 | static char *get_mempack_path(void) |
93 | { |
94 | return formatstr("%s%s.mpk", get_savesrampath(), ROM_SETTINGS.goodname); |
95 | } |
96 | |
97 | static void mempack_format(void) |
98 | { |
99 | unsigned char init[] = |
100 | { |
101 | 0x81,0x01,0x02,0x03, 0x04,0x05,0x06,0x07, 0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f, |
102 | 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f, |
103 | 0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
104 | 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd, |
105 | 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
106 | 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
107 | 0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
108 | 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd, |
109 | 0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
110 | 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd, |
111 | 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
112 | 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
113 | 0xff,0xff,0xff,0xff, 0x05,0x1a,0x5f,0x13, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
114 | 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x01,0xff, 0x66,0x25,0x99,0xcd, |
115 | 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
116 | 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, |
117 | 0x00,0x71,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03, 0x00,0x03,0x00,0x03 |
118 | }; |
119 | int i,j; |
120 | for (i=0; i<4; i++) |
121 | { |
122 | for (j=0; j<0x8000; j+=2) |
123 | { |
124 | mempack[i][j] = 0; |
125 | mempack[i][j+1] = 0x03; |
126 | } |
127 | memcpy(mempack[i], init, 272); |
128 | } |
129 | } |
130 | |
131 | static void mempack_read_file(void) |
132 | { |
133 | char *filename = get_mempack_path(); |
134 | |
135 | switch (read_from_file(filename, mempack, sizeof(mempack))) |
136 | { |
137 | case file_open_error: |
138 | DebugMessage(M64MSG_VERBOSE, "couldn't open memory pack file '%s' for reading", filename); |
139 | mempack_format(); |
140 | break; |
141 | case file_read_error: |
142 | DebugMessage(M64MSG_WARNING, "fread() failed for 128kb mempack file '%s'", filename); |
143 | break; |
144 | default: break; |
145 | } |
146 | |
147 | free(filename); |
148 | } |
149 | |
150 | static void mempack_write_file(void) |
151 | { |
152 | char *filename = get_mempack_path(); |
153 | |
154 | switch (write_to_file(filename, mempack, sizeof(mempack))) |
155 | { |
156 | case file_open_error: |
157 | DebugMessage(M64MSG_WARNING, "couldn't open memory pack file '%s' for writing", filename); |
158 | break; |
159 | case file_write_error: |
160 | DebugMessage(M64MSG_WARNING, "fwrite() failed for 128kb mempack file '%s'", filename); |
161 | break; |
162 | default: break; |
163 | } |
164 | |
165 | free(filename); |
166 | } |
167 | |
168 | //#define DEBUG_PIF |
169 | #ifdef DEBUG_PIF |
170 | void print_pif(void) |
171 | { |
172 | int i; |
173 | for (i=0; i<(64/8); i++) |
174 | DebugMessage(M64MSG_INFO, "%x %x %x %x | %x %x %x %x", |
175 | PIF_RAMb[i*8+0], PIF_RAMb[i*8+1],PIF_RAMb[i*8+2], PIF_RAMb[i*8+3], |
176 | PIF_RAMb[i*8+4], PIF_RAMb[i*8+5],PIF_RAMb[i*8+6], PIF_RAMb[i*8+7]); |
177 | } |
178 | #endif |
179 | |
180 | static unsigned char byte2bcd(int n) |
181 | { |
182 | n %= 100; |
183 | return ((n / 10) << 4) | (n % 10); |
184 | } |
185 | |
186 | static void EepromCommand(unsigned char *Command) |
187 | { |
188 | time_t curtime_time; |
189 | struct tm *curtime; |
190 | |
191 | switch (Command[2]) |
192 | { |
193 | case 0: // check |
194 | #ifdef DEBUG_PIF |
195 | DebugMessage(M64MSG_INFO, "EepromCommand() check size"); |
196 | #endif |
197 | if (Command[1] != 3) |
198 | { |
199 | Command[1] |= 0x40; |
200 | if ((Command[1] & 3) > 0) |
201 | Command[3] = 0; |
202 | if ((Command[1] & 3) > 1) |
203 | Command[4] = (ROM_SETTINGS.savetype != EEPROM_16KB) ? 0x80 : 0xc0; |
204 | if ((Command[1] & 3) > 2) |
205 | Command[5] = 0; |
206 | } |
207 | else |
208 | { |
209 | Command[3] = 0; |
210 | Command[4] = (ROM_SETTINGS.savetype != EEPROM_16KB) ? 0x80 : 0xc0; |
211 | Command[5] = 0; |
212 | } |
213 | break; |
214 | case 4: // read |
215 | { |
216 | #ifdef DEBUG_PIF |
217 | DebugMessage(M64MSG_INFO, "EepromCommand() read 8-byte block %i", Command[3]); |
218 | #endif |
219 | eeprom_read_file(); |
220 | memcpy(&Command[4], eeprom + Command[3]*8, 8); |
221 | } |
222 | break; |
223 | case 5: // write |
224 | { |
225 | #ifdef DEBUG_PIF |
226 | DebugMessage(M64MSG_INFO, "EepromCommand() write 8-byte block %i", Command[3]); |
227 | #endif |
228 | eeprom_read_file(); |
229 | memcpy(eeprom + Command[3]*8, &Command[4], 8); |
230 | eeprom_write_file(); |
231 | } |
232 | break; |
233 | case 6: |
234 | #ifdef DEBUG_PIF |
235 | DebugMessage(M64MSG_INFO, "EepromCommand() RTC status query"); |
236 | #endif |
237 | // RTCstatus query |
238 | Command[3] = 0x00; |
239 | Command[4] = 0x10; |
240 | Command[5] = 0x00; |
241 | break; |
242 | case 7: |
243 | #ifdef DEBUG_PIF |
244 | DebugMessage(M64MSG_INFO, "EepromCommand() read RTC block %i", Command[3]); |
245 | #endif |
246 | // read RTC block |
247 | switch (Command[3]) { // block number |
248 | case 0: |
249 | Command[4] = 0x00; |
250 | Command[5] = 0x02; |
251 | Command[12] = 0x00; |
252 | break; |
253 | case 1: |
254 | DebugMessage(M64MSG_ERROR, "RTC command in EepromCommand(): read block %d", Command[2]); |
255 | break; |
256 | case 2: |
257 | time(&curtime_time); |
258 | curtime = localtime(&curtime_time); |
259 | Command[4] = byte2bcd(curtime->tm_sec); |
260 | Command[5] = byte2bcd(curtime->tm_min); |
261 | Command[6] = 0x80 + byte2bcd(curtime->tm_hour); |
262 | Command[7] = byte2bcd(curtime->tm_mday); |
263 | Command[8] = byte2bcd(curtime->tm_wday); |
264 | Command[9] = byte2bcd(curtime->tm_mon + 1); |
265 | Command[10] = byte2bcd(curtime->tm_year); |
266 | Command[11] = byte2bcd(curtime->tm_year / 100); |
267 | Command[12] = 0x00; // status |
268 | break; |
269 | } |
270 | break; |
271 | case 8: |
272 | // write RTC block |
273 | DebugMessage(M64MSG_ERROR, "RTC write in EepromCommand(): %d not yet implemented", Command[2]); |
274 | break; |
275 | default: |
276 | DebugMessage(M64MSG_ERROR, "unknown command in EepromCommand(): %x", Command[2]); |
277 | } |
278 | } |
279 | |
280 | static unsigned char mempack_crc(unsigned char *data) |
281 | { |
282 | int i; |
283 | unsigned char CRC = 0; |
284 | for (i=0; i<=0x20; i++) |
285 | { |
286 | int mask; |
287 | for (mask = 0x80; mask >= 1; mask >>= 1) |
288 | { |
289 | int xor_tap = (CRC & 0x80) ? 0x85 : 0x00; |
290 | CRC <<= 1; |
291 | if (i != 0x20 && (data[i] & mask)) CRC |= 1; |
292 | CRC ^= xor_tap; |
293 | } |
294 | } |
295 | return CRC; |
296 | } |
297 | |
298 | static void internal_ReadController(int Control, unsigned char *Command) |
299 | { |
300 | switch (Command[2]) |
301 | { |
302 | case 1: |
303 | #ifdef DEBUG_PIF |
304 | DebugMessage(M64MSG_INFO, "internal_ReadController() Channel %i Command 1 read buttons", Control); |
305 | #endif |
306 | if (Controls[Control].Present) |
307 | { |
308 | BUTTONS Keys; |
309 | input.getKeys(Control, &Keys); |
310 | *((unsigned int *)(Command + 3)) = Keys.Value; |
311 | #ifdef COMPARE_CORE |
312 | CoreCompareDataSync(4, Command+3); |
313 | #endif |
314 | } |
315 | break; |
316 | case 2: // read controller pack |
317 | #ifdef DEBUG_PIF |
318 | DebugMessage(M64MSG_INFO, "internal_ReadController() Channel %i Command 2 read controller pack (in Input plugin)", Control); |
319 | #endif |
320 | if (Controls[Control].Present) |
321 | { |
322 | if (Controls[Control].Plugin == PLUGIN_RAW) |
323 | if (input.readController) |
324 | input.readController(Control, Command); |
325 | } |
326 | break; |
327 | case 3: // write controller pack |
328 | #ifdef DEBUG_PIF |
329 | DebugMessage(M64MSG_INFO, "internal_ReadController() Channel %i Command 3 write controller pack (in Input plugin)", Control); |
330 | #endif |
331 | if (Controls[Control].Present) |
332 | { |
333 | if (Controls[Control].Plugin == PLUGIN_RAW) |
334 | if (input.readController) |
335 | input.readController(Control, Command); |
336 | } |
337 | break; |
338 | } |
339 | } |
340 | |
341 | static void internal_ControllerCommand(int Control, unsigned char *Command) |
342 | { |
343 | switch (Command[2]) |
344 | { |
345 | case 0x00: // read status |
346 | case 0xFF: // reset |
347 | if ((Command[1] & 0x80)) |
348 | break; |
349 | #ifdef DEBUG_PIF |
350 | DebugMessage(M64MSG_INFO, "internal_ControllerCommand() Channel %i Command %02x check pack present", Control, Command[2]); |
351 | #endif |
352 | if (Controls[Control].Present) |
353 | { |
354 | Command[3] = 0x05; |
355 | Command[4] = 0x00; |
356 | switch (Controls[Control].Plugin) |
357 | { |
358 | case PLUGIN_MEMPAK: |
359 | Command[5] = 1; |
360 | break; |
361 | case PLUGIN_RAW: |
362 | Command[5] = 1; |
363 | break; |
364 | default: |
365 | Command[5] = 0; |
366 | break; |
367 | } |
368 | } |
369 | else |
370 | Command[1] |= 0x80; |
371 | break; |
372 | case 0x01: |
373 | #ifdef DEBUG_PIF |
374 | DebugMessage(M64MSG_INFO, "internal_ControllerCommand() Channel %i Command 1 check controller present", Control); |
375 | #endif |
376 | if (!Controls[Control].Present) |
377 | Command[1] |= 0x80; |
378 | break; |
379 | case 0x02: // read controller pack |
380 | if (Controls[Control].Present) |
381 | { |
382 | switch (Controls[Control].Plugin) |
383 | { |
384 | case PLUGIN_MEMPAK: |
385 | { |
386 | int address = (Command[3] << 8) | Command[4]; |
387 | #ifdef DEBUG_PIF |
388 | DebugMessage(M64MSG_INFO, "internal_ControllerCommand() Channel %i Command 2 read mempack address %04x", Control, address); |
389 | #endif |
390 | if (address == 0x8001) |
391 | { |
392 | memset(&Command[5], 0, 0x20); |
393 | Command[0x25] = mempack_crc(&Command[5]); |
394 | } |
395 | else |
396 | { |
397 | address &= 0xFFE0; |
398 | if (address <= 0x7FE0) |
399 | { |
400 | mempack_read_file(); |
401 | memcpy(&Command[5], &mempack[Control][address], 0x20); |
402 | } |
403 | else |
404 | { |
405 | memset(&Command[5], 0, 0x20); |
406 | } |
407 | Command[0x25] = mempack_crc(&Command[5]); |
408 | } |
409 | } |
410 | break; |
411 | case PLUGIN_RAW: |
412 | #ifdef DEBUG_PIF |
413 | DebugMessage(M64MSG_INFO, "internal_ControllerCommand() Channel %i Command 2 controllerCommand (in Input plugin)", Control); |
414 | #endif |
415 | if (input.controllerCommand) |
416 | input.controllerCommand(Control, Command); |
417 | break; |
418 | default: |
419 | #ifdef DEBUG_PIF |
420 | DebugMessage(M64MSG_INFO, "internal_ControllerCommand() Channel %i Command 2 (no pack plugged in)", Control); |
421 | #endif |
422 | memset(&Command[5], 0, 0x20); |
423 | Command[0x25] = 0; |
424 | } |
425 | } |
426 | else |
427 | Command[1] |= 0x80; |
428 | break; |
429 | case 0x03: // write controller pack |
430 | if (Controls[Control].Present) |
431 | { |
432 | switch (Controls[Control].Plugin) |
433 | { |
434 | case PLUGIN_MEMPAK: |
435 | { |
436 | int address = (Command[3] << 8) | Command[4]; |
437 | #ifdef DEBUG_PIF |
438 | DebugMessage(M64MSG_INFO, "internal_ControllerCommand() Channel %i Command 3 write mempack address %04x", Control, address); |
439 | #endif |
440 | if (address == 0x8001) |
441 | Command[0x25] = mempack_crc(&Command[5]); |
442 | else |
443 | { |
444 | address &= 0xFFE0; |
445 | if (address <= 0x7FE0) |
446 | { |
447 | mempack_read_file(); |
448 | memcpy(&mempack[Control][address], &Command[5], 0x20); |
449 | mempack_write_file(); |
450 | } |
451 | Command[0x25] = mempack_crc(&Command[5]); |
452 | } |
453 | } |
454 | break; |
455 | case PLUGIN_RAW: |
456 | #ifdef DEBUG_PIF |
457 | DebugMessage(M64MSG_INFO, "internal_ControllerCommand() Channel %i Command 3 controllerCommand (in Input plugin)", Control); |
458 | #endif |
459 | if (input.controllerCommand) |
460 | input.controllerCommand(Control, Command); |
461 | break; |
462 | default: |
463 | #ifdef DEBUG_PIF |
464 | DebugMessage(M64MSG_INFO, "internal_ControllerCommand() Channel %i Command 3 (no pack plugged in)", Control); |
465 | #endif |
466 | Command[0x25] = mempack_crc(&Command[5]); |
467 | } |
468 | } |
469 | else |
470 | Command[1] |= 0x80; |
471 | break; |
472 | } |
473 | } |
474 | |
475 | void update_pif_write(void) |
476 | { |
477 | char challenge[30], response[30]; |
478 | int i=0, channel=0; |
479 | if (PIF_RAMb[0x3F] > 1) |
480 | { |
481 | switch (PIF_RAMb[0x3F]) |
482 | { |
483 | case 0x02: |
484 | #ifdef DEBUG_PIF |
485 | DebugMessage(M64MSG_INFO, "update_pif_write() pif_ram[0x3f] = 2 - CIC challenge"); |
486 | #endif |
487 | // format the 'challenge' message into 30 nibbles for X-Scale's CIC code |
488 | for (i = 0; i < 15; i++) |
489 | { |
490 | challenge[i*2] = (PIF_RAMb[48+i] >> 4) & 0x0f; |
491 | challenge[i*2+1] = PIF_RAMb[48+i] & 0x0f; |
492 | } |
493 | // calculate the proper response for the given challenge (X-Scale's algorithm) |
494 | n64_cic_nus_6105(challenge, response, CHL_LEN - 2); |
495 | // re-format the 'response' into a byte stream |
496 | for (i = 0; i < 15; i++) |
497 | { |
498 | PIF_RAMb[48+i] = (response[i*2] << 4) + response[i*2+1]; |
499 | } |
500 | // the last byte (2 nibbles) is always 0 |
501 | PIF_RAMb[63] = 0; |
502 | break; |
503 | case 0x08: |
504 | #ifdef DEBUG_PIF |
505 | DebugMessage(M64MSG_INFO, "update_pif_write() pif_ram[0x3f] = 8"); |
506 | #endif |
507 | PIF_RAMb[0x3F] = 0; |
508 | break; |
509 | default: |
510 | DebugMessage(M64MSG_ERROR, "error in update_pif_write(): %x", PIF_RAMb[0x3F]); |
511 | } |
512 | return; |
513 | } |
514 | while (i<0x40) |
515 | { |
516 | switch (PIF_RAMb[i]) |
517 | { |
518 | case 0x00: |
519 | channel++; |
520 | if (channel > 6) i=0x40; |
521 | break; |
522 | case 0xFF: |
523 | break; |
524 | default: |
525 | if (!(PIF_RAMb[i] & 0xC0)) |
526 | { |
527 | if (channel < 4) |
528 | { |
529 | if (Controls[channel].Present && |
530 | Controls[channel].RawData) |
531 | input.controllerCommand(channel, &PIF_RAMb[i]); |
532 | else |
533 | internal_ControllerCommand(channel, &PIF_RAMb[i]); |
534 | } |
535 | else if (channel == 4) |
536 | EepromCommand(&PIF_RAMb[i]); |
537 | else |
538 | DebugMessage(M64MSG_ERROR, "channel >= 4 in update_pif_write"); |
539 | i += PIF_RAMb[i] + (PIF_RAMb[(i+1)] & 0x3F) + 1; |
540 | channel++; |
541 | } |
542 | else |
543 | i=0x40; |
544 | } |
545 | i++; |
546 | } |
547 | //PIF_RAMb[0x3F] = 0; |
548 | input.controllerCommand(-1, NULL); |
549 | } |
550 | |
551 | void update_pif_read(void) |
552 | { |
553 | int i=0, channel=0; |
554 | while (i<0x40) |
555 | { |
556 | switch (PIF_RAMb[i]) |
557 | { |
558 | case 0x00: |
559 | channel++; |
560 | if (channel > 6) i=0x40; |
561 | break; |
562 | case 0xFE: |
563 | i = 0x40; |
564 | break; |
565 | case 0xFF: |
566 | break; |
567 | case 0xB4: |
568 | case 0x56: |
569 | case 0xB8: |
570 | break; |
571 | default: |
572 | if (!(PIF_RAMb[i] & 0xC0)) |
573 | { |
574 | if (channel < 4) |
575 | { |
576 | if (Controls[channel].Present && |
577 | Controls[channel].RawData) |
578 | input.readController(channel, &PIF_RAMb[i]); |
579 | else |
580 | internal_ReadController(channel, &PIF_RAMb[i]); |
581 | } |
582 | i += PIF_RAMb[i] + (PIF_RAMb[(i+1)] & 0x3F) + 1; |
583 | channel++; |
584 | } |
585 | else |
586 | i=0x40; |
587 | } |
588 | i++; |
589 | } |
590 | input.readController(-1, NULL); |
591 | } |
592 | |