1 /***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
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. *
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. *
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 ***************************************************************************/
21 * Miscellaneous functions, including savestates and CD-ROM loading.
33 char CdromId[10] = "";
34 char CdromLabel[33] = "";
36 // PSX Executable types
42 #define ISODCL(from, to) (to - from + 1)
44 struct iso_directory_record {
45 char length [ISODCL (1, 1)]; /* 711 */
46 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
47 char extent [ISODCL (3, 10)]; /* 733 */
48 char size [ISODCL (11, 18)]; /* 733 */
49 char date [ISODCL (19, 25)]; /* 7 by 711 */
50 char flags [ISODCL (26, 26)];
51 char file_unit_size [ISODCL (27, 27)]; /* 711 */
52 char interleave [ISODCL (28, 28)]; /* 711 */
53 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
54 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
58 void mmssdd( char *b, char *p )
61 int block = SWAP32(*((uint32_t*) b));
64 m = block / 4500; // minutes
65 block = block - m * 4500; // minutes rest
66 s = block / 75; // seconds
67 d = block - s * 75; // seconds rest
69 m = ((m / 10) << 4) | m % 10;
70 s = ((s / 10) << 4) | s % 10;
71 d = ((d / 10) << 4) | d % 10;
79 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
84 if (time[1] == 60) { \
89 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
92 if (CDR_readTrack(time) == -1) return -1; \
93 buf = (void *)CDR_getBuffer(); \
94 if (buf == NULL) return -1; \
95 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
97 #define READDIR(_dir) \
99 memcpy(_dir, buf + 12, 2048); \
103 memcpy(_dir + 2048, buf + 12, 2048);
105 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
106 struct iso_directory_record *dir;
112 // only try to scan if a filename is given
113 if (!strlen(filename)) return -1;
117 dir = (struct iso_directory_record*) &mdir[i];
118 if (dir->length[0] == 0) {
121 i += (u8)dir->length[0];
123 if (dir->flags[0] & 0x2) { // it's a dir
124 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
125 if (filename[dir->name_len[0]] != '\\') continue;
127 filename += dir->name_len[0] + 1;
129 mmssdd(dir->extent, (char *)time);
135 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
136 mmssdd(dir->extent, (char *)time);
145 static const unsigned int gpu_ctl_def[] = {
146 0x00000000, 0x01000000, 0x03000000, 0x04000000,
147 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
150 static const unsigned int gpu_data_def[] = {
151 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
152 0xe5001000, 0xe6000000,
153 0x02000000, 0x00000000, 0x01ff03ff,
156 static void fake_bios_gpu_setup(void)
160 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
161 GPU_writeStatus(gpu_ctl_def[i]);
163 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
164 GPU_writeData(gpu_data_def[i]);
169 struct iso_directory_record *dir;
174 // not the best place to do it, but since BIOS boot logo killer
175 // is just below, do it here
176 fake_bios_gpu_setup();
180 psxRegs.pc = psxRegs.GPR.n.ra;
184 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
188 // skip head and sub, and go to the root directory record
189 dir = (struct iso_directory_record*) &buf[12+156];
191 mmssdd(dir->extent, (char*)time);
195 // Load SYSTEM.CNF and scan for the main executable
196 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
197 // if SYSTEM.CNF is missing, start an existing PSX.EXE
198 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
203 // read the SYSTEM.CNF
206 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
207 if (GetCdromFile(mdir, time, exename) == -1) {
208 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
209 if (GetCdromFile(mdir, time, exename) == -1) {
210 char *ptr = strstr((char *)buf + 12, "cdrom:");
213 while (*ptr == '\\' || *ptr == '/') ptr++;
214 strncpy(exename, ptr, 255);
217 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
219 if (GetCdromFile(mdir, time, exename) == -1)
226 // Read the EXE-Header
230 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
232 psxRegs.pc = SWAP32(tmpHead.pc0);
233 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
234 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
235 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
237 tmpHead.t_size = SWAP32(tmpHead.t_size);
238 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
240 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
243 // Read the rest of the main executable
244 while (tmpHead.t_size & ~2047) {
245 void *ptr = (void *)PSXM(tmpHead.t_addr);
250 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
252 tmpHead.t_size -= 2048;
253 tmpHead.t_addr += 2048;
259 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
260 struct iso_directory_record *dir;
267 sscanf(filename, "cdrom:\\%255s", exename);
269 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
273 // skip head and sub, and go to the root directory record
274 dir = (struct iso_directory_record *)&buf[12 + 156];
276 mmssdd(dir->extent, (char*)time);
280 if (GetCdromFile(mdir, time, exename) == -1) return -1;
284 memcpy(head, buf + 12, sizeof(EXE_HEADER));
288 psxCpu->Clear(addr, size / 4);
291 while (size & ~2047) {
297 memcpy(mem, buf + 12, 2048);
307 struct iso_directory_record *dir;
308 unsigned char time[4];
310 unsigned char mdir[4096];
318 time[2] = itob(0x10);
322 memset(CdromLabel, 0, sizeof(CdromLabel));
323 memset(CdromId, 0, sizeof(CdromId));
324 memset(exename, 0, sizeof(exename));
326 strncpy(CdromLabel, buf + 52, 32);
328 // skip head and sub, and go to the root directory record
329 dir = (struct iso_directory_record *)&buf[12 + 156];
331 mmssdd(dir->extent, (char *)time);
335 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
338 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
339 if (GetCdromFile(mdir, time, exename) == -1) {
340 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
341 if (GetCdromFile(mdir, time, exename) == -1) {
342 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
345 while (*ptr == '\\' || *ptr == '/') ptr++;
346 strncpy(exename, ptr, 255);
349 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
351 if (GetCdromFile(mdir, time, exename) == -1)
352 return -1; // main executable not found
357 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
358 strcpy(exename, "PSX.EXE;1");
359 strcpy(CdromId, "SLUS99999");
361 return -1; // SYSTEM.CNF and PSX.EXE not found
363 if (CdromId[0] == '\0') {
364 len = strlen(exename);
366 for (i = 0; i < len; ++i) {
367 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
369 if (isalnum(exename[i]))
370 CdromId[c++] = exename[i];
374 if (CdromId[0] == '\0')
375 strcpy(CdromId, "SLUS99999");
377 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
379 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
380 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
381 !strncmp(CdromId, "DTLS3035", 8) ||
382 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
383 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
384 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
385 Config.PsxType = PSX_TYPE_PAL; // pal
386 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
389 if (CdromLabel[0] == ' ') {
390 strncpy(CdromLabel, CdromId, 9);
392 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
393 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
394 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
403 static int PSXGetFileType(FILE *f) {
404 unsigned long current;
410 fseek(f, 0L, SEEK_SET);
411 fread(mybuf, 2048, 1, f);
412 fseek(f, current, SEEK_SET);
414 exe_hdr = (EXE_HEADER *)mybuf;
415 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
418 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
421 coff_hdr = (FILHDR *)mybuf;
422 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
428 // temporary pandora workaround..
430 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
435 tmp = malloc(size * nmemb);
437 ret = fread(tmp, size, nmemb, stream);
438 memcpy(ptr, tmp, size * nmemb);
444 int Load(const char *ExePath) {
450 u32 section_address, section_size;
453 strncpy(CdromId, "SLUS99999", 9);
454 strncpy(CdromLabel, "SLUS_999.99", 11);
456 tmpFile = fopen(ExePath, "rb");
457 if (tmpFile == NULL) {
458 SysPrintf(_("Error opening file: %s.\n"), ExePath);
461 type = PSXGetFileType(tmpFile);
464 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
465 section_address = SWAP32(tmpHead.t_addr);
466 section_size = SWAP32(tmpHead.t_size);
467 mem = PSXM(section_address);
469 fseek(tmpFile, 0x800, SEEK_SET);
470 fread_to_ram(mem, section_size, 1, tmpFile);
471 psxCpu->Clear(section_address, section_size / 4);
474 psxRegs.pc = SWAP32(tmpHead.pc0);
475 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
476 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
477 if (psxRegs.GPR.n.sp == 0)
478 psxRegs.GPR.n.sp = 0x801fff00;
482 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
484 fread(&opcode, 1, 1, tmpFile);
486 case 1: /* Section loading */
487 fread(§ion_address, 4, 1, tmpFile);
488 fread(§ion_size, 4, 1, tmpFile);
489 section_address = SWAPu32(section_address);
490 section_size = SWAPu32(section_size);
492 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
494 mem = PSXM(section_address);
496 fread_to_ram(mem, section_size, 1, tmpFile);
497 psxCpu->Clear(section_address, section_size / 4);
500 case 3: /* register loading (PC only?) */
501 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
502 fread(&psxRegs.pc, 4, 1, tmpFile);
503 psxRegs.pc = SWAPu32(psxRegs.pc);
505 case 0: /* End of file */
508 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
512 } while (opcode != 0 && retval == 0);
515 SysPrintf(_("COFF files not supported.\n"));
519 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
520 SysPrintf(_("(did you forget -cdfile ?)\n"));
528 CdromLabel[0] = '\0';
536 static void *zlib_open(const char *name, const char *mode)
538 return gzopen(name, mode);
541 static int zlib_read(void *file, void *buf, u32 len)
543 return gzread(file, buf, len);
546 static int zlib_write(void *file, const void *buf, u32 len)
548 return gzwrite(file, buf, len);
551 static long zlib_seek(void *file, long offs, int whence)
553 return gzseek(file, offs, whence);
556 static void zlib_close(void *file)
561 struct PcsxSaveFuncs SaveFuncs = {
562 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
565 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
567 // Savestate Versioning!
568 // If you make changes to the savestate version, please increment the value below.
569 static const u32 SaveVersion = 0x8b410006;
571 int SaveState(const char *file) {
578 f = SaveFuncs.open(file, "wb");
579 if (f == NULL) return -1;
581 new_dyna_before_save();
583 SaveFuncs.write(f, (void *)PcsxHeader, 32);
584 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
585 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
587 pMem = (unsigned char *)malloc(128 * 96 * 3);
588 if (pMem == NULL) return -1;
589 GPU_getScreenPic(pMem);
590 SaveFuncs.write(f, pMem, 128 * 96 * 3);
596 SaveFuncs.write(f, psxM, 0x00200000);
597 SaveFuncs.write(f, psxR, 0x00080000);
598 SaveFuncs.write(f, psxH, 0x00010000);
599 // only partial save of psxRegisters to maintain savestate compat
600 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
603 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
604 gpufP->ulFreezeVersion = 1;
605 GPU_freeze(1, gpufP);
606 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
610 spufP = (SPUFreeze_t *) malloc(16);
611 SPU_freeze(2, spufP, psxRegs.cycle);
612 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
614 spufP = (SPUFreeze_t *) malloc(Size);
615 SPU_freeze(1, spufP, psxRegs.cycle);
616 SaveFuncs.write(f, spufP, Size);
624 new_dyna_freeze(f, 1);
628 new_dyna_after_save();
633 int LoadState(const char *file) {
642 f = SaveFuncs.open(file, "rb");
643 if (f == NULL) return -1;
645 SaveFuncs.read(f, header, sizeof(header));
646 SaveFuncs.read(f, &version, sizeof(u32));
647 SaveFuncs.read(f, &hle, sizeof(boolean));
649 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
659 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
661 SaveFuncs.read(f, psxM, 0x00200000);
662 SaveFuncs.read(f, psxR, 0x00080000);
663 SaveFuncs.read(f, psxH, 0x00010000);
664 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
665 psxRegs.gteBusyCycle = psxRegs.cycle;
671 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
672 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
673 GPU_freeze(0, gpufP);
675 if (HW_GPU_STATUS == 0)
676 HW_GPU_STATUS = SWAP32(GPU_readStatus());
679 SaveFuncs.read(f, &Size, 4);
680 spufP = (SPUFreeze_t *)malloc(Size);
681 SaveFuncs.read(f, spufP, Size);
682 SPU_freeze(0, spufP, psxRegs.cycle);
690 new_dyna_freeze(f, 0);
697 int CheckState(const char *file) {
703 f = SaveFuncs.open(file, "rb");
704 if (f == NULL) return -1;
706 SaveFuncs.read(f, header, sizeof(header));
707 SaveFuncs.read(f, &version, sizeof(u32));
708 SaveFuncs.read(f, &hle, sizeof(boolean));
712 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
718 // NET Function Helpers
721 if (NET_recvData == NULL || NET_sendData == NULL)
724 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
725 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
726 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
727 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
728 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
729 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
737 if (NET_recvData == NULL || NET_sendData == NULL)
740 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
741 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
742 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
743 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
744 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
749 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
750 if (tmp != Config.Cpu) {
753 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
754 else psxCpu = &psxRec;
758 if (psxCpu->Init() == -1) {
759 SysClose(); return -1;
767 // remove the leading and trailing spaces in a string
768 void trim(char *str) {
772 // skip leading blanks
773 while (str[pos] <= ' ' && str[pos] > 0)
777 *(dest++) = str[pos];
781 *(dest--) = '\0'; // store the null
783 // remove trailing blanks
784 while (dest >= str && *dest <= ' ' && *dest > 0)
788 // lookup table for crc calculation
789 static unsigned short crctab[256] = {
790 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
791 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
792 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
793 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
794 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
795 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
796 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
797 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
798 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
799 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
800 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
801 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
802 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
803 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
804 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
805 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
806 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
807 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
808 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
809 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
810 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
811 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
812 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
813 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
814 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
815 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
816 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
817 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
818 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
821 u16 calcCrc(u8 *d, int len) {
825 for (i = 0; i < len; i++) {
826 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);