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