Merge branch 'mainline' into libretro
[pcsx_rearmed.git] / libpcsxcore / sio.c
1 /***************************************************************************
2  *   Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team              *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA.           *
18  ***************************************************************************/
19
20 /*
21 * SIO functions.
22 */
23
24 #include "sio.h"
25 #include <sys/stat.h>
26
27 // Status Flags
28 #define TX_RDY          0x0001
29 #define RX_RDY          0x0002
30 #define TX_EMPTY        0x0004
31 #define PARITY_ERR      0x0008
32 #define RX_OVERRUN      0x0010
33 #define FRAMING_ERR     0x0020
34 #define SYNC_DETECT     0x0040
35 #define DSR                     0x0080
36 #define CTS                     0x0100
37 #define IRQ                     0x0200
38
39 // Control Flags
40 #define TX_PERM         0x0001
41 #define DTR                     0x0002
42 #define RX_PERM         0x0004
43 #define BREAK           0x0008
44 #define RESET_ERR       0x0010
45 #define RTS                     0x0020
46 #define SIO_RESET       0x0040
47
48 // *** FOR WORKS ON PADS AND MEMORY CARDS *****
49
50 static unsigned char buf[256];
51 static unsigned char cardh1[4] = { 0xff, 0x08, 0x5a, 0x5d };
52 static unsigned char cardh2[4] = { 0xff, 0x08, 0x5a, 0x5d };
53
54 // Transfer Ready and the Buffer is Empty
55 // static unsigned short StatReg = 0x002b;
56 static unsigned short StatReg = TX_RDY | TX_EMPTY;
57 static unsigned short ModeReg;
58 static unsigned short CtrlReg;
59 static unsigned short BaudReg;
60
61 static unsigned int bufcount;
62 static unsigned int parp;
63 static unsigned int mcdst, rdwr;
64 static unsigned char adrH, adrL;
65 static unsigned int padst;
66
67 char Mcd1Data[MCD_SIZE], Mcd2Data[MCD_SIZE];
68 char McdDisable[2];
69
70 #define SIO_INT(eCycle) { \
71         if (!Config.Sio) { \
72                 psxRegs.interrupt |= (1 << PSXINT_SIO); \
73                 psxRegs.intCycle[PSXINT_SIO].cycle = eCycle; \
74                 psxRegs.intCycle[PSXINT_SIO].sCycle = psxRegs.cycle; \
75                 new_dyna_set_event(PSXINT_SIO, eCycle); \
76         } \
77 }
78
79 // clk cycle byte
80 // 4us * 8bits = (PSXCLK / 1000000) * 32; (linuzappz)
81 // TODO: add SioModePrescaler and BaudReg
82 #define SIO_CYCLES              535
83
84 void sioWrite8(unsigned char value) {
85 #ifdef PAD_LOG
86         PAD_LOG("sio write8 %x\n", value);
87 #endif
88         switch (padst) {
89                 case 1: SIO_INT(SIO_CYCLES);
90                         if ((value & 0x40) == 0x40) {
91                                 padst = 2; parp = 1;
92                                 if (!Config.UseNet) {
93                                         switch (CtrlReg & 0x2002) {
94                                                 case 0x0002:
95                                                         buf[parp] = PAD1_poll(value);
96                                                         break;
97                                                 case 0x2002:
98                                                         buf[parp] = PAD2_poll(value);
99                                                         break;
100                                         }
101                                 }/* else {
102 //                                      SysPrintf("%x: %x, %x, %x, %x\n", CtrlReg&0x2002, buf[2], buf[3], buf[4], buf[5]);
103                                 }*/
104
105                                 if (!(buf[parp] & 0x0f)) {
106                                         bufcount = 2 + 32;
107                                 } else {
108                                         bufcount = 2 + (buf[parp] & 0x0f) * 2;
109                                 }
110                                 if (buf[parp] == 0x41) {
111                                         switch (value) {
112                                                 case 0x43:
113                                                         buf[1] = 0x43;
114                                                         break;
115                                                 case 0x45:
116                                                         buf[1] = 0xf3;
117                                                         break;
118                                         }
119                                 }
120                                 // NegCon - Wipeout 3
121                                 if( buf[parp] == 0x23 ) {
122                                         switch (value) {
123                                                 // enter config mode
124                                                 case 0x43:
125                                                         buf[1] = 0x79;
126                                                         break;
127
128                                                 // get status
129                                                 case 0x45:
130                                                         buf[1] = 0xf3;
131                                                         break;
132                                         }
133                                 }
134                         }
135                         else padst = 0;
136                         return;
137                 case 2:
138                         parp++;
139 /*                      if (buf[1] == 0x45) {
140                                 buf[parp] = 0;
141                                 SIO_INT(SIO_CYCLES);
142                                 return;
143                         }*/
144                         if (!Config.UseNet) {
145                                 switch (CtrlReg & 0x2002) {
146                                         case 0x0002: buf[parp] = PAD1_poll(value); break;
147                                         case 0x2002: buf[parp] = PAD2_poll(value); break;
148                                 }
149                         }
150
151                         if (parp == bufcount) { padst = 0; return; }
152                         SIO_INT(SIO_CYCLES);
153                         return;
154         }
155
156         switch (mcdst) {
157                 case 1:
158                         SIO_INT(SIO_CYCLES);
159                         if (rdwr) { parp++; return; }
160                         parp = 1;
161                         switch (value) {
162                                 case 0x52: rdwr = 1; break;
163                                 case 0x57: rdwr = 2; break;
164                                 default: mcdst = 0;
165                         }
166                         return;
167                 case 2: // address H
168                         SIO_INT(SIO_CYCLES);
169                         adrH = value;
170                         *buf = 0;
171                         parp = 0;
172                         bufcount = 1;
173                         mcdst = 3;
174                         return;
175                 case 3: // address L
176                         SIO_INT(SIO_CYCLES);
177                         adrL = value;
178                         *buf = adrH;
179                         parp = 0;
180                         bufcount = 1;
181                         mcdst = 4;
182                         return;
183                 case 4:
184                         SIO_INT(SIO_CYCLES);
185                         parp = 0;
186                         switch (rdwr) {
187                                 case 1: // read
188                                         buf[0] = 0x5c;
189                                         buf[1] = 0x5d;
190                                         buf[2] = adrH;
191                                         buf[3] = adrL;
192                                         switch (CtrlReg & 0x2002) {
193                                                 case 0x0002:
194                                                         memcpy(&buf[4], Mcd1Data + (adrL | (adrH << 8)) * 128, 128);
195                                                         break;
196                                                 case 0x2002:
197                                                         memcpy(&buf[4], Mcd2Data + (adrL | (adrH << 8)) * 128, 128);
198                                                         break;
199                                         }
200                                         {
201                                         char xor = 0;
202                                         int i;
203                                         for (i = 2; i < 128 + 4; i++)
204                                                 xor ^= buf[i];
205                                         buf[132] = xor;
206                                         }
207                                         buf[133] = 0x47;
208                                         bufcount = 133;
209                                         break;
210                                 case 2: // write
211                                         buf[0] = adrL;
212                                         buf[1] = value;
213                                         buf[129] = 0x5c;
214                                         buf[130] = 0x5d;
215                                         buf[131] = 0x47;
216                                         bufcount = 131;
217                                         break;
218                         }
219                         mcdst = 5;
220                         return;
221                 case 5:
222                         parp++;
223                         if ((rdwr == 1 && parp == 132) ||
224                             (rdwr == 2 && parp == 129)) {
225                                 // clear "new card" flags
226                                 if (CtrlReg & 0x2000)
227                                         cardh2[1] &= ~8;
228                                 else
229                                         cardh1[1] &= ~8;
230                         }
231                         if (rdwr == 2) {
232                                 if (parp < 128) buf[parp + 1] = value;
233                         }
234                         SIO_INT(SIO_CYCLES);
235                         return;
236         }
237
238         switch (value) {
239                 case 0x01: // start pad
240                         StatReg |= RX_RDY;              // Transfer is Ready
241
242                         if (!Config.UseNet) {
243                                 switch (CtrlReg & 0x2002) {
244                                         case 0x0002: buf[0] = PAD1_startPoll(1); break;
245                                         case 0x2002: buf[0] = PAD2_startPoll(2); break;
246                                 }
247                         } else {
248                                 if ((CtrlReg & 0x2002) == 0x0002) {
249                                         int i, j;
250
251                                         PAD1_startPoll(1);
252                                         buf[0] = 0;
253                                         buf[1] = PAD1_poll(0x42);
254                                         if (!(buf[1] & 0x0f)) {
255                                                 bufcount = 32;
256                                         } else {
257                                                 bufcount = (buf[1] & 0x0f) * 2;
258                                         }
259                                         buf[2] = PAD1_poll(0);
260                                         i = 3;
261                                         j = bufcount;
262                                         while (j--) {
263                                                 buf[i++] = PAD1_poll(0);
264                                         }
265                                         bufcount+= 3;
266
267                                         if (NET_sendPadData(buf, bufcount) == -1)
268                                                 netError();
269
270                                         if (NET_recvPadData(buf, 1) == -1)
271                                                 netError();
272                                         if (NET_recvPadData(buf + 128, 2) == -1)
273                                                 netError();
274                                 } else {
275                                         memcpy(buf, buf + 128, 32);
276                                 }
277                         }
278
279                         bufcount = 2;
280                         parp = 0;
281                         padst = 1;
282                         SIO_INT(SIO_CYCLES);
283                         return;
284                 case 0x81: // start memcard
285                         if (CtrlReg & 0x2000)
286                         {
287                                 if (McdDisable[1])
288                                         goto no_device;
289                                 memcpy(buf, cardh2, 4);
290                         }
291                         else
292                         {
293                                 if (McdDisable[0])
294                                         goto no_device;
295                                 memcpy(buf, cardh1, 4);
296                         }
297                         StatReg |= RX_RDY;
298                         parp = 0;
299                         bufcount = 3;
300                         mcdst = 1;
301                         rdwr = 0;
302                         SIO_INT(SIO_CYCLES);
303                         return;
304                 default:
305                 no_device:
306                         StatReg |= RX_RDY;
307                         buf[0] = 0xff;
308                         parp = 0;
309                         bufcount = 0;
310                         return;
311         }
312 }
313
314 void sioWriteStat16(unsigned short value) {
315 }
316
317 void sioWriteMode16(unsigned short value) {
318         ModeReg = value;
319 }
320
321 void sioWriteCtrl16(unsigned short value) {
322         CtrlReg = value & ~RESET_ERR;
323         if (value & RESET_ERR) StatReg &= ~IRQ;
324         if ((CtrlReg & SIO_RESET) || !(CtrlReg & DTR)) {
325                 padst = 0; mcdst = 0; parp = 0;
326                 StatReg = TX_RDY | TX_EMPTY;
327                 psxRegs.interrupt &= ~(1 << PSXINT_SIO);
328         }
329 }
330
331 void sioWriteBaud16(unsigned short value) {
332         BaudReg = value;
333 }
334
335 unsigned char sioRead8() {
336         unsigned char ret = 0;
337
338         if ((StatReg & RX_RDY)/* && (CtrlReg & RX_PERM)*/) {
339 //              StatReg &= ~RX_OVERRUN;
340                 ret = buf[parp];
341                 if (parp == bufcount) {
342                         StatReg &= ~RX_RDY;             // Receive is not Ready now
343                         if (mcdst == 5) {
344                                 mcdst = 0;
345                                 if (rdwr == 2) {
346                                         switch (CtrlReg & 0x2002) {
347                                                 case 0x0002:
348                                                         memcpy(Mcd1Data + (adrL | (adrH << 8)) * 128, &buf[1], 128);
349                                                         SaveMcd(Config.Mcd1, Mcd1Data, (adrL | (adrH << 8)) * 128, 128);
350                                                         break;
351                                                 case 0x2002:
352                                                         memcpy(Mcd2Data + (adrL | (adrH << 8)) * 128, &buf[1], 128);
353                                                         SaveMcd(Config.Mcd2, Mcd2Data, (adrL | (adrH << 8)) * 128, 128);
354                                                         break;
355                                         }
356                                 }
357                         }
358                         if (padst == 2) padst = 0;
359                         if (mcdst == 1) {
360                                 mcdst = 2;
361                                 StatReg|= RX_RDY;
362                         }
363                 }
364         }
365
366 #ifdef PAD_LOG
367         PAD_LOG("sio read8 ;ret = %x\n", ret);
368 #endif
369         return ret;
370 }
371
372 unsigned short sioReadStat16() {
373         return StatReg;
374 }
375
376 unsigned short sioReadMode16() {
377         return ModeReg;
378 }
379
380 unsigned short sioReadCtrl16() {
381         return CtrlReg;
382 }
383
384 unsigned short sioReadBaud16() {
385         return BaudReg;
386 }
387
388 void netError() {
389         ClosePlugins();
390         SysMessage(_("Connection closed!\n"));
391
392         CdromId[0] = '\0';
393         CdromLabel[0] = '\0';
394
395         SysRunGui();
396 }
397
398 void sioInterrupt() {
399 #ifdef PAD_LOG
400         PAD_LOG("Sio Interrupt (CP0.Status = %x)\n", psxRegs.CP0.n.Status);
401 #endif
402 //      SysPrintf("Sio Interrupt\n");
403         if (!(StatReg & IRQ)) {
404                 StatReg |= IRQ;
405                 psxHu32ref(0x1070) |= SWAPu32(0x80);
406         }
407 }
408
409 void LoadMcd(int mcd, char *str) {
410         FILE *f;
411         char *data = NULL;
412
413         if (mcd != 1 && mcd != 2)
414                 return;
415
416         if (mcd == 1) {
417                 data = Mcd1Data;
418                 cardh1[1] |= 8; // mark as new
419         }
420         if (mcd == 2) {
421                 data = Mcd2Data;
422                 cardh2[1] |= 8;
423         }
424
425         McdDisable[mcd - 1] = 0;
426         if (str == NULL || strcmp(str, "none") == 0) {
427                 McdDisable[mcd - 1] = 1;
428                 return;
429         }
430         if (*str == 0)
431                 return;
432
433         f = fopen(str, "rb");
434         if (f == NULL) {
435                 SysPrintf(_("The memory card %s doesn't exist - creating it\n"), str);
436                 CreateMcd(str);
437                 f = fopen(str, "rb");
438                 if (f != NULL) {
439                         struct stat buf;
440
441                         if (stat(str, &buf) != -1) {
442                                 if (buf.st_size == MCD_SIZE + 64)
443                                         fseek(f, 64, SEEK_SET);
444                                 else if(buf.st_size == MCD_SIZE + 3904)
445                                         fseek(f, 3904, SEEK_SET);
446                         }
447                         fread(data, 1, MCD_SIZE, f);
448                         fclose(f);
449                 }
450                 else
451                         SysMessage(_("Memory card %s failed to load!\n"), str);
452         }
453         else {
454                 struct stat buf;
455                 SysPrintf(_("Loading memory card %s\n"), str);
456                 if (stat(str, &buf) != -1) {
457                         if (buf.st_size == MCD_SIZE + 64)
458                                 fseek(f, 64, SEEK_SET);
459                         else if(buf.st_size == MCD_SIZE + 3904)
460                                 fseek(f, 3904, SEEK_SET);
461                 }
462                 fread(data, 1, MCD_SIZE, f);
463                 fclose(f);
464         }
465 }
466
467 void LoadMcds(char *mcd1, char *mcd2) {
468         LoadMcd(1, mcd1);
469         LoadMcd(2, mcd2);
470 }
471
472 void SaveMcd(char *mcd, char *data, uint32_t adr, int size) {
473         FILE *f;
474
475         if (mcd == NULL || *mcd == 0 || strcmp(mcd, "none") == 0)
476                 return;
477
478         f = fopen(mcd, "r+b");
479         if (f != NULL) {
480                 struct stat buf;
481
482                 if (stat(mcd, &buf) != -1) {
483                         if (buf.st_size == MCD_SIZE + 64)
484                                 fseek(f, adr + 64, SEEK_SET);
485                         else if (buf.st_size == MCD_SIZE + 3904)
486                                 fseek(f, adr + 3904, SEEK_SET);
487                         else
488                                 fseek(f, adr, SEEK_SET);
489                 } else
490                         fseek(f, adr, SEEK_SET);
491
492                 fwrite(data + adr, 1, size, f);
493                 fclose(f);
494                 return;
495         }
496
497 #if 0
498         // try to create it again if we can't open it
499         f = fopen(mcd, "wb");
500         if (f != NULL) {
501                 fwrite(data, 1, MCD_SIZE, f);
502                 fclose(f);
503         }
504 #endif
505
506         ConvertMcd(mcd, data);
507 }
508
509 void CreateMcd(char *mcd) {
510         FILE *f;
511         struct stat buf;
512         int s = MCD_SIZE;
513         int i = 0, j;
514
515         f = fopen(mcd, "wb");
516         if (f == NULL)
517                 return;
518
519         if (stat(mcd, &buf) != -1) {
520                 if ((buf.st_size == MCD_SIZE + 3904) || strstr(mcd, ".gme")) {
521                         s = s + 3904;
522                         fputc('1', f);
523                         s--;
524                         fputc('2', f);
525                         s--;
526                         fputc('3', f);
527                         s--;
528                         fputc('-', f);
529                         s--;
530                         fputc('4', f);
531                         s--;
532                         fputc('5', f);
533                         s--;
534                         fputc('6', f);
535                         s--;
536                         fputc('-', f);
537                         s--;
538                         fputc('S', f);
539                         s--;
540                         fputc('T', f);
541                         s--;
542                         fputc('D', f);
543                         s--;
544                         for (i = 0; i < 7; i++) {
545                                 fputc(0, f);
546                                 s--;
547                         }
548                         fputc(1, f);
549                         s--;
550                         fputc(0, f);
551                         s--;
552                         fputc(1, f);
553                         s--;
554                         fputc('M', f);
555                         s--;
556                         fputc('Q', f);
557                         s--;
558                         for (i = 0; i < 14; i++) {
559                                 fputc(0xa0, f);
560                                 s--;
561                         }
562                         fputc(0, f);
563                         s--;
564                         fputc(0xff, f);
565                         while (s-- > (MCD_SIZE + 1))
566                                 fputc(0, f);
567                 } else if ((buf.st_size == MCD_SIZE + 64) || strstr(mcd, ".mem") || strstr(mcd, ".vgs")) {
568                         s = s + 64;
569                         fputc('V', f);
570                         s--;
571                         fputc('g', f);
572                         s--;
573                         fputc('s', f);
574                         s--;
575                         fputc('M', f);
576                         s--;
577                         for (i = 0; i < 3; i++) {
578                                 fputc(1, f);
579                                 s--;
580                                 fputc(0, f);
581                                 s--;
582                                 fputc(0, f);
583                                 s--;
584                                 fputc(0, f);
585                                 s--;
586                         }
587                         fputc(0, f);
588                         s--;
589                         fputc(2, f);
590                         while (s-- > (MCD_SIZE + 1))
591                                 fputc(0, f);
592                 }
593         }
594         fputc('M', f);
595         s--;
596         fputc('C', f);
597         s--;
598         while (s-- > (MCD_SIZE - 127))
599                 fputc(0, f);
600         fputc(0xe, f);
601         s--;
602
603         for (i = 0; i < 15; i++) { // 15 blocks
604                 fputc(0xa0, f);
605                 s--;
606                 fputc(0x00, f);
607                 s--;
608                 fputc(0x00, f);
609                 s--;
610                 fputc(0x00, f);
611                 s--;
612                 fputc(0x00, f);
613                 s--;
614                 fputc(0x00, f);
615                 s--;
616                 fputc(0x00, f);
617                 s--;
618                 fputc(0x00, f);
619                 s--;
620                 fputc(0xff, f);
621                 s--;
622                 fputc(0xff, f);
623                 s--;
624                 for (j = 0; j < 117; j++) {
625                         fputc(0x00, f);
626                         s--;
627                 }
628                 fputc(0xa0, f);
629                 s--;
630         }
631
632         for (i = 0; i < 20; i++) {
633                 fputc(0xff, f);
634                 s--;
635                 fputc(0xff, f);
636                 s--;
637                 fputc(0xff, f);
638                 s--;
639                 fputc(0xff, f);
640                 s--;
641                 fputc(0x00, f);
642                 s--;
643                 fputc(0x00, f);
644                 s--;
645                 fputc(0x00, f);
646                 s--;
647                 fputc(0x00, f);
648                 s--;
649                 fputc(0xff, f);
650                 s--;
651                 fputc(0xff, f);
652                 s--;
653                 for (j = 0; j < 118; j++) {
654                         fputc(0x00, f);
655                         s--;
656                 }
657         }
658
659         while ((s--) >= 0)
660                 fputc(0, f);
661
662         fclose(f);
663 }
664
665 void ConvertMcd(char *mcd, char *data) {
666         FILE *f;
667         int i = 0;
668         int s = MCD_SIZE;
669
670         if (strstr(mcd, ".gme")) {
671                 f = fopen(mcd, "wb");
672                 if (f != NULL) {
673                         fwrite(data - 3904, 1, MCD_SIZE + 3904, f);
674                         fclose(f);
675                 }
676                 f = fopen(mcd, "r+");
677                 s = s + 3904;
678                 fputc('1', f); s--;
679                 fputc('2', f); s--;
680                 fputc('3', f); s--;
681                 fputc('-', f); s--;
682                 fputc('4', f); s--;
683                 fputc('5', f); s--;
684                 fputc('6', f); s--;
685                 fputc('-', f); s--;
686                 fputc('S', f); s--;
687                 fputc('T', f); s--;
688                 fputc('D', f); s--;
689                 for (i = 0; i < 7; i++) {
690                         fputc(0, f); s--;
691                 }
692                 fputc(1, f); s--;
693                 fputc(0, f); s--;
694                 fputc(1, f); s--;
695                 fputc('M', f); s--;
696                 fputc('Q', f); s--;
697                 for(i=0;i<14;i++) {
698                         fputc(0xa0, f); s--;
699                 }
700                 fputc(0, f); s--;
701                 fputc(0xff, f);
702                 while (s-- > (MCD_SIZE+1)) fputc(0, f);
703                 fclose(f);
704         } else if(strstr(mcd, ".mem") || strstr(mcd,".vgs")) {
705                 f = fopen(mcd, "wb");
706                 if (f != NULL) {
707                         fwrite(data-64, 1, MCD_SIZE+64, f);
708                         fclose(f);
709                 }
710                 f = fopen(mcd, "r+");
711                 s = s + 64;
712                 fputc('V', f); s--;
713                 fputc('g', f); s--;
714                 fputc('s', f); s--;
715                 fputc('M', f); s--;
716                 for(i=0;i<3;i++) {
717                         fputc(1, f); s--;
718                         fputc(0, f); s--;
719                         fputc(0, f); s--;
720                         fputc(0, f); s--;
721                 }
722                 fputc(0, f); s--;
723                 fputc(2, f);
724                 while (s-- > (MCD_SIZE+1)) fputc(0, f);
725                 fclose(f);
726         } else {
727                 f = fopen(mcd, "wb");
728                 if (f != NULL) {
729                         fwrite(data, 1, MCD_SIZE, f);
730                         fclose(f);
731                 }
732         }
733 }
734
735 void GetMcdBlockInfo(int mcd, int block, McdBlock *Info) {
736         char *data = NULL, *ptr, *str, *sstr;
737         unsigned short clut[16];
738         unsigned short c;
739         int i, x;
740
741         memset(Info, 0, sizeof(McdBlock));
742
743         if (mcd != 1 && mcd != 2)
744                 return;
745
746         if (McdDisable[mcd - 1])
747                 return;
748
749         if (mcd == 1) data = Mcd1Data;
750         if (mcd == 2) data = Mcd2Data;
751
752         ptr = data + block * 8192 + 2;
753
754         Info->IconCount = *ptr & 0x3;
755
756         ptr += 2;
757
758         x = 0;
759
760         str = Info->Title;
761         sstr = Info->sTitle;
762
763         for (i = 0; i < 48; i++) {
764                 c = *(ptr) << 8;
765                 c |= *(ptr + 1);
766                 if (!c) break;
767
768                 // Convert ASCII characters to half-width
769                 if (c >= 0x8281 && c <= 0x829A)
770                         c = (c - 0x8281) + 'a';
771                 else if (c >= 0x824F && c <= 0x827A)
772                         c = (c - 0x824F) + '0';
773                 else if (c == 0x8140) c = ' ';
774                 else if (c == 0x8143) c = ',';
775                 else if (c == 0x8144) c = '.';
776                 else if (c == 0x8146) c = ':';
777                 else if (c == 0x8147) c = ';';
778                 else if (c == 0x8148) c = '?';
779                 else if (c == 0x8149) c = '!';
780                 else if (c == 0x815E) c = '/';
781                 else if (c == 0x8168) c = '"';
782                 else if (c == 0x8169) c = '(';
783                 else if (c == 0x816A) c = ')';
784                 else if (c == 0x816D) c = '[';
785                 else if (c == 0x816E) c = ']';
786                 else if (c == 0x817C) c = '-';
787                 else {
788                         str[i] = ' ';
789                         sstr[x++] = *ptr++; sstr[x++] = *ptr++;
790                         continue;
791                 }
792
793                 str[i] = sstr[x++] = c;
794                 ptr += 2;
795         }
796
797         trim(str);
798         trim(sstr);
799
800         ptr = data + block * 8192 + 0x60; // icon palette data
801
802         for (i = 0; i < 16; i++) {
803                 clut[i] = *((unsigned short *)ptr);
804                 ptr += 2;
805         }
806
807         for (i = 0; i < Info->IconCount; i++) {
808                 short *icon = &Info->Icon[i * 16 * 16];
809
810                 ptr = data + block * 8192 + 128 + 128 * i; // icon data
811
812                 for (x = 0; x < 16 * 16; x++) {
813                         icon[x++] = clut[*ptr & 0xf];
814                         icon[x] = clut[*ptr >> 4];
815                         ptr++;
816                 }
817         }
818
819         ptr = data + block * 128;
820
821         Info->Flags = *ptr;
822
823         ptr += 0xa;
824         strncpy(Info->ID, ptr, 12);
825         ptr += 12;
826         strncpy(Info->Name, ptr, 16);
827 }
828
829 int sioFreeze(void *f, int Mode) {
830         gzfreeze(buf, sizeof(buf));
831         gzfreeze(&StatReg, sizeof(StatReg));
832         gzfreeze(&ModeReg, sizeof(ModeReg));
833         gzfreeze(&CtrlReg, sizeof(CtrlReg));
834         gzfreeze(&BaudReg, sizeof(BaudReg));
835         gzfreeze(&bufcount, sizeof(bufcount));
836         gzfreeze(&parp, sizeof(parp));
837         gzfreeze(&mcdst, sizeof(mcdst));
838         gzfreeze(&rdwr, sizeof(rdwr));
839         gzfreeze(&adrH, sizeof(adrH));
840         gzfreeze(&adrL, sizeof(adrL));
841         gzfreeze(&padst, sizeof(padst));
842
843         return 0;
844 }