/******************************************************************************* Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. (c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com) and Jerremy Koot (jkoot@snes9x.com) (c) Copyright 2001 - 2004 John Weidman (jweidman@slip.net) (c) Copyright 2002 - 2004 Brad Jorsch (anomie@users.sourceforge.net), funkyass (funkyass@spam.shaw.ca), Joel Yliluoma (http://iki.fi/bisqwit/) Kris Bleakley (codeviolation@hotmail.com), Matthew Kendora, Nach (n-a-c-h@users.sourceforge.net), Peter Bortas (peter@bortas.org) and zones (kasumitokoduck@yahoo.com) C4 x86 assembler and some C emulation code (c) Copyright 2000 - 2003 zsKnight (zsknight@zsnes.com), _Demo_ (_demo_@zsnes.com), and Nach C4 C++ code (c) Copyright 2003 Brad Jorsch DSP-1 emulator code (c) Copyright 1998 - 2004 Ivar (ivar@snes9x.com), _Demo_, Gary Henderson, John Weidman, neviksti (neviksti@hotmail.com), Kris Bleakley, Andreas Naive DSP-2 emulator code (c) Copyright 2003 Kris Bleakley, John Weidman, neviksti, Matthew Kendora, and Lord Nightmare (lord_nightmare@users.sourceforge.net OBC1 emulator code (c) Copyright 2001 - 2004 zsKnight, pagefault (pagefault@zsnes.com) and Kris Bleakley Ported from x86 assembler to C by sanmaiwashi SPC7110 and RTC C++ emulator code (c) Copyright 2002 Matthew Kendora with research by zsKnight, John Weidman, and Dark Force S-DD1 C emulator code (c) Copyright 2003 Brad Jorsch with research by Andreas Naive and John Weidman S-RTC C emulator code (c) Copyright 2001 John Weidman ST010 C++ emulator code (c) Copyright 2003 Feather, Kris Bleakley, John Weidman and Matthew Kendora Super FX x86 assembler emulator code (c) Copyright 1998 - 2003 zsKnight, _Demo_, and pagefault Super FX C emulator code (c) Copyright 1997 - 1999 Ivar, Gary Henderson and John Weidman SH assembler code partly based on x86 assembler code (c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se) Specific ports contains the works of other authors. See headers in individual files. Snes9x homepage: http://www.snes9x.com Permission to use, copy, modify and distribute Snes9x in both binary and source form, for non-commercial purposes, is hereby granted without fee, providing that this license information and copyright notice appear with all copies and any derived work. This software is provided 'as-is', without any express or implied warranty. In no event shall the authors be held liable for any damages arising from the use of this software. Snes9x is freeware for PERSONAL USE only. Commercial users should seek permission of the copyright holders first. Commercial use includes charging money for Snes9x or software derived from Snes9x. The copyright holders request that bug fixes and improvements to the code should be forwarded to them so everyone can benefit from the modifications in future versions. Super NES and Super Nintendo Entertainment System are trademarks of Nintendo Co., Limited and its subsidiary companies. *******************************************************************************/ #include "spc7110.h" #include "memmap.h" #include #include //Windows includes #ifdef __WIN32__ #ifndef _XBOX // chdir and getcwd not supported on Xbox hardware #include #define chdir _chdir #define getcwd _getcwd #endif #ifndef PATH_MAX #ifdef MAX_PATH #define PATH_MAX MAX_PATH #else #define PATH_MAX 1024 #endif #endif #else // Unix #include "display.h" #include #include #endif const char* S9xGetFilename(const char*); char* osd_GetPackDir(); //really not needed, but usually MS adds the _ to POSIX functions, //while *nix doesn't, so this was to "un-M$" the function. #define splitpath _splitpath //not much headroom, but FEOEZ has 41 tables, I think, and SPL4 has 38. #define MAX_TABLES 48 //default to using 5 megs of RAM for method 3 caching. uint16_t cacheMegs = 5; //using function pointers to initialize cache management void (*CleanUp7110)(void) = NULL; void (*LoadUp7110)(char*) = &SPC7110Load; void (*Copy7110)(void) = NULL; //size and offset of the pack data //offset and size of reads from pack typedef struct SPC7110DecompressionLocationStruct { uint32_t offset; uint32_t size; uint16_t used_offset; uint16_t used_len; } Data7110; //this maps an index.bin table to the decompression pack typedef struct SPC7110DecompressionIndexStruct { int table; bool is_file; Data7110 location[256]; } Index7110; //this contains all the data for the decompression pack. typedef struct SPC7110DecompressionPackStructure { uint8_t* binfiles[MAX_TABLES]; Index7110 tableEnts[MAX_TABLES]; int last_table; int idx; uint8_t last_idx; uint16_t last_offset; } Pack7110; char pfold[9]; //hack variable for log naming (each game makes a different log) Pack7110* decompack = NULL; //decompression pack uses a fair chunk of RAM, so dynalloc it. SPC7110Regs s7r; //SPC7110 registers, about 33KB S7RTC rtc_f9; //FEOEZ (and Shounen Jump no SHou) RTC void S9xUpdateRTC(); //S-RTC function hacked to work with the RTC //Emulate power on state void S9xSpc7110Init() { s7r.DataRomOffset = 0x00100000; //handy constant! s7r.DataRomSize = Memory.CalculatedSize - s7r.DataRomOffset; s7r.reg4800 = 0; s7r.reg4801 = 0; s7r.reg4802 = 0; s7r.reg4803 = 0; s7r.reg4804 = 0; s7r.reg4805 = 0; s7r.reg4806 = 0; s7r.reg4807 = 0; s7r.reg4808 = 0; s7r.reg4809 = 0; s7r.reg480A = 0; s7r.reg480B = 0; s7r.reg480C = 0; s7r.reg4811 = 0; s7r.reg4812 = 0; s7r.reg4813 = 0; s7r.reg4814 = 0; s7r.reg4815 = 0; s7r.reg4816 = 0; s7r.reg4817 = 0; s7r.reg4818 = 0; s7r.reg4820 = 0; s7r.reg4821 = 0; s7r.reg4822 = 0; s7r.reg4823 = 0; s7r.reg4824 = 0; s7r.reg4825 = 0; s7r.reg4826 = 0; s7r.reg4827 = 0; s7r.reg4828 = 0; s7r.reg4829 = 0; s7r.reg482A = 0; s7r.reg482B = 0; s7r.reg482C = 0; s7r.reg482D = 0; s7r.reg482E = 0; s7r.reg482F = 0; s7r.reg4830 = 0; s7r.reg4831 = 0; s7r.reg4832 = 1; s7r.reg4833 = 2; s7r.reg4834 = 0; s7r.reg4840 = 0; s7r.reg4841 = 0; s7r.reg4842 = 0; s7r.written = 0; s7r.offset_add = 0; s7r.AlignBy = 1; (*LoadUp7110)(osd_GetPackDir()); s7r.bank50Internal = 0; memset(s7r.bank50, 0x00, DECOMP_BUFFER_SIZE); } //full cache decompression routine (memcpy) Method 1 void MovePackData() { //log the last entry Data7110* log = & (decompack->tableEnts[decompack->idx].location[decompack->last_idx]); if ((log->used_len + log->used_offset) < (decompack->last_offset + (uint16_t)s7r.bank50Internal)) { log->used_len = s7r.bank50Internal; log->used_offset = decompack->last_offset; } //set up for next logging decompack->last_offset = (s7r.reg4805) | (s7r.reg4806 << 8); decompack->last_idx = s7r.reg4804; //start decompression int table = (s7r.reg4803 << 16) | (s7r.reg4802 << 8) | s7r.reg4801; //the table is a offset multiplier byte and a big-endian pointer int j = 4 * s7r.reg4804; j += s7r.DataRomOffset; j += table; //set proper offsetting. if (s7r.reg480B == 0) s7r.AlignBy = 0; else { switch (Memory.ROM[j]) { case 0x03: s7r.AlignBy = 8; break; case 0x01: s7r.AlignBy = 2; break; case 0x02: s7r.AlignBy = 4; break; case 0x00: default: s7r.AlignBy = 1; break; } } //note that we are still setting up for the next log. decompack->last_offset *= s7r.AlignBy; decompack->last_offset %= DECOMP_BUFFER_SIZE; //find the table if (table != decompack->last_table) { int i = 0; while (i < MAX_TABLES && decompack->tableEnts[i].table != table) i++; if (i == MAX_TABLES) { #ifdef _XBOX FILE* fp = fopen("T:\\sp7err.out", "a"); #else FILE* fp = fopen("sp7err.out", "a"); #endif // fprintf(fp, "Table Entry %06X:%02X not found\n", table, s7r.reg4804); fclose(fp); return; } decompack->idx = i; decompack->last_table = table; } //copy data if (decompack->binfiles[decompack->idx]) { memcpy(s7r.bank50, &(decompack->binfiles[decompack->idx][decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset]), decompack->tableEnts[decompack->idx].location[s7r.reg4804].size); } } //this is similar to the last function, but it keeps the last 5 accessed files open, // and reads the data directly. Method 2 void ReadPackData() { static int table_age_2; static int table_age_3; static int table_age_4; static int table_age_5; int table = (s7r.reg4803 << 16) | (s7r.reg4802 << 8) | s7r.reg4801; if (table == 0) { table_age_2 = table_age_3 = table_age_4 = table_age_5 = MAX_TABLES; return; } if (table_age_2 == 0 && table_age_3 == 0 && table_age_4 == 0 && table_age_5 == 0) table_age_2 = table_age_3 = table_age_4 = table_age_5 = MAX_TABLES; Data7110* log = & (decompack->tableEnts[decompack->idx].location[decompack->last_idx]); if ((log->used_len + log->used_offset) < (decompack->last_offset + (uint16_t)s7r.bank50Internal)) { log->used_len = s7r.bank50Internal; log->used_offset = decompack->last_offset; } decompack->last_offset = (s7r.reg4805) | (s7r.reg4806 << 8); decompack->last_idx = s7r.reg4804; int j = 4 * s7r.reg4804; j += s7r.DataRomOffset; j += table; if (s7r.reg480B == 0) s7r.AlignBy = 0; else { switch (Memory.ROM[j]) { case 0x03: s7r.AlignBy = 8; break; case 0x01: s7r.AlignBy = 2; break; case 0x02: s7r.AlignBy = 4; break; case 0x00: default: s7r.AlignBy = 1; break; } } decompack->last_offset *= s7r.AlignBy; decompack->last_offset %= DECOMP_BUFFER_SIZE; if (table != decompack->last_table) { int i = 0; while (i < MAX_TABLES && decompack->tableEnts[i].table != table) i++; if (i == MAX_TABLES) { FILE* fp = fopen("sp7err.out", "a"); // fprintf(fp, "Table Entry %06X:%02X not found\n", table, s7r.reg4804); fclose(fp); return; } if (i != table_age_2 && i != table_age_3 && i != table_age_4 && i != table_age_5) { if (table_age_5 != MAX_TABLES && decompack->binfiles[table_age_5]) { fclose((FILE*)(decompack->binfiles[table_age_5])); (decompack->binfiles[table_age_5]) = NULL; } table_age_5 = table_age_4; table_age_4 = table_age_3; table_age_3 = table_age_2; table_age_2 = decompack->idx; char name[PATH_MAX]; //open file char drive [_MAX_DRIVE + 1]; char dir [_MAX_DIR + 1]; char fname [_MAX_FNAME + 1]; char ext [_MAX_EXT + 1]; splitpath(Memory.ROMFilename, drive, dir, fname, ext); strcpy(name, drive); //strcat(filename, "\\"); strcat(name, dir); strcat(name, pfold); char bfname[11]; sprintf(bfname, "%06X.bin", table); strcat(name, "/"); strcat(name, bfname); decompack->binfiles[i] = (uint8_t*)fopen(name, "rb"); } else { //fix tables in this case if (table_age_5 == i) { table_age_5 = table_age_4; table_age_4 = table_age_3; table_age_3 = table_age_2; table_age_2 = decompack->idx; } if (table_age_4 == i) { table_age_4 = table_age_3; table_age_3 = table_age_2; table_age_2 = decompack->idx; } if (table_age_3 == i) { table_age_3 = table_age_2; table_age_2 = decompack->idx; } if (table_age_2 == i) table_age_2 = decompack->idx; } decompack->idx = i; decompack->last_table = table; } //do read here. if (decompack->binfiles[decompack->idx]) { fseek((FILE*)(decompack->binfiles[decompack->idx]), decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset, 0); fread(s7r.bank50, 1, ( decompack->tableEnts[decompack->idx].location[s7r.reg4804].size), (FILE*)(decompack->binfiles[decompack->idx])); } } //Cache Method 3: some entries are cached, others are file handles. //use is_file to distinguish. void GetPackData() { Data7110* log = & (decompack->tableEnts[decompack->idx].location[decompack->last_idx]); if ((log->used_len + log->used_offset) < (decompack->last_offset + (uint16_t)s7r.bank50Internal)) { log->used_len = s7r.bank50Internal; log->used_offset = decompack->last_offset; } decompack->last_offset = (s7r.reg4805) | (s7r.reg4806 << 8); decompack->last_idx = s7r.reg4804; int table = (s7r.reg4803 << 16) | (s7r.reg4802 << 8) | s7r.reg4801; int j = 4 * s7r.reg4804; j += s7r.DataRomOffset; j += table; if (s7r.reg480B == 0) s7r.AlignBy = 0; else { switch (Memory.ROM[j]) { case 0x03: s7r.AlignBy = 8; break; case 0x01: s7r.AlignBy = 2; break; case 0x02: s7r.AlignBy = 4; break; case 0x00: default: s7r.AlignBy = 1; break; } } decompack->last_offset *= s7r.AlignBy; decompack->last_offset %= DECOMP_BUFFER_SIZE; if (table != decompack->last_table) { int i = 0; while (i < MAX_TABLES && decompack->tableEnts[i].table != table) i++; if (i == MAX_TABLES) { FILE* fp = fopen("sp7err.out", "a"); // fprintf(fp, "Table Entry %06X:%02X not found\n", table, s7r.reg4804); fclose(fp); return; } decompack->idx = i; decompack->last_table = table; } if (decompack->binfiles[decompack->idx]) { if (decompack->tableEnts[decompack->idx].is_file) { fseek((FILE*)decompack->binfiles[decompack->idx], decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset, 0); fread(s7r.bank50, 1, ( decompack->tableEnts[decompack->idx].location[s7r.reg4804].size), (FILE*)(decompack->binfiles[decompack->idx])); } else { memcpy(s7r.bank50, &(decompack->binfiles[decompack->idx][decompack->tableEnts[decompack->idx].location[s7r.reg4804].offset]), decompack->tableEnts[decompack->idx].location[s7r.reg4804].size); } } } //reads SPC7110 and RTC registers. uint8_t S9xGetSPC7110(uint16_t Address) { switch (Address) { //decompressed data read port. decrements 4809-A (with wrap) //4805-6 is the offset into the bank //AlignBy is set (afaik) at decompression time, and is the offset multiplier //bank50internal is an internal pointer to the actual byte to read. //so you read from offset*multiplier + bank50internal //the offset registers cannot be incremented due to the offset multiplier. case 0x4800: { uint16_t count = s7r.reg4809 | (s7r.reg480A << 8); uint32_t i, j; j = (s7r.reg4805 | (s7r.reg4806 << 8)); j *= s7r.AlignBy; i = j; if (count > 0) count--; else count = 0xFFFF; s7r.reg4809 = 0x00ff & count; s7r.reg480A = (0xff00 & count) >> 8; i += s7r.bank50Internal; i %= DECOMP_BUFFER_SIZE; s7r.reg4800 = s7r.bank50[i]; s7r.bank50Internal++; s7r.bank50Internal %= DECOMP_BUFFER_SIZE; } return s7r.reg4800; //table register low case 0x4801: return s7r.reg4801; //table register middle case 0x4802: return s7r.reg4802; //table register high case 0x4803: return s7r.reg4803; //index of pointer in table (each entry is 4 bytes) case 0x4804: return s7r.reg4804; //offset register low case 0x4805: return s7r.reg4805; //offset register high case 0x4806: return s7r.reg4806; //DMA channel (not that I see this usually set, //regardless of what channel DMA is on) case 0x4807: return s7r.reg4807; //C r/w option, unknown, defval:00 is what Dark Force says //afaict, Snes9x doesn't use this at all. case 0x4808: return s7r.reg4808; //C-Length low //counts down the number of bytes left to read from the decompression buffer. //this is set by the ROM, and wraps on bounds. case 0x4809: return s7r.reg4809; //C Length high case 0x480A: return s7r.reg480A; //Offset enable. //if this is zero, 4805-6 are useless. Emulated by setting AlignBy to 0 case 0x480B: return s7r.reg480B; //decompression finished: just emulated by switching each read. case 0x480C: s7r.reg480C ^= 0x80; return s7r.reg480C ^ 0x80; //Data access port //reads from the data ROM (anywhere over the first 8 mbits //behavior is complex, will document later, //possibly missing cases, because of the number of switches in play case 0x4810: if (s7r.written == 0) return 0; if ((s7r.written & 0x07) == 0x07) { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; i %= s7r.DataRomSize; if (s7r.reg4818 & 0x02) { if (s7r.reg4818 & 0x08) { int16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; i += r4814; r4814++; s7r.reg4815 = (uint8_t)(r4814 >> 8); s7r.reg4814 = (uint8_t)(r4814 & 0x00FF); } else { uint16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; i += r4814; if (r4814 != 0xFFFF) r4814++; else r4814 = 0; s7r.reg4815 = (uint8_t)(r4814 >> 8); s7r.reg4814 = (uint8_t)(r4814 & 0x00FF); } } i += s7r.DataRomOffset; uint8_t tmp = Memory.ROM[i]; i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x02) { } else if (s7r.reg4818 & 0x01) { if (s7r.reg4818 & 0x04) { int16_t inc; inc = (s7r.reg4817 << 8) | s7r.reg4816; if (!(s7r.reg4818 & 0x10)) i += inc; else { if (s7r.reg4818 & 0x08) { int16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += inc; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } else { uint16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += inc; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } } //is signed } else { uint16_t inc; inc = (s7r.reg4817 << 8) | s7r.reg4816; if (!(s7r.reg4818 & 0x10)) i += inc; else { if (s7r.reg4818 & 0x08) { int16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += inc; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } else { uint16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += inc; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } } } } else { if (!(s7r.reg4818 & 0x10)) i += 1; else { if (s7r.reg4818 & 0x08) { int16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += 1; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } else { uint16_t r4814; r4814 = (s7r.reg4815 << 8) | s7r.reg4814; r4814 += 1; s7r.reg4815 = (r4814 & 0xFF00) >> 8; s7r.reg4814 = r4814 & 0xFF; } } } i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); return tmp; } else return 0; //direct read address low case 0x4811: return s7r.reg4811; //direct read address middle case 0x4812: return s7r.reg4812; //direct read access high case 0x4813: return s7r.reg4813; //read adjust low case 0x4814: return s7r.reg4814; //read adjust high case 0x4815: return s7r.reg4815; //read increment low case 0x4816: return s7r.reg4816; //read increment high case 0x4817: return s7r.reg4817; //Data ROM command mode //essentially, this controls the insane code of $4810 and $481A case 0x4818: return s7r.reg4818; //read after adjust port //what this does, besides more nasty stuff like 4810, //I don't know. Just assume it is a different implementation of $4810, //if it helps your sanity case 0x481A: if (s7r.written == 0x1F) { uint32_t i = ((s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811); if (s7r.reg4818 & 0x08) { int16_t adj; adj = ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; i += adj; } else { uint16_t adj; adj = (s7r.reg4815 << 8) | s7r.reg4814; i += adj; } i %= s7r.DataRomSize; i += s7r.DataRomOffset; uint8_t tmp = Memory.ROM[i]; i = ((s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811); if (0x60 == (s7r.reg4818 & 0x60)) { i = ((s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811); if (!(s7r.reg4818 & 0x10)) { if (s7r.reg4818 & 0x08) { int16_t adj = ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; i += adj; } else { uint16_t adj; adj = (s7r.reg4815 << 8) | s7r.reg4814; i += adj; } i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } else { if (s7r.reg4818 & 0x08) { int16_t adj = ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; adj += adj; s7r.reg4815 = (adj & 0xFF00) >> 8; s7r.reg4814 = adj & 0xFF; } else { uint16_t adj; adj = (s7r.reg4815 << 8) | s7r.reg4814; adj += adj; s7r.reg4815 = (adj & 0xFF00) >> 8; s7r.reg4814 = adj & 0xFF; } } } return tmp; } else return 0; //multiplicand low or dividend lowest case 0x4820: return s7r.reg4820; //multiplicand high or divdend lower case 0x4821: return s7r.reg4821; //dividend higher case 0x4822: return s7r.reg4822; //dividend highest case 0x4823: return s7r.reg4823; //multiplier low case 0x4824: return s7r.reg4824; //multiplier high case 0x4825: return s7r.reg4825; //divisor low case 0x4826: return s7r.reg4826; //divisor high case 0x4827: return s7r.reg4827; //result lowest case 0x4828: return s7r.reg4828; //result lower case 0x4829: return s7r.reg4829; //result higher case 0x482A: return s7r.reg482A; //result highest case 0x482B: return s7r.reg482B; //remainder (division) low case 0x482C: return s7r.reg482C; //remainder (division) high case 0x482D: return s7r.reg482D; //signed/unsigned case 0x482E: return s7r.reg482E; //finished flag, emulated as an on-read toggle. case 0x482F: if (s7r.reg482F) { s7r.reg482F = 0; return 0x80; } return 0; break; //SRAM toggle case 0x4830: return s7r.reg4830; //DX bank mapping case 0x4831: return s7r.reg4831; //EX bank mapping case 0x4832: return s7r.reg4832; //FX bank mapping case 0x4833: return s7r.reg4833; //SRAM mapping? We have no clue! case 0x4834: return s7r.reg4834; //RTC enable case 0x4840: if (!Settings.SPC7110RTC) return Address >> 8; return s7r.reg4840; //command/index/value of RTC (essentially, zero unless we're in read mode case 0x4841: if (!Settings.SPC7110RTC) return Address >> 8; if (rtc_f9.init) { S9xUpdateRTC(); uint8_t tmp = rtc_f9.reg[rtc_f9.index]; rtc_f9.index++; rtc_f9.index %= 0x10; return tmp; } else return 0; //RTC done flag case 0x4842: if (!Settings.SPC7110RTC) return Address >> 8; s7r.reg4842 ^= 0x80; return s7r.reg4842 ^ 0x80; default: return 0x00; } } void S9xSetSPC7110(uint8_t data, uint16_t Address) { switch (Address) { //Writes to $4800 are undefined. //table low, middle, and high. case 0x4801: s7r.reg4801 = data; break; case 0x4802: s7r.reg4802 = data; break; case 0x4803: s7r.reg4803 = data; break; //table index (4 byte entries, bigendian with a multiplier byte) case 0x4804: s7r.reg4804 = data; break; //offset low case 0x4805: s7r.reg4805 = data; break; //offset high, starts decompression case 0x4806: s7r.reg4806 = data; (*Copy7110)(); s7r.bank50Internal = 0; s7r.reg480C &= 0x7F; break; //DMA channel register (Is it used??) case 0x4807: s7r.reg4807 = data; break; //C r/w? I have no idea. If you get weird values written here before a bug, //The Dumper should probably be contacted about running a test. case 0x4808: s7r.reg4808 = data; break; //C-Length low case 0x4809: s7r.reg4809 = data; break; //C-Length high case 0x480A: s7r.reg480A = data; break; //Offset enable case 0x480B: { s7r.reg480B = data; int table = (s7r.reg4803 << 16) | (s7r.reg4802 << 8) | s7r.reg4801; int j = 4 * s7r.reg4804; j += s7r.DataRomOffset; j += table; if (s7r.reg480B == 0) s7r.AlignBy = 0; else { switch (Memory.ROM[j]) { case 0x03: s7r.AlignBy = 8; break; case 0x01: s7r.AlignBy = 2; break; case 0x02: s7r.AlignBy = 4; break; case 0x00: default: s7r.AlignBy = 1; break; } } // s7r.decomp_set=true; } break; //$4810 is probably read only. //Data port address low case 0x4811: s7r.reg4811 = data; s7r.written |= 0x01; break; //data port address middle case 0x4812: s7r.reg4812 = data; s7r.written |= 0x02; break; //data port address high case 0x4813: s7r.reg4813 = data; s7r.written |= 0x04; break; //data port adjust low (has a funky immediate increment mode) case 0x4814: s7r.reg4814 = data; if (s7r.reg4818 & 0x02) { if ((s7r.reg4818 & 0x20) && !(s7r.reg4818 & 0x40)) { s7r.offset_add |= 0x01; if (s7r.offset_add == 3) { if (s7r.reg4818 & 0x10) { } else { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x08) i += (signed char)s7r.reg4814; else i += s7r.reg4814; i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } } } else if ((s7r.reg4818 & 0x40) && !(s7r.reg4818 & 0x20)) { s7r.offset_add |= 0x01; if (s7r.offset_add == 3) { if (s7r.reg4818 & 0x10) { } else { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x08) { int16_t adj; adj = ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; i += adj; } else { uint16_t adj; adj = (s7r.reg4815 << 8) | s7r.reg4814; i += adj; } i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } } } } s7r.written |= 0x08; break; //data port adjust high (has a funky immediate increment mode) case 0x4815: s7r.reg4815 = data; if (s7r.reg4818 & 0x02) { if (s7r.reg4818 & 0x20 && !(s7r.reg4818 & 0x40)) { s7r.offset_add |= 0x02; if (s7r.offset_add == 3) { if (s7r.reg4818 & 0x10) { } else { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x08) i += (signed char)s7r.reg4814; else i += s7r.reg4814; i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } } } else if (s7r.reg4818 & 0x40 && !(s7r.reg4818 & 0x20)) { s7r.offset_add |= 0x02; if (s7r.offset_add == 3) { if (s7r.reg4818 & 0x10) { } else { uint32_t i = (s7r.reg4813 << 16) | (s7r.reg4812 << 8) | s7r.reg4811; if (s7r.reg4818 & 0x08) { int16_t adj; adj = ((int16_t)(s7r.reg4815 << 8)) | s7r.reg4814; i += adj; } else { uint16_t adj; adj = (s7r.reg4815 << 8) | s7r.reg4814; i += adj; } i %= s7r.DataRomSize; s7r.reg4811 = i & 0x00FF; s7r.reg4812 = (i & 0x00FF00) >> 8; s7r.reg4813 = ((i & 0xFF0000) >> 16); } } } } s7r.written |= 0x10; break; //data port increment low case 0x4816: s7r.reg4816 = data; break; //data port increment high case 0x4817: s7r.reg4817 = data; break; //data port mode switches //note that it starts inactive. case 0x4818: if ((s7r.written & 0x18) != 0x18) break; s7r.offset_add = 0; s7r.reg4818 = data; break; //multiplicand low or dividend lowest case 0x4820: s7r.reg4820 = data; break; //multiplicand high or dividend lower case 0x4821: s7r.reg4821 = data; break; //dividend higher case 0x4822: s7r.reg4822 = data; break; //dividend highest case 0x4823: s7r.reg4823 = data; break; //multiplier low case 0x4824: s7r.reg4824 = data; break; //multiplier high (triggers operation) case 0x4825: s7r.reg4825 = data; if (s7r.reg482E & 0x01) { int16_t m1 = (int16_t)((s7r.reg4824) | (s7r.reg4825 << 8)); int16_t m2 = (int16_t)((s7r.reg4820) | (s7r.reg4821 << 8)); int mul = m1 * m2; s7r.reg4828 = (uint8_t)(mul & 0x000000FF); s7r.reg4829 = (uint8_t)((mul & 0x0000FF00) >> 8); s7r.reg482A = (uint8_t)((mul & 0x00FF0000) >> 16); s7r.reg482B = (uint8_t)((mul & 0xFF000000) >> 24); } else { uint32_t mul; uint16_t m1 = (uint16_t)((s7r.reg4824) | (s7r.reg4825 << 8)); uint16_t m2 = (uint16_t)((s7r.reg4820) | (s7r.reg4821 << 8)); mul = m1 * m2; s7r.reg4828 = (uint8_t)(mul & 0x000000FF); s7r.reg4829 = (uint8_t)((mul & 0x0000FF00) >> 8); s7r.reg482A = (uint8_t)((mul & 0x00FF0000) >> 16); s7r.reg482B = (uint8_t)((mul & 0xFF000000) >> 24); } s7r.reg482F = 0x80; break; //divisor low case 0x4826: s7r.reg4826 = data; break; //divisor high (triggers operation) case 0x4827: s7r.reg4827 = data; if (s7r.reg482E & 0x01) { int quotient; int16_t remainder; int dividend = (int)(s7r.reg4820 | (s7r.reg4821 << 8) | (s7r.reg4822 << 16) | (s7r.reg4823 << 24)); int16_t divisor = (int16_t)(s7r.reg4826 | (s7r.reg4827 << 8)); if (divisor != 0) { quotient = (int)(dividend / divisor); remainder = (int16_t)(dividend % divisor); } else { quotient = 0; remainder = dividend & 0x0000FFFF; } s7r.reg4828 = (uint8_t)(quotient & 0x000000FF); s7r.reg4829 = (uint8_t)((quotient & 0x0000FF00) >> 8); s7r.reg482A = (uint8_t)((quotient & 0x00FF0000) >> 16); s7r.reg482B = (uint8_t)((quotient & 0xFF000000) >> 24); s7r.reg482C = (uint8_t)remainder & 0x00FF; s7r.reg482D = (uint8_t)((remainder & 0xFF00) >> 8); } else { uint32_t quotient; uint16_t remainder; uint32_t dividend = (uint32_t)(s7r.reg4820 | (s7r.reg4821 << 8) | (s7r.reg4822 << 16) | (s7r.reg4823 << 24)); uint16_t divisor = (uint16_t)(s7r.reg4826 | (s7r.reg4827 << 8)); if (divisor != 0) { quotient = (uint32_t)(dividend / divisor); remainder = (uint16_t)(dividend % divisor); } else { quotient = 0; remainder = dividend & 0x0000FFFF; } s7r.reg4828 = (uint8_t)(quotient & 0x000000FF); s7r.reg4829 = (uint8_t)((quotient & 0x0000FF00) >> 8); s7r.reg482A = (uint8_t)((quotient & 0x00FF0000) >> 16); s7r.reg482B = (uint8_t)((quotient & 0xFF000000) >> 24); s7r.reg482C = (uint8_t)remainder & 0x00FF; s7r.reg482D = (uint8_t)((remainder & 0xFF00) >> 8); } s7r.reg482F = 0x80; break; //result registers are possibly read-only //reset: writes here nuke the whole math unit //Zero indicates unsigned math, resets with non-zero values turn on signed math case 0x482E: s7r.reg4820 = s7r.reg4821 = s7r.reg4822 = s7r.reg4823 = s7r.reg4824 = s7r.reg4825 = s7r.reg4826 = s7r.reg4827 = s7r.reg4828 = s7r.reg4829 = s7r.reg482A = s7r.reg482B = s7r.reg482C = s7r.reg482D = 0; s7r.reg482E = data; break; //math status register possibly read only //SRAM toggle case 0x4830: SPC7110Sram(data); s7r.reg4830 = data; break; //Bank DX mapping case 0x4831: s7r.reg4831 = data; break; //Bank EX mapping case 0x4832: s7r.reg4832 = data; break; //Bank FX mapping case 0x4833: s7r.reg4833 = data; break; //S-RAM mapping? who knows? case 0x4834: s7r.reg4834 = data; break; //RTC Toggle case 0x4840: if (0 == data) { S9xUpdateRTC(); // rtc_f9.init=false; // rtc_f9.index=-1; } if (data & 0x01) { s7r.reg4842 = 0x80; //rtc_f9.last_used=time(NULL);//???? rtc_f9.init = false; rtc_f9.index = -1; } s7r.reg4840 = data; break; //RTC init/command/index register case 0x4841: if (rtc_f9.init) { if (-1 == rtc_f9.index) { rtc_f9.index = data & 0x0F; break; } if (rtc_f9.control == 0x0C) { rtc_f9.index = data & 0x0F; s7r.reg4842 = 0x80; rtc_f9.last_used = time(NULL); } else { if (0x0D == rtc_f9.index) { if (data & 0x08) { if (rtc_f9.reg[1] < 3) { S9xUpdateRTC(); rtc_f9.reg[0] = 0; rtc_f9.reg[1] = 0; rtc_f9.last_used = time(NULL); } else { S9xUpdateRTC(); rtc_f9.reg[0] = 0; rtc_f9.reg[1] = 0; rtc_f9.last_used = time(NULL) - 60; S9xUpdateRTC(); rtc_f9.last_used = time(NULL); } data &= 0x07; } if (rtc_f9.reg[0x0D] & 0x01) { if (!(data % 2)) { rtc_f9.reg[rtc_f9.index & 0x0F] = data; rtc_f9.last_used = time(NULL) - 1; S9xUpdateRTC(); rtc_f9.last_used = time(NULL); } } } if (0x0F == rtc_f9.index) { if (data & 0x01 && !(rtc_f9.reg[0x0F] & 0x01)) { S9xUpdateRTC(); rtc_f9.reg[0] = 0; rtc_f9.reg[1] = 0; rtc_f9.last_used = time(NULL); } if (data & 0x02 && !(rtc_f9.reg[0x0F] & 0x02)) { S9xUpdateRTC(); rtc_f9.last_used = time(NULL); } } rtc_f9.reg[rtc_f9.index & 0x0F] = data; s7r.reg4842 = 0x80; rtc_f9.index = (rtc_f9.index + 1) % 0x10; } } else { if (data == 0x03 || data == 0x0C) { rtc_f9.init = true; rtc_f9.control = data; rtc_f9.index = -1; } } break; //writes to RTC status register aren't expected to be meaningful default: Address -= 0x4800; break; //16 BIT MULTIPLIER: ($FF00) high byte, defval:00 } } //emulate the SPC7110's ability to remap banks Dx, Ex, and Fx. uint8_t S9xGetSPC7110Byte(uint32_t Address) { uint32_t i; switch ((Address & 0x00F00000) >> 16) { case 0xD0: i = s7r.reg4831 * 0x00100000; break; case 0xE0: i = s7r.reg4832 * 0x00100000; break; case 0xF0: i = s7r.reg4833 * 0x00100000; break; default: i = 0; } i += Address & 0x000FFFFF; i += s7r.DataRomOffset; return Memory.ROM[i]; } /**********************************************************************************************/ /* S9xSRTCDaysInMonth() */ /* Return the number of days in a specific month for a certain year */ /* copied directly for RTC functionality, separated in case of incompatibilities */ /**********************************************************************************************/ int S9xRTCDaysInMonth(int month, int year) { int mdays; switch (month) { case 2: if ((year % 4 == 0)) // DKJM2 only uses 199x - 22xx mdays = 29; else mdays = 28; break; case 4: case 6: case 9: case 11: mdays = 30; break; default: // months 1,3,5,7,8,10,12 mdays = 31; break; } return mdays; } #define DAYTICKS (60*60*24) #define HOURTICKS (60*60) #define MINUTETICKS 60 /**********************************************************************************************/ /* S9xUpdateRTC() */ /* Advance the RTC time */ /**********************************************************************************************/ void S9xUpdateRTC() { time_t cur_systime; long time_diff; // Keep track of game time by computing the number of seconds that pass on the system // clock and adding the same number of seconds to the RTC clock structure. if (rtc_f9.init && 0 == (rtc_f9.reg[0x0D] & 0x01) && 0 == (rtc_f9.reg[0x0F] & 0x03)) { cur_systime = time(NULL); // This method assumes one time_t clock tick is one second // which should work on PCs and GNU systems. // If your tick interval is different adjust the // DAYTICK, HOURTICK, and MINUTETICK defines time_diff = (long)(cur_systime - rtc_f9.last_used); rtc_f9.last_used = cur_systime; if (time_diff > 0) { int seconds; int minutes; int hours; int days; int month; int year; int temp_days; int year_tens; int year_ones; if (time_diff > DAYTICKS) { days = time_diff / DAYTICKS; time_diff = time_diff - days * DAYTICKS; } else days = 0; if (time_diff > HOURTICKS) { hours = time_diff / HOURTICKS; time_diff = time_diff - hours * HOURTICKS; } else hours = 0; if (time_diff > MINUTETICKS) { minutes = time_diff / MINUTETICKS; time_diff = time_diff - minutes * MINUTETICKS; } else minutes = 0; if (time_diff > 0) seconds = time_diff; else seconds = 0; seconds += (rtc_f9.reg[1] * 10 + rtc_f9.reg[0]); if (seconds >= 60) { seconds -= 60; minutes += 1; } minutes += (rtc_f9.reg[3] * 10 + rtc_f9.reg[2]); if (minutes >= 60) { minutes -= 60; hours += 1; } hours += (rtc_f9.reg[5] * 10 + rtc_f9.reg[4]); if (hours >= 24) { hours -= 24; days += 1; } year = rtc_f9.reg[11] * 10 + rtc_f9.reg[10]; year += (1900); month = rtc_f9.reg[8] + 10 * rtc_f9.reg[9]; rtc_f9.reg[12] += days; days += (rtc_f9.reg[7] * 10 + rtc_f9.reg[6]); if (days > 0) { while (days > (temp_days = S9xRTCDaysInMonth(month, year))) { days -= temp_days; month += 1; if (month > 12) { year += 1; month = 1; } } } year_tens = year % 100; year_ones = year_tens % 10; year_tens /= 10; rtc_f9.reg[0] = seconds % 10; rtc_f9.reg[1] = seconds / 10; rtc_f9.reg[2] = minutes % 10; rtc_f9.reg[3] = minutes / 10; rtc_f9.reg[4] = hours % 10; rtc_f9.reg[5] = hours / 10; rtc_f9.reg[6] = days % 10; rtc_f9.reg[7] = days / 10; rtc_f9.reg[8] = month % 10; rtc_f9.reg[9] = month / 10; rtc_f9.reg[10] = year_ones; rtc_f9.reg[11] = year_tens; rtc_f9.reg[12] %= 7; return; } } } //allows DMA from the ROM (is this even possible on the SPC7110? uint8_t* Get7110BasePtr(uint32_t Address) { uint32_t i; switch ((Address & 0x00F00000) >> 16) { case 0xD0: i = s7r.reg4831 * 0x100000; break; case 0xE0: i = s7r.reg4832 * 0x100000; break; case 0xF0: i = s7r.reg4833 * 0x100000; break; default: i = 0; } i += Address & 0x000F0000; return &Memory.ROM[i]; } //loads the index into memory. //index.bin is little-endian //format index (1)-table(3)-file offset(4)-length(4) bool Load7110Index(char* filename) { FILE* fp; uint8_t buffer[12]; int table = 0; uint8_t index = 0; uint32_t offset = 0; uint32_t size = 0; int i = 0; fp = fopen(filename, "rb"); if (NULL == fp) return false; int f_len; //do while (1) { i = 0; f_len = fread(buffer, 1, 12, fp); if (f_len < 12) break; table = (buffer[3] << 16) | (buffer[2] << 8) | buffer[1]; index = buffer[0]; offset = (buffer[7] << 24) | (buffer[6] << 16) | (buffer[5] << 8) | buffer[4]; size = (buffer[11] << 24) | (buffer[10] << 16) | (buffer[9] << 8) | buffer[8]; while (i < MAX_TABLES && decompack->tableEnts[i].table != table && decompack->tableEnts[i].table != 0) i++; if (i == MAX_TABLES) return false; //added decompack->tableEnts[i].table = table; //----- decompack->tableEnts[i].location[index].offset = offset; decompack->tableEnts[i].location[index].size = size; decompack->tableEnts[i].location[index].used_len = 0; decompack->tableEnts[i].location[index].used_offset = 0; } //while(!feof(fp)); fclose(fp); return true; } //Cache 1 load function void SPC7110Load(char* dirname) { char temp_path[PATH_MAX]; int i = 0; decompack = (Pack7110*)malloc(sizeof(Pack7110)); #if !defined(_XBOX) && !defined(VITA) getcwd(temp_path, PATH_MAX); #endif memset(decompack, 0, sizeof(Pack7110)); #if !defined(_XBOX) && !defined(VITA) if (-1 == chdir(dirname)) S9xMessage(0, 0, "Graphics Pack not found!"); #endif #ifndef _XBOX Load7110Index("index.bin"); #else // D:\\ is always app.path in Xbox Load7110Index("d:\\index.bin"); #endif for (i = 0; i < MAX_TABLES; i++) { if (decompack->tableEnts[i].table != 0) { char binname[PATH_MAX]; #ifndef _XBOX sprintf(binname, "%06X.bin", decompack->tableEnts[i].table); #else sprintf(binname, "%s%06X.bin", filename, decompack->tableEnts[i].table); #endif struct stat buf; if (-1 != stat(binname, &buf)) decompack->binfiles[i] = (uint8_t*)malloc(buf.st_size); FILE* fp = fopen(binname, "rb"); if (fp) { fread(decompack->binfiles[i], buf.st_size, 1, fp); fclose(fp); } } } #if !defined(_XBOX) && !defined(VITA) chdir(temp_path); #endif Copy7110 = &MovePackData; CleanUp7110 = &Del7110Gfx; } //Cache 2 load function void SPC7110Open(char* dirname) { char temp_path[PATH_MAX]; int i = 0; decompack = (Pack7110*)malloc(sizeof(Pack7110)); #if !defined(_XBOX) && !defined(VITA) getcwd(temp_path, PATH_MAX); #endif memset(decompack, 0, sizeof(Pack7110)); #if !defined(_XBOX) && !defined(VITA) if (-1 == chdir(dirname)) S9xMessage(0, 0, "Graphics Pack not found!"); #endif #ifndef _XBOX Load7110Index("index.bin"); #else // D:\\ is always app.path in Xbox Load7110Index("d:\\index.bin"); #endif for (i = 0; i < MAX_TABLES; i++) decompack->binfiles[i] = NULL; ReadPackData(); #if !defined(_XBOX) && !defined(VITA) chdir(temp_path); #endif Copy7110 = &ReadPackData; CleanUp7110 = &Close7110Gfx; } //Cache 3's load function void SPC7110Grab(char* dirname) { char temp_path[PATH_MAX]; int i = 0; decompack = (Pack7110*)malloc(sizeof(Pack7110)); #if !defined(_XBOX) && !defined(VITA) getcwd(temp_path, PATH_MAX); #endif int32_t buffer_size = 1024 * 1024 * cacheMegs; //*some setting memset(decompack, 0, sizeof(Pack7110)); #if !defined(_XBOX) && !defined(VITA) if (-1 == chdir(dirname)) S9xMessage(0, 0, "Graphics Pack not found!"); #endif #ifndef _XBOX Load7110Index("index.bin"); #else // D:\\ is always app.path in Xbox Load7110Index("d:\\index.bin"); #endif for (i = 0; i < MAX_TABLES; i++) { if (decompack->tableEnts[i].table != 0) { char binname[PATH_MAX]; #ifndef _XBOX sprintf(binname, "%06X.bin", decompack->tableEnts[i].table); #else sprintf(binname, "%s%06X.bin", filename, decompack->tableEnts[i].table); #endif struct stat buf; //add load/no load calculations here if (-1 != stat(binname, &buf)) { if (buf.st_size < buffer_size) decompack->binfiles[i] = (uint8_t*)malloc(buf.st_size); FILE* fp = fopen(binname, "rb"); //use them here if (fp) { if (buf.st_size < buffer_size) { fread(decompack->binfiles[i], buf.st_size, 1, fp); fclose(fp); buffer_size -= buf.st_size; decompack->tableEnts[i].is_file = false; } else { decompack->binfiles[i] = (uint8_t*)fp; decompack->tableEnts[i].is_file = true; } } } } } #if !defined(_XBOX) && !defined(VITA) chdir(temp_path); #endif Copy7110 = &GetPackData; CleanUp7110 = &Drop7110Gfx; } //Cache 1 clean up function void Del7110Gfx() { int i; if (Settings.SPC7110) { Do7110Logging(); } for (i = 0; i < MAX_TABLES; i++) { if (decompack->binfiles[i] != NULL) { free(decompack->binfiles[i]); decompack->binfiles[i] = NULL; } } Settings.SPC7110 = false; Settings.SPC7110RTC = false; if (NULL != decompack) free(decompack); decompack = NULL; CleanUp7110 = NULL; Copy7110 = NULL; } //Cache2 cleanup function void Close7110Gfx() { int i; if (Settings.SPC7110) { Do7110Logging(); } for (i = 0; i < MAX_TABLES; i++) { if (decompack->binfiles[i] != NULL) { fclose((FILE*)decompack->binfiles[i]); decompack->binfiles[i] = NULL; } } Settings.SPC7110 = false; Settings.SPC7110RTC = false; if (NULL != decompack) free(decompack); decompack = NULL; CleanUp7110 = NULL; Copy7110 = NULL; } //cache 3's clean-up code void Drop7110Gfx() { int i; if (Settings.SPC7110) { Do7110Logging(); } for (i = 0; i < MAX_TABLES; i++) { if (decompack->binfiles[i] != NULL) { if (decompack->tableEnts[i].is_file) { fclose((FILE*)decompack->binfiles[i]); decompack->binfiles[i] = NULL; } else { free(decompack->binfiles[i]); decompack->binfiles[i] = NULL; } } } Settings.SPC7110 = false; Settings.SPC7110RTC = false; if (NULL != decompack) free(decompack); decompack = NULL; CleanUp7110 = NULL; Copy7110 = NULL; } //emulate a reset. void S9xSpc7110Reset() { s7r.reg4800 = 0; s7r.reg4801 = 0; s7r.reg4802 = 0; s7r.reg4803 = 0; s7r.reg4804 = 0; s7r.reg4805 = 0; s7r.reg4806 = 0; s7r.reg4807 = 0; s7r.reg4808 = 0; s7r.reg4809 = 0; s7r.reg480A = 0; s7r.reg480B = 0; s7r.reg480C = 0; s7r.reg4811 = 0; s7r.reg4812 = 0; s7r.reg4813 = 0; s7r.reg4814 = 0; s7r.reg4815 = 0; s7r.reg4816 = 0; s7r.reg4817 = 0; s7r.reg4818 = 0; s7r.reg4820 = 0; s7r.reg4821 = 0; s7r.reg4822 = 0; s7r.reg4823 = 0; s7r.reg4824 = 0; s7r.reg4825 = 0; s7r.reg4826 = 0; s7r.reg4827 = 0; s7r.reg4828 = 0; s7r.reg4829 = 0; s7r.reg482A = 0; s7r.reg482B = 0; s7r.reg482C = 0; s7r.reg482D = 0; s7r.reg482E = 0; s7r.reg482F = 0; s7r.reg4830 = 0; s7r.reg4831 = 0; s7r.reg4832 = 1; s7r.reg4833 = 2; s7r.reg4834 = 0; s7r.reg4840 = 0; s7r.reg4841 = 0; s7r.reg4842 = 0; s7r.written = 0; s7r.offset_add = 0; s7r.AlignBy = 1; s7r.bank50Internal = 0; memset(s7r.bank50, 0x00, DECOMP_BUFFER_SIZE); } //outputs a cumulative log for the game. //there's nothing really weird here, just //reading the old log, and writing a new one. //note the logs are explicitly little-endian, not host byte order. void Do7110Logging() { uint8_t ent_temp; FILE* flog; int entries = 0; if (Settings.SPC7110) { //flush last read into logging (*Copy7110)(); if (!strncmp((char*)&Memory.ROM [0xffc0], "SUPER POWER LEAG 4 ", 21)) { #ifdef _XBOX flog = fopen("T:\\spl4-sp7.dat", "rb"); #else flog = fopen("spl4-sp7.dat", "rb"); #endif } else if (!strncmp((char*)&Memory.ROM [0xffc0], "MOMOTETSU HAPPY ", 21)) { #ifdef _XBOX flog = fopen("T:\\smht-sp7.dat", "rb"); #else flog = fopen("smht-sp7.dat", "rb"); #endif } else if (!strncmp((char*)&Memory.ROM [0xffc0], "HU TENGAI MAKYO ZERO ", 21)) { #ifdef _XBOX flog = fopen("T:\\feoezsp7.dat", "rb"); #else flog = fopen("feoezsp7.dat", "rb"); #endif } else if (!strncmp((char*)&Memory.ROM [0xffc0], "JUMP TENGAIMAKYO ZERO", 21)) { #ifdef _XBOX flog = fopen("T:\\sjumpsp7.dat", "rb"); #else flog = fopen("sjumpsp7.dat", "rb"); #endif } else { #ifdef _XBOX flog = fopen("T:\\misc-sp7.dat", "rb"); #else flog = fopen("misc-sp7.dat", "rb"); #endif } if (flog) { uint8_t buffer[8]; int table = 0; uint16_t offset = 0; uint16_t length = 0; fseek(flog, 35, 0); int f_len; //do while (1) { int i = 0; Data7110* log = NULL; f_len = fread(buffer, 1, 8, flog); if (f_len < 8) break; table = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16); offset = buffer[6] | (buffer[7] << 8); length = buffer[4] | (buffer[5] << 8); while (i < MAX_TABLES && log == NULL) { if (decompack->tableEnts[i].table == table) { log = &(decompack->tableEnts[i].location[(buffer[3])]); if ((log->used_offset + log->used_len) < (offset + length)) { log->used_offset = offset; log->used_len = length; } } i++; } } //while(!feof(flog)); fclose(flog); } if (!strncmp((char*)&Memory.ROM [0xffc0], "SUPER POWER LEAG 4 ", 21)) { #ifdef _XBOX // cwd could be the dvd-rom, so write to T:\\ which is storage region for each title flog = fopen("T:\\spl4-sp7.dat", "wb"); #else flog = fopen("spl4-sp7.dat", "wb"); #endif } else if (!strncmp((char*)&Memory.ROM [0xffc0], "MOMOTETSU HAPPY ", 21)) { #ifdef _XBOX flog = fopen("T:\\smht-sp7.dat", "wb"); #else flog = fopen("smht-sp7.dat", "wb"); #endif } else if (!strncmp((char*)&Memory.ROM [0xffc0], "HU TENGAI MAKYO ZERO ", 21)) { #ifdef _XBOX flog = fopen("T:\\feoezsp7.dat", "wb"); #else flog = fopen("feoezsp7.dat", "wb"); #endif } else if (!strncmp((char*)&Memory.ROM [0xffc0], "JUMP TENGAIMAKYO ZERO", 21)) { #ifdef _XBOX flog = fopen("T:\\sjumpsp7.dat", "wb"); #else flog = fopen("sjumpsp7.dat", "wb"); #endif } else { #ifdef _XBOX flog = fopen("T:\\misc-sp7.dat", "wb"); #else flog = fopen("misc-sp7.dat", "wb"); #endif } //count entries if (flog) { int j = 0; int temp = 0; for (j = 0; j < MAX_TABLES; j++) { int k; for (k = 0; k < 256; k++) { if (decompack->tableEnts[j].location[k].used_len != 0) entries++; } } ent_temp = entries & 0xFF; fwrite(&ent_temp, 1, 1, flog); ent_temp = (entries >> 8) & 0xFF; fwrite(&ent_temp, 1, 1, flog); ent_temp = (entries >> 16) & 0xFF; fwrite(&ent_temp, 1, 1, flog); ent_temp = (entries >> 24) & 0xFF; fwrite(&ent_temp, 1, 1, flog); fwrite(&temp, 1, 4, flog); fwrite(&temp, 1, 4, flog); fwrite(&temp, 1, 4, flog); fwrite(&temp, 1, 4, flog); fwrite(&temp, 1, 4, flog); fwrite(&temp, 1, 4, flog); fwrite(&temp, 1, 4, flog); ent_temp = 0; fwrite(&ent_temp, 1, 1, flog); ent_temp = 0; fwrite(&ent_temp, 1, 1, flog); ent_temp = 0; fwrite(&ent_temp, 1, 1, flog); for (j = 0; j < MAX_TABLES; j++) { int k; for (k = 0; k < 256; k++) { if (decompack->tableEnts[j].location[k].used_len != 0) { ent_temp = decompack->tableEnts[j].table & 0xFF; fwrite(&ent_temp, 1, 1, flog); //801 ent_temp = (decompack->tableEnts[j].table >> 8) & 0xFF;; fwrite(&ent_temp, 1, 1, flog); //802 ent_temp = (decompack->tableEnts[j].table >> 16) & 0xFF;; fwrite(&ent_temp, 1, 1, flog); //803 ent_temp = k & 0xFF; fwrite(&ent_temp, 1, 1, flog); //804 ent_temp = decompack->tableEnts[j].location[k].used_len & 0xFF; fwrite(&ent_temp, 1, 1, flog); //lsb of ent_temp = (decompack->tableEnts[j].location[k].used_len >> 8) & 0xFF; fwrite(&ent_temp, 1, 1, flog); //msb of ent_temp = (decompack->tableEnts[j].location[k].used_offset) & 0xFF; fwrite(&ent_temp, 1, 1, flog); //lsb of ent_temp = (decompack->tableEnts[j].location[k].used_offset >> 8) & 0xFF; fwrite(&ent_temp, 1, 1, flog); //msb of } } } fwrite(&temp, 1, 4, flog); fwrite(&temp, 1, 4, flog); fclose(flog); } } } bool S9xSaveSPC7110RTC(S7RTC* rtc_f9) { FILE* fp; if ((fp = fopen(S9xGetFilename("rtc"), "wb")) == NULL) return (false); int i = 0; uint8_t temp = 0; for (i = 0; i < 16; i++) fwrite(&rtc_f9->reg[i], 1, 1, fp); temp = rtc_f9->index & 0x00FF; fwrite(&temp, 1, 1, fp); temp = (rtc_f9->index) >> 8; fwrite(&temp, 1, 1, fp); temp = (uint8_t)rtc_f9->control; fwrite(&temp, 1, 1, fp); temp = (uint8_t)rtc_f9->init; fwrite(&temp, 1, 1, fp); temp = rtc_f9->last_used & 0x00FF; fwrite(&temp, 1, 1, fp); temp = (rtc_f9->last_used >> 8) & 0x00FF; fwrite(&temp, 1, 1, fp); temp = (rtc_f9->last_used >> 16) & 0x00FF; fwrite(&temp, 1, 1, fp); temp = (rtc_f9->last_used >> 24) & 0x00FF;; fwrite(&temp, 1, 1, fp); fclose(fp); return (true); } bool S9xLoadSPC7110RTC(S7RTC* rtc_f9) { FILE* fp; if ((fp = fopen(S9xGetFilename("rtc"), "rb")) == NULL) return (false); int i; for (i = 0; i < 16; i++) fread(&(rtc_f9->reg[i]), 1, 1, fp); uint8_t temp = 0; fread(&temp, 1, 1, fp); rtc_f9->index = temp; fread(&temp, 1, 1, fp); rtc_f9->index |= (temp << 8); fread(&rtc_f9->control, 1, 1, fp); fread(&rtc_f9->init, 1, 1, fp); fread(&temp, 1, 1, fp); rtc_f9->last_used = temp; fread(&temp, 1, 1, fp); rtc_f9->last_used |= (temp << 8); fread(&temp, 1, 1, fp); rtc_f9->last_used |= (temp << 16); fread(&temp, 1, 1, fp); rtc_f9->last_used |= (temp << 24); fclose(fp); return (true); }