diff --git a/README.md b/README.md index 534ea35..d3c4f8f 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Wii/GameCube. * Artwork (artwork, cover or screenshot) support * Famicom 3D System support * IPS/UPS automatic patching support -* NES Compatibility Based on FCEUX 2.6.4+ (git def5768) +* NES Compatibility Based on FCEUX 2.6.6+ (git 2b8f6e7) * Open Source! @@ -57,6 +57,7 @@ Wii/GameCube. [3.5.5] +* Updated to the latest FCEUX 2.6.6+ (git 2b8f6e7) * Compiled with latest devkitPPC/libogc2 * Added Digital Prime (FBX) palette diff --git a/source/cheatmgr.cpp b/source/cheatmgr.cpp index 0db7095..facdd41 100644 --- a/source/cheatmgr.cpp +++ b/source/cheatmgr.cpp @@ -74,29 +74,30 @@ static int LoadCheats (int length) if(doc) { - char *neo=&tbuf[4+2+2+1+1+1]; - if(sscanf(tbuf,"%04x%*[:]%02x%*[:]%02x",&addr,&val,&compare)!=3) + char *neo = &tbuf[4+2+2+1+1+1]; + if(sscanf(tbuf, "%04x%*[:]%02x%*[:]%02x", &addr, &val, &compare) != 3) continue; namebuf=(char *)malloc(strlen(neo)+1); - strcpy(namebuf,neo); + strcpy(namebuf, neo); } else { - char *neo=&tbuf[4+2+1+1]; - if(sscanf(tbuf,"%04x%*[:]%02x",&addr,&val)!=2) + char *neo = &tbuf[4+2+1+1]; + if(sscanf(tbuf, "%04x%*[:]%02x", &addr, &val) != 2) continue; namebuf=(char *)malloc(strlen(neo)+1); - strcpy(namebuf,neo); + strcpy(namebuf, neo); } - for(x=0;x<(int)strlen(namebuf);x++) + for(x = 0; x < (int)strlen(namebuf); x++) { - if(namebuf[x]==10 || namebuf[x]==13) + if(namebuf[x] == 10 || namebuf[x] == 13) { - namebuf[x]=0; + namebuf[x] = 0; break; } - else if(namebuf[x]<0x20) namebuf[x]=' '; + else if(namebuf[x] > 0x00 && namebuf[x] < 0x20) + namebuf[x] = 0x20; } AddCheatEntry(namebuf,addr,val,doc?compare:-1,status,type); diff --git a/source/fceugx.cpp b/source/fceugx.cpp index b94addd..d058d4b 100644 --- a/source/fceugx.cpp +++ b/source/fceugx.cpp @@ -79,6 +79,7 @@ char appPath[1024] = { 0 }; int frameskip = 0; int turbomode = 0; unsigned char * nesrom = NULL; +int eoptions=0; /**************************************************************************** * Shutdown / Reboot / Exit diff --git a/source/fceultra/asm.cpp b/source/fceultra/asm.cpp deleted file mode 100644 index 0aa2a61..0000000 --- a/source/fceultra/asm.cpp +++ /dev/null @@ -1,529 +0,0 @@ -/// \file -/// \brief 6502 assembler and disassembler - -#include "types.h" -#include "utils/xstring.h" -#include "debug.h" -#include "asm.h" -#include "x6502.h" - -#include -#include -#include -///assembles the string to an instruction located at addr, storing opcodes in output buffer -int Assemble(unsigned char *output, int addr, char *str) { - //unsigned char opcode[3] = { 0,0,0 }; - output[0] = output[1] = output[2] = 0; - char astr[128],ins[4]; - int len = strlen(str); - if ((!len) || (len > 127)) return 1; - - strcpy(astr,str); - str_ucase(astr); - sscanf(astr,"%3s",ins); //get instruction - if (strlen(ins) != 3) return 1; - strcpy(astr,strstr(astr,ins)+3); //heheh, this is probably a bad idea, but let's do it anyway! - if ((astr[0] != ' ') && (astr[0] != 0)) return 1; - - //remove all whitespace - str_strip(astr,STRIP_SP|STRIP_TAB|STRIP_CR|STRIP_LF); - - //repair syntax - chr_replace(astr,'[','('); //brackets - chr_replace(astr,']',')'); - chr_replace(astr,'{','('); - chr_replace(astr,'}',')'); - chr_replace(astr,';',0); //comments - str_replace(astr,"0X","$"); //miscellaneous - - //This does the following: - // 1) Sets opcode[0] on success, else returns 1. - // 2) Parses text in *astr to build the rest of the assembled - // data in 'opcode', else returns 1 on error. - - if (!strlen(astr)) { - //Implied instructions - if (!strcmp(ins,"BRK")) output[0] = 0x00; - else if (!strcmp(ins,"PHP")) output[0] = 0x08; - else if (!strcmp(ins,"ASL")) output[0] = 0x0A; - else if (!strcmp(ins,"CLC")) output[0] = 0x18; - else if (!strcmp(ins,"PLP")) output[0] = 0x28; - else if (!strcmp(ins,"ROL")) output[0] = 0x2A; - else if (!strcmp(ins,"SEC")) output[0] = 0x38; - else if (!strcmp(ins,"RTI")) output[0] = 0x40; - else if (!strcmp(ins,"PHA")) output[0] = 0x48; - else if (!strcmp(ins,"LSR")) output[0] = 0x4A; - else if (!strcmp(ins,"CLI")) output[0] = 0x58; - else if (!strcmp(ins,"RTS")) output[0] = 0x60; - else if (!strcmp(ins,"PLA")) output[0] = 0x68; - else if (!strcmp(ins,"ROR")) output[0] = 0x6A; - else if (!strcmp(ins,"SEI")) output[0] = 0x78; - else if (!strcmp(ins,"DEY")) output[0] = 0x88; - else if (!strcmp(ins,"TXA")) output[0] = 0x8A; - else if (!strcmp(ins,"TYA")) output[0] = 0x98; - else if (!strcmp(ins,"TXS")) output[0] = 0x9A; - else if (!strcmp(ins,"TAY")) output[0] = 0xA8; - else if (!strcmp(ins,"TAX")) output[0] = 0xAA; - else if (!strcmp(ins,"CLV")) output[0] = 0xB8; - else if (!strcmp(ins,"TSX")) output[0] = 0xBA; - else if (!strcmp(ins,"INY")) output[0] = 0xC8; - else if (!strcmp(ins,"DEX")) output[0] = 0xCA; - else if (!strcmp(ins,"CLD")) output[0] = 0xD8; - else if (!strcmp(ins,"INX")) output[0] = 0xE8; - else if (!strcmp(ins,"NOP")) output[0] = 0xEA; - else if (!strcmp(ins,"SED")) output[0] = 0xF8; - else return 1; - } - else { - //Instructions with Operands - if (!strcmp(ins,"ORA")) output[0] = 0x01; - else if (!strcmp(ins,"ASL")) output[0] = 0x06; - else if (!strcmp(ins,"BPL")) output[0] = 0x10; - else if (!strcmp(ins,"JSR")) output[0] = 0x20; - else if (!strcmp(ins,"AND")) output[0] = 0x21; - else if (!strcmp(ins,"BIT")) output[0] = 0x24; - else if (!strcmp(ins,"ROL")) output[0] = 0x26; - else if (!strcmp(ins,"BMI")) output[0] = 0x30; - else if (!strcmp(ins,"EOR")) output[0] = 0x41; - else if (!strcmp(ins,"LSR")) output[0] = 0x46; - else if (!strcmp(ins,"JMP")) output[0] = 0x4C; - else if (!strcmp(ins,"BVC")) output[0] = 0x50; - else if (!strcmp(ins,"ADC")) output[0] = 0x61; - else if (!strcmp(ins,"ROR")) output[0] = 0x66; - else if (!strcmp(ins,"BVS")) output[0] = 0x70; - else if (!strcmp(ins,"STA")) output[0] = 0x81; - else if (!strcmp(ins,"STY")) output[0] = 0x84; - else if (!strcmp(ins,"STX")) output[0] = 0x86; - else if (!strcmp(ins,"BCC")) output[0] = 0x90; - else if (!strcmp(ins,"LDY")) output[0] = 0xA0; - else if (!strcmp(ins,"LDA")) output[0] = 0xA1; - else if (!strcmp(ins,"LDX")) output[0] = 0xA2; - else if (!strcmp(ins,"BCS")) output[0] = 0xB0; - else if (!strcmp(ins,"CPY")) output[0] = 0xC0; - else if (!strcmp(ins,"CMP")) output[0] = 0xC1; - else if (!strcmp(ins,"DEC")) output[0] = 0xC6; - else if (!strcmp(ins,"BNE")) output[0] = 0xD0; - else if (!strcmp(ins,"CPX")) output[0] = 0xE0; - else if (!strcmp(ins,"SBC")) output[0] = 0xE1; - else if (!strcmp(ins,"INC")) output[0] = 0xE6; - else if (!strcmp(ins,"BEQ")) output[0] = 0xF0; - else return 1; - - { - //Parse Operands - // It's not the sexiest thing ever, but it works well enough! - - //TODO: - // Add branches. - // Fix certain instructions. (Setting bits is not 100% perfect.) - // Fix instruction/operand matching. (Instructions like "jmp ($94),Y" are no good!) - // Optimizations? - int tmpint; - char tmpchr,tmpstr[20]; - - if (sscanf(astr,"#$%2X%c",&tmpint,&tmpchr) == 1) { //#Immediate - switch (output[0]) { - case 0x20: case 0x4C: //Jumps - case 0x10: case 0x30: case 0x50: case 0x70: //Branches - case 0x90: case 0xB0: case 0xD0: case 0xF0: - case 0x06: case 0x24: case 0x26: case 0x46: //Other instructions incapable of #Immediate - case 0x66: case 0x81: case 0x84: case 0x86: - case 0xC6: case 0xE6: - return 1; - default: - //cheap hack for certain instructions - switch (output[0]) { - case 0xA0: case 0xA2: case 0xC0: case 0xE0: - break; - default: - output[0] |= 0x08; - break; - } - output[1] = tmpint; - break; - } - } - else if (sscanf(astr,"$%4X%c",&tmpint,&tmpchr) == 1) { //Absolute, Zero Page, Branch, or Jump - switch (output[0]) { - case 0x20: case 0x4C: //Jumps - output[1] = (tmpint & 0xFF); - output[2] = (tmpint >> 8); - break; - case 0x10: case 0x30: case 0x50: case 0x70: //Branches - case 0x90: case 0xB0: case 0xD0: case 0xF0: - tmpint -= (addr+2); - if ((tmpint < -128) || (tmpint > 127)) return 1; - output[1] = (tmpint & 0xFF); - break; - //return 1; //FIX ME - default: - if (tmpint > 0xFF) { //Absolute - output[0] |= 0x0C; - output[1] = (tmpint & 0xFF); - output[2] = (tmpint >> 8); - } - else { //Zero Page - output[0] |= 0x04; - output[1] = (tmpint & 0xFF); - } - break; - } - } - else if (sscanf(astr,"$%4X%s",&tmpint,tmpstr) == 2) { //Absolute,X, Zero Page,X, Absolute,Y or Zero Page,Y - if (!strcmp(tmpstr,",X")) { //Absolute,X or Zero Page,X - switch (output[0]) { - case 0x20: case 0x4C: //Jumps - case 0x10: case 0x30: case 0x50: case 0x70: //Branches - case 0x90: case 0xB0: case 0xD0: case 0xF0: - case 0x24: case 0x86: case 0xA2: case 0xC0: //Other instructions incapable of Absolute,X or Zero Page,X - case 0xE0: - return 1; - default: - if (tmpint > 0xFF) { //Absolute - if (output[0] == 0x84) return 1; //No STY Absolute,X! - output[0] |= 0x1C; - output[1] = (tmpint & 0xFF); - output[2] = (tmpint >> 8); - } - else { //Zero Page - output[0] |= 0x14; - output[1] = (tmpint & 0xFF); - } - break; - } - } - else if (!strcmp(tmpstr,",Y")) { //Absolute,Y or Zero Page,Y - switch (output[0]) { - case 0x20: case 0x4C: //Jumps - case 0x10: case 0x30: case 0x50: case 0x70: //Branches - case 0x90: case 0xB0: case 0xD0: case 0xF0: - case 0x06: case 0x24: case 0x26: case 0x46: //Other instructions incapable of Absolute,Y or Zero Page,Y - case 0x66: case 0x84: case 0x86: case 0xA0: - case 0xC0: case 0xC6: case 0xE0: case 0xE6: - return 1; - case 0xA2: //cheap hack for LDX - output[0] |= 0x04; - default: - if (tmpint > 0xFF) { //Absolute - if (output[0] == 0x86) return 1; //No STX Absolute,Y! - output[0] |= 0x18; - output[1] = (tmpint & 0xFF); - output[2] = (tmpint >> 8); - } - else { //Zero Page - if ((output[0] != 0x86) && (output[0] != 0xA2)) return 1; //only STX and LDX Absolute,Y! - output[0] |= 0x10; - output[1] = (tmpint & 0xFF); - } - break; - } - } - else return 1; - } - else if (sscanf(astr,"($%4X%s",&tmpint,tmpstr) == 2) { //Jump (Indirect), (Indirect,X) or (Indirect),Y - switch (output[0]) { - case 0x20: //Jumps - case 0x10: case 0x30: case 0x50: case 0x70: //Branches - case 0x90: case 0xB0: case 0xD0: case 0xF0: - case 0x06: case 0x24: case 0x26: case 0x46: //Other instructions incapable of Jump (Indirect), (Indirect,X) or (Indirect),Y - case 0x66: case 0x84: case 0x86: case 0xA0: - case 0xA2: case 0xC0: case 0xC6: case 0xE0: - case 0xE6: - return 1; - default: - if ((!strcmp(tmpstr,")")) && (output[0] == 0x4C)) { //Jump (Indirect) - output[0] = 0x6C; - output[1] = (tmpint & 0xFF); - output[2] = (tmpint >> 8); - } - else if ((!strcmp(tmpstr,",X)")) && (tmpint <= 0xFF) && (output[0] != 0x4C)) { //(Indirect,X) - output[1] = (tmpint & 0xFF); - } - else if ((!strcmp(tmpstr,"),Y")) && (tmpint <= 0xFF) && (output[0] != 0x4C)) { //(Indirect),Y - output[0] |= 0x10; - output[1] = (tmpint & 0xFF); - } - else return 1; - break; - } - } - else return 1; - } - } - - return 0; -} - -///disassembles the opcodes in the buffer assuming the provided address. Uses GetMem() and 6502 current registers to query referenced values. returns a static string buffer. -char *Disassemble(int addr, uint8 *opcode) { - static char str[64]={0},chr[5]={0}; - uint16 tmp,tmp2; - - //these may be replaced later with passed-in values to make a lighter-weight disassembly mode that may not query the referenced values - #define RX (X.X) - #define RY (X.Y) - - switch (opcode[0]) { - #define relative(a) { \ - if (((a)=opcode[1])&0x80) (a) = addr-(((a)-1)^0xFF); \ - else (a)+=addr; \ - } - #define absolute(a) { \ - (a) = opcode[1] | opcode[2]<<8; \ - } - #define zpIndex(a,i) { \ - (a) = (opcode[1]+(i))&0xFF; \ - } - #define indirectX(a) { \ - (a) = (opcode[1]+RX)&0xFF; \ - (a) = GetMem((a)) | (GetMem(((a)+1)&0xff))<<8; \ - } - #define indirectY(a) { \ - (a) = GetMem(opcode[1]) | (GetMem((opcode[1]+1)&0xff))<<8; \ - (a) += RY; \ - } - - - #ifdef BRK_3BYTE_HACK - case 0x00: - sprintf(str,"BRK %02X %02X", opcode[1], opcode[2]); - break; - #else - case 0x00: strcpy(str,"BRK"); break; - #endif - - //odd, 1-byte opcodes - case 0x08: strcpy(str,"PHP"); break; - case 0x0A: strcpy(str,"ASL"); break; - case 0x18: strcpy(str,"CLC"); break; - case 0x28: strcpy(str,"PLP"); break; - case 0x2A: strcpy(str,"ROL"); break; - case 0x38: strcpy(str,"SEC"); break; - case 0x40: strcpy(str,"RTI"); break; - case 0x48: strcpy(str,"PHA"); break; - case 0x4A: strcpy(str,"LSR"); break; - case 0x58: strcpy(str,"CLI"); break; - case 0x60: strcpy(str,"RTS"); break; - case 0x68: strcpy(str,"PLA"); break; - case 0x6A: strcpy(str,"ROR"); break; - case 0x78: strcpy(str,"SEI"); break; - case 0x88: strcpy(str,"DEY"); break; - case 0x8A: strcpy(str,"TXA"); break; - case 0x98: strcpy(str,"TYA"); break; - case 0x9A: strcpy(str,"TXS"); break; - case 0xA8: strcpy(str,"TAY"); break; - case 0xAA: strcpy(str,"TAX"); break; - case 0xB8: strcpy(str,"CLV"); break; - case 0xBA: strcpy(str,"TSX"); break; - case 0xC8: strcpy(str,"INY"); break; - case 0xCA: strcpy(str,"DEX"); break; - case 0xD8: strcpy(str,"CLD"); break; - case 0xE8: strcpy(str,"INX"); break; - case 0xEA: strcpy(str,"NOP"); break; - case 0xF8: strcpy(str,"SED"); break; - - //(Indirect,X) - case 0x01: strcpy(chr,"ORA"); goto _indirectx; - case 0x21: strcpy(chr,"AND"); goto _indirectx; - case 0x41: strcpy(chr,"EOR"); goto _indirectx; - case 0x61: strcpy(chr,"ADC"); goto _indirectx; - case 0x81: strcpy(chr,"STA"); goto _indirectx; - case 0xA1: strcpy(chr,"LDA"); goto _indirectx; - case 0xC1: strcpy(chr,"CMP"); goto _indirectx; - case 0xE1: strcpy(chr,"SBC"); goto _indirectx; - _indirectx: - indirectX(tmp); - sprintf(str,"%s ($%02X,X) @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); - break; - - //Zero Page - case 0x05: strcpy(chr,"ORA"); goto _zeropage; - case 0x06: strcpy(chr,"ASL"); goto _zeropage; - case 0x24: strcpy(chr,"BIT"); goto _zeropage; - case 0x25: strcpy(chr,"AND"); goto _zeropage; - case 0x26: strcpy(chr,"ROL"); goto _zeropage; - case 0x45: strcpy(chr,"EOR"); goto _zeropage; - case 0x46: strcpy(chr,"LSR"); goto _zeropage; - case 0x65: strcpy(chr,"ADC"); goto _zeropage; - case 0x66: strcpy(chr,"ROR"); goto _zeropage; - case 0x84: strcpy(chr,"STY"); goto _zeropage; - case 0x85: strcpy(chr,"STA"); goto _zeropage; - case 0x86: strcpy(chr,"STX"); goto _zeropage; - case 0xA4: strcpy(chr,"LDY"); goto _zeropage; - case 0xA5: strcpy(chr,"LDA"); goto _zeropage; - case 0xA6: strcpy(chr,"LDX"); goto _zeropage; - case 0xC4: strcpy(chr,"CPY"); goto _zeropage; - case 0xC5: strcpy(chr,"CMP"); goto _zeropage; - case 0xC6: strcpy(chr,"DEC"); goto _zeropage; - case 0xE4: strcpy(chr,"CPX"); goto _zeropage; - case 0xE5: strcpy(chr,"SBC"); goto _zeropage; - case 0xE6: strcpy(chr,"INC"); goto _zeropage; - _zeropage: - // ################################## Start of SP CODE ########################### - // Change width to %04X // don't! - sprintf(str,"%s $%02X = #$%02X", chr,opcode[1],GetMem(opcode[1])); - // ################################## End of SP CODE ########################### - break; - - //#Immediate - case 0x09: strcpy(chr,"ORA"); goto _immediate; - case 0x29: strcpy(chr,"AND"); goto _immediate; - case 0x49: strcpy(chr,"EOR"); goto _immediate; - case 0x69: strcpy(chr,"ADC"); goto _immediate; - //case 0x89: strcpy(chr,"STA"); goto _immediate; //baka, no STA #imm!! - case 0xA0: strcpy(chr,"LDY"); goto _immediate; - case 0xA2: strcpy(chr,"LDX"); goto _immediate; - case 0xA9: strcpy(chr,"LDA"); goto _immediate; - case 0xC0: strcpy(chr,"CPY"); goto _immediate; - case 0xC9: strcpy(chr,"CMP"); goto _immediate; - case 0xE0: strcpy(chr,"CPX"); goto _immediate; - case 0xE9: strcpy(chr,"SBC"); goto _immediate; - _immediate: - sprintf(str,"%s #$%02X", chr,opcode[1]); - break; - - //Absolute - case 0x0D: strcpy(chr,"ORA"); goto _absolute; - case 0x0E: strcpy(chr,"ASL"); goto _absolute; - case 0x2C: strcpy(chr,"BIT"); goto _absolute; - case 0x2D: strcpy(chr,"AND"); goto _absolute; - case 0x2E: strcpy(chr,"ROL"); goto _absolute; - case 0x4D: strcpy(chr,"EOR"); goto _absolute; - case 0x4E: strcpy(chr,"LSR"); goto _absolute; - case 0x6D: strcpy(chr,"ADC"); goto _absolute; - case 0x6E: strcpy(chr,"ROR"); goto _absolute; - case 0x8C: strcpy(chr,"STY"); goto _absolute; - case 0x8D: strcpy(chr,"STA"); goto _absolute; - case 0x8E: strcpy(chr,"STX"); goto _absolute; - case 0xAC: strcpy(chr,"LDY"); goto _absolute; - case 0xAD: strcpy(chr,"LDA"); goto _absolute; - case 0xAE: strcpy(chr,"LDX"); goto _absolute; - case 0xCC: strcpy(chr,"CPY"); goto _absolute; - case 0xCD: strcpy(chr,"CMP"); goto _absolute; - case 0xCE: strcpy(chr,"DEC"); goto _absolute; - case 0xEC: strcpy(chr,"CPX"); goto _absolute; - case 0xED: strcpy(chr,"SBC"); goto _absolute; - case 0xEE: strcpy(chr,"INC"); goto _absolute; - _absolute: - absolute(tmp); - sprintf(str,"%s $%04X = #$%02X", chr,tmp,GetMem(tmp)); - break; - - //branches - case 0x10: strcpy(chr,"BPL"); goto _branch; - case 0x30: strcpy(chr,"BMI"); goto _branch; - case 0x50: strcpy(chr,"BVC"); goto _branch; - case 0x70: strcpy(chr,"BVS"); goto _branch; - case 0x90: strcpy(chr,"BCC"); goto _branch; - case 0xB0: strcpy(chr,"BCS"); goto _branch; - case 0xD0: strcpy(chr,"BNE"); goto _branch; - case 0xF0: strcpy(chr,"BEQ"); goto _branch; - _branch: - relative(tmp); - sprintf(str,"%s $%04X", chr,tmp); - break; - - //(Indirect),Y - case 0x11: strcpy(chr,"ORA"); goto _indirecty; - case 0x31: strcpy(chr,"AND"); goto _indirecty; - case 0x51: strcpy(chr,"EOR"); goto _indirecty; - case 0x71: strcpy(chr,"ADC"); goto _indirecty; - case 0x91: strcpy(chr,"STA"); goto _indirecty; - case 0xB1: strcpy(chr,"LDA"); goto _indirecty; - case 0xD1: strcpy(chr,"CMP"); goto _indirecty; - case 0xF1: strcpy(chr,"SBC"); goto _indirecty; - _indirecty: - indirectY(tmp); - sprintf(str,"%s ($%02X),Y @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); - break; - - //Zero Page,X - case 0x15: strcpy(chr,"ORA"); goto _zeropagex; - case 0x16: strcpy(chr,"ASL"); goto _zeropagex; - case 0x35: strcpy(chr,"AND"); goto _zeropagex; - case 0x36: strcpy(chr,"ROL"); goto _zeropagex; - case 0x55: strcpy(chr,"EOR"); goto _zeropagex; - case 0x56: strcpy(chr,"LSR"); goto _zeropagex; - case 0x75: strcpy(chr,"ADC"); goto _zeropagex; - case 0x76: strcpy(chr,"ROR"); goto _zeropagex; - case 0x94: strcpy(chr,"STY"); goto _zeropagex; - case 0x95: strcpy(chr,"STA"); goto _zeropagex; - case 0xB4: strcpy(chr,"LDY"); goto _zeropagex; - case 0xB5: strcpy(chr,"LDA"); goto _zeropagex; - case 0xD5: strcpy(chr,"CMP"); goto _zeropagex; - case 0xD6: strcpy(chr,"DEC"); goto _zeropagex; - case 0xF5: strcpy(chr,"SBC"); goto _zeropagex; - case 0xF6: strcpy(chr,"INC"); goto _zeropagex; - _zeropagex: - zpIndex(tmp,RX); - // ################################## Start of SP CODE ########################### - // Change width to %04X // don't! - sprintf(str,"%s $%02X,X @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); - // ################################## End of SP CODE ########################### - break; - - //Absolute,Y - case 0x19: strcpy(chr,"ORA"); goto _absolutey; - case 0x39: strcpy(chr,"AND"); goto _absolutey; - case 0x59: strcpy(chr,"EOR"); goto _absolutey; - case 0x79: strcpy(chr,"ADC"); goto _absolutey; - case 0x99: strcpy(chr,"STA"); goto _absolutey; - case 0xB9: strcpy(chr,"LDA"); goto _absolutey; - case 0xBE: strcpy(chr,"LDX"); goto _absolutey; - case 0xD9: strcpy(chr,"CMP"); goto _absolutey; - case 0xF9: strcpy(chr,"SBC"); goto _absolutey; - _absolutey: - absolute(tmp); - tmp2=(tmp+RY); - sprintf(str,"%s $%04X,Y @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)); - break; - - //Absolute,X - case 0x1D: strcpy(chr,"ORA"); goto _absolutex; - case 0x1E: strcpy(chr,"ASL"); goto _absolutex; - case 0x3D: strcpy(chr,"AND"); goto _absolutex; - case 0x3E: strcpy(chr,"ROL"); goto _absolutex; - case 0x5D: strcpy(chr,"EOR"); goto _absolutex; - case 0x5E: strcpy(chr,"LSR"); goto _absolutex; - case 0x7D: strcpy(chr,"ADC"); goto _absolutex; - case 0x7E: strcpy(chr,"ROR"); goto _absolutex; - case 0x9D: strcpy(chr,"STA"); goto _absolutex; - case 0xBC: strcpy(chr,"LDY"); goto _absolutex; - case 0xBD: strcpy(chr,"LDA"); goto _absolutex; - case 0xDD: strcpy(chr,"CMP"); goto _absolutex; - case 0xDE: strcpy(chr,"DEC"); goto _absolutex; - case 0xFD: strcpy(chr,"SBC"); goto _absolutex; - case 0xFE: strcpy(chr,"INC"); goto _absolutex; - _absolutex: - absolute(tmp); - tmp2=(tmp+RX); - sprintf(str,"%s $%04X,X @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)); - break; - - //jumps - case 0x20: strcpy(chr,"JSR"); goto _jump; - case 0x4C: strcpy(chr,"JMP"); goto _jump; - case 0x6C: absolute(tmp); sprintf(str,"JMP ($%04X) = $%04X", tmp,GetMem(tmp)|GetMem(tmp+1)<<8); break; - _jump: - absolute(tmp); - sprintf(str,"%s $%04X", chr,tmp); - break; - - //Zero Page,Y - case 0x96: strcpy(chr,"STX"); goto _zeropagey; - case 0xB6: strcpy(chr,"LDX"); goto _zeropagey; - _zeropagey: - zpIndex(tmp,RY); - // ################################## Start of SP CODE ########################### - // Change width to %04X // don't! - sprintf(str,"%s $%02X,Y @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); - // ################################## End of SP CODE ########################### - break; - - //UNDEFINED - default: strcpy(str,"ERROR"); break; - - } - - return str; -} diff --git a/source/fceultra/asm.h b/source/fceultra/asm.h deleted file mode 100644 index 5dff9f3..0000000 --- a/source/fceultra/asm.h +++ /dev/null @@ -1,2 +0,0 @@ -int Assemble(unsigned char *output, int addr, char *str); -char *Disassemble(int addr, uint8 *opcode); diff --git a/source/fceultra/boards/121.cpp b/source/fceultra/boards/121.cpp index aa4e5c2..b2cd06b 100644 --- a/source/fceultra/boards/121.cpp +++ b/source/fceultra/boards/121.cpp @@ -48,7 +48,7 @@ static void M121CW(uint32 A, uint8 V) { if (PRGsize[0] == CHRsize[0]) { // A9713 multigame extension hack! setchr1(A, V | ((EXPREGS[3] & 0x80) << 1)); } else { - if ((A & 0x1000) == ((MMC3_cmd & 0x80) << 5)) + if ((A & 0x1000) == static_cast((MMC3_cmd & 0x80) << 5)) setchr1(A, V | 0x100); else setchr1(A, V); diff --git a/source/fceultra/boards/15.cpp b/source/fceultra/boards/15.cpp index 01f55cd..2558fdc 100644 --- a/source/fceultra/boards/15.cpp +++ b/source/fceultra/boards/15.cpp @@ -24,7 +24,7 @@ static uint16 latchea; static uint8 latched; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static SFORMAT StateRegs[] = { { &latchea, 2, "AREG" }, @@ -108,8 +108,7 @@ void Mapper15_Init(CartInfo *info) { WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(WRAM, WRAMSIZE, 0, "WRAM"); AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/151.cpp b/source/fceultra/boards/151.cpp index 2d9335f..109bf3c 100644 --- a/source/fceultra/boards/151.cpp +++ b/source/fceultra/boards/151.cpp @@ -1,7 +1,7 @@ -/* FCE Ultra - NES/Famicom Emulator +/* FCEUmm - NES/Famicom Emulator * * Copyright notice for this file: - * Copyright (C) 2012 CaH4e3 + * Copyright (C) 2024 negativeExponent * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,42 +18,48 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +/* NES 2.0 Mapper 471 denotes the Impact Soft IM1 circuit board, used for + * Haratyler (without HG or MP) and Haraforce. It is basically INES Mapper 201 + * with the addition of a scanline IRQ.*/ + #include "mapinc.h" -static uint8 regs[8]; +static uint32 latch; -static SFORMAT StateRegs[] = -{ - { regs, 8, "REGS" }, - { 0 } -}; - -static void Sync(void) { - setprg8(0x8000, regs[0]); - setprg8(0xA000, regs[2]); - setprg8(0xC000, regs[4]); - setprg8(0xE000, ~0); - setchr4(0x0000, regs[6]); - setchr4(0x1000, regs[7]); +static void Sync() { + setprg32(0x8000, latch); + setchr8(latch); } -static DECLFW(M151Write) { - regs[(A >> 12) & 7] = V; +static DECLFW(Write) { + X6502_IRQEnd(FCEU_IQEXT); + latch = A; Sync(); } -static void M151Power(void) { +static void Reset() { + latch = 0; Sync(); +} + +static void Power() { SetReadHandler(0x8000, 0xFFFF, CartBR); - SetWriteHandler(0x8000, 0xFFFF, M151Write); + SetWriteHandler(0x8000, 0xFFFF, Write); + Reset(); } static void StateRestore(int version) { Sync(); } -void Mapper151_Init(CartInfo *info) { - info->Power = M151Power; - GameStateRestore = StateRestore; - AddExState(&StateRegs, ~0, 0, 0); +static void HBHook() { + X6502_IRQBegin(FCEU_IQEXT); +} + +void Mapper471_Init(CartInfo *info) { + info->Power = Power; + info->Reset = Reset; + GameHBIRQHook = HBHook; + GameStateRestore = StateRestore; + AddExState(&latch, sizeof(latch), 0, "LATC"); } diff --git a/source/fceultra/boards/164.cpp b/source/fceultra/boards/164.cpp index 6eb963b..c4344d0 100644 --- a/source/fceultra/boards/164.cpp +++ b/source/fceultra/boards/164.cpp @@ -27,7 +27,7 @@ static uint8 laststrobe, trigger; static uint8 reg[8]; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static writefunc pcmwrite; @@ -123,8 +123,7 @@ void Mapper164_Init(CartInfo *info) { AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } GameStateRestore = StateRestore; @@ -172,8 +171,7 @@ void Mapper163_Init(CartInfo *info) { AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } GameStateRestore = StateRestore; AddExState(&StateRegs, ~0, 0, 0); @@ -223,8 +221,7 @@ void UNLFS304_Init(CartInfo *info) { AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } GameStateRestore = StateRestore; diff --git a/source/fceultra/boards/177.cpp b/source/fceultra/boards/177.cpp index 0416060..9525939 100644 --- a/source/fceultra/boards/177.cpp +++ b/source/fceultra/boards/177.cpp @@ -23,7 +23,7 @@ static uint8 reg; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static SFORMAT StateRegs[] = { @@ -73,8 +73,7 @@ void Mapper177_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/178.cpp b/source/fceultra/boards/178.cpp index d577329..5dc7709 100644 --- a/source/fceultra/boards/178.cpp +++ b/source/fceultra/boards/178.cpp @@ -27,7 +27,7 @@ static uint8 reg[4]; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; // Tennis with VR sensor, very simple behaviour extern void GetMouseData(uint32 (&md)[3]); @@ -192,8 +192,7 @@ void Mapper178_Init(CartInfo *info) { WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(WRAM, WRAMSIZE, 0, "WRAM"); diff --git a/source/fceultra/boards/18.cpp b/source/fceultra/boards/18.cpp index a9ac4b6..844fed1 100644 --- a/source/fceultra/boards/18.cpp +++ b/source/fceultra/boards/18.cpp @@ -16,6 +16,9 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * The Moero!! Pro Yakyuu series have an ADPCM chip with internal ROM, + * used for voice samples (not dumped, so emulation isn't possible) */ #include "mapinc.h" @@ -24,7 +27,7 @@ static uint8 preg[4], creg[8]; static uint8 IRQa, mirr; static int32 IRQCount, IRQLatch; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static SFORMAT StateRegs[] = { @@ -125,8 +128,7 @@ void Mapper18_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/187.cpp b/source/fceultra/boards/187.cpp index 6e5baa2..cb559bc 100644 --- a/source/fceultra/boards/187.cpp +++ b/source/fceultra/boards/187.cpp @@ -22,7 +22,7 @@ #include "mmc3.h" static void M187CW(uint32 A, uint8 V) { - if ((A & 0x1000) == ((MMC3_cmd & 0x80) << 5)) + if ((A & 0x1000) == static_cast((MMC3_cmd & 0x80) << 5)) setchr1(A, V | 0x100); else setchr1(A, V); diff --git a/source/fceultra/boards/218.cpp b/source/fceultra/boards/218.cpp deleted file mode 100644 index 50b6021..0000000 --- a/source/fceultra/boards/218.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* FCE Ultra - NES/Famicom Emulator - * - * Copyright notice for this file: - * Copyright (C) 2020 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "mapinc.h" -#include "../ines.h" - -static uint8 mirror; -static uint8 mask; - -static void M218Power(void) { - setchr8(0); - setprg32(0x8000, 0); - SetReadHandler(0x8000, 0xFFFF, CartBR); -} - -void Mapper218_Init(CartInfo* info) { - if (head.ROM_type & 0x08) - SetupCartMirroring(MI_0 + (head.ROM_type & 0x01), 1, NULL); - SetupCartCHRMapping(0, NTARAM, 2048, 1); - info->Power = M218Power; -} diff --git a/source/fceultra/boards/225.cpp b/source/fceultra/boards/225.cpp index e8237ed..f185737 100644 --- a/source/fceultra/boards/225.cpp +++ b/source/fceultra/boards/225.cpp @@ -1,30 +1,40 @@ /* FCE Ultra - NES/Famicom Emulator - * - * Copyright notice for this file: - * Copyright (C) 2011 CaH4e3 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ +* +* Copyright notice for this file: +* Copyright (C) 2011 CaH4e3 +* Copyright (C) 2019 Libretro Team +* Copyright (C) 2020 +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* PCB-018 board, discrete multigame cart 110-in-1 +* +* Mapper 225 +* Mapper 255 +* +*/ + +/* 2020-2-20 - merge mapper 255, re-implement extra RAM */ #include "mapinc.h" -static uint8 prot[4], prg, mode, chr, mirr; +static uint8 extraRAM[4], prg, mode, chr, mirr; static SFORMAT StateRegs[] = { - { prot, 4, "PROT" }, + { extraRAM, 4, "PROT" }, { &prg, 1, "PRG" }, { &chr, 1, "CHR" }, { &mode, 1, "MODE" }, @@ -36,14 +46,15 @@ static void Sync(void) { if (mode) { setprg16(0x8000, prg); setprg16(0xC000, prg); - } else + } + else setprg32(0x8000, prg >> 1); setchr8(chr); setmirror(mirr ^ 1); } static DECLFW(M225Write) { - uint32 bank = (A >> 14) & 1; + uint8 bank = (A >> 14) & 1; mirr = (A >> 13) & 1; mode = (A >> 12) & 1; chr = (A & 0x3f) | (bank << 6); @@ -52,35 +63,28 @@ static DECLFW(M225Write) { } static DECLFW(M225LoWrite) { - if (A & 0x800) { - prot[A & 0x03] = V; - } + /* e.g. 115-in-1 [p1][!] CRC32 0xb39d30b4 */ + if (A & 0x800) extraRAM[A & 3] = V & 0x0F; } static DECLFR(M225LoRead) { - if (A & 0x800) { - return prot[A & 3] & 0x0F; - } + if (A & 0x800) return extraRAM[A & 3]; return X.DB; } static void M225Power(void) { prg = 0; - chr = 0; mode = 0; - mirr = 0; Sync(); - SetReadHandler(0x5000, 0x5FFF, M225LoRead); - SetWriteHandler(0x5000, 0x5FFF, M225LoWrite); + SetReadHandler(0x5000, 0x5fff, M225LoRead); + SetWriteHandler(0x5000, 0x5fff, M225LoWrite); SetReadHandler(0x8000, 0xFFFF, CartBR); SetWriteHandler(0x8000, 0xFFFF, M225Write); } static void M225Reset(void) { prg = 0; - chr = 0; mode = 0; - mirr = 0; Sync(); } @@ -94,3 +98,7 @@ void Mapper225_Init(CartInfo *info) { GameStateRestore = StateRestore; AddExState(&StateRegs, ~0, 0, 0); } + +void Mapper255_Init(CartInfo *info) { + Mapper225_Init(info); +} \ No newline at end of file diff --git a/source/fceultra/boards/235.cpp b/source/fceultra/boards/235.cpp index 98ce11b..4c7392c 100644 --- a/source/fceultra/boards/235.cpp +++ b/source/fceultra/boards/235.cpp @@ -19,36 +19,79 @@ */ #include "mapinc.h" +#include "../ines.h" + +static uint16 cmdreg = 0; +static uint8 openbus = 0; + +// For carts with extra 128K prg rom (Contra) +static uint8 unrom = 0; +static uint8 unromData = 0; + +static uint32 PRGROMSize = 0; -static uint16 cmdreg; static SFORMAT StateRegs[] = { { &cmdreg, 2, "CREG" }, + { &openbus, 1, "OB" }, + { &unrom, 1, "UROM" }, + { &unromData, 1, "UDTA" }, { 0 } }; static void Sync(void) { - if (cmdreg & 0x400) - setmirror(MI_0); - else - setmirror(((cmdreg >> 13) & 1) ^ 1); - if (cmdreg & 0x800) { - setprg16(0x8000, ((cmdreg & 0x300) >> 3) | ((cmdreg & 0x1F) << 1) | ((cmdreg >> 12) & 1)); - setprg16(0xC000, ((cmdreg & 0x300) >> 3) | ((cmdreg & 0x1F) << 1) | ((cmdreg >> 12) & 1)); - } else - setprg32(0x8000, ((cmdreg & 0x300) >> 4) | (cmdreg & 0x1F)); + if (unrom) { + int PRGPageCount = PRGROMSize / (16 * 1024); + setprg16(0x8000, PRGPageCount & 0xC0 | (unromData & 7)); + setprg16(0xC000, PRGPageCount & 0xC0 | 7); + setmirror(MI_V); + } else { + uint8 bank = ((cmdreg & 0x300) >> 3) | (cmdreg & 0x1F); + if (bank >= (PRGROMSize / (32 * 1024))) { + openbus = 1; + } else { + if (cmdreg & 0x400) + setmirror(MI_0); + else + setmirror(((cmdreg >> 13) & 1) ^ 1); + if (cmdreg & 0x800) { + setprg16(0x8000, (bank << 1) | ((cmdreg >> 12) & 1)); + setprg16(0xC000, (bank << 1) | ((cmdreg >> 12) & 1)); + } else + setprg32(0x8000, bank); + } + } +} + +static DECLFR(M235Read) { + if (openbus) { + openbus = 0; + return X.DB; + } + return CartBR(A); } static DECLFW(M235Write) { cmdreg = A; + unromData = V; + Sync(); +} + +static void M235Reset(void) { + cmdreg = 0; + unromData = 0; + if (PRGROMSize & 0x20000) + unrom = (unrom + 1) & 1; Sync(); } static void M235Power(void) { setchr8(0); SetWriteHandler(0x8000, 0xFFFF, M235Write); - SetReadHandler(0x8000, 0xFFFF, CartBR); + SetReadHandler(0x8000, 0xFFFF, M235Read); cmdreg = 0; + unromData = 0; + unrom = 0; Sync(); } @@ -57,7 +100,11 @@ static void M235Restore(int version) { } void Mapper235_Init(CartInfo *info) { + info->Reset = M235Reset; info->Power = M235Power; GameStateRestore = M235Restore; AddExState(&StateRegs, ~0, 0, 0); + + // needs raw, non-pow2 PRGROM size for comparison + PRGROMSize = head.ROM_size * 16384; } diff --git a/source/fceultra/boards/246.cpp b/source/fceultra/boards/246.cpp index ec069de..3e00f04 100644 --- a/source/fceultra/boards/246.cpp +++ b/source/fceultra/boards/246.cpp @@ -22,7 +22,7 @@ static uint8 regs[8]; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE = 0; static SFORMAT StateRegs[] = { @@ -79,8 +79,7 @@ void Mapper246_Init(CartInfo *info) { AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/252.cpp b/source/fceultra/boards/252.cpp index 9962e08..51de2e9 100644 --- a/source/fceultra/boards/252.cpp +++ b/source/fceultra/boards/252.cpp @@ -23,9 +23,9 @@ static uint8 creg[8], preg[2]; static int32 IRQa, IRQCount, IRQClock, IRQLatch; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static uint8 *CHRRAM = NULL; -static uint32 CHRRAMSIZE; +static uint32 CHRRAMSIZE=0; static SFORMAT StateRegs[] = { @@ -127,8 +127,7 @@ void Mapper252_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } GameStateRestore = StateRestore; AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/253.cpp b/source/fceultra/boards/253.cpp index 3ed9bfe..04bbb09 100644 --- a/source/fceultra/boards/253.cpp +++ b/source/fceultra/boards/253.cpp @@ -23,9 +23,9 @@ static uint8 chrlo[8], chrhi[8], prg[2], mirr, vlock; static int32 IRQa, IRQCount, IRQLatch, IRQClock; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static uint8 *CHRRAM = NULL; -static uint32 CHRRAMSIZE; +static uint32 CHRRAMSIZE=0; static SFORMAT StateRegs[] = { @@ -144,8 +144,7 @@ void Mapper253_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/354.cpp b/source/fceultra/boards/354.cpp new file mode 100644 index 0000000..fcda681 --- /dev/null +++ b/source/fceultra/boards/354.cpp @@ -0,0 +1,98 @@ +/* FCEUmm - NES/Famicom Emulator +* +* Copyright notice for this file: +* Copyright (C) 2022 +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "mapinc.h" + +static uint16 latchAddr; +static uint8 latchData; +static uint8 submapper; + +static SFORMAT StateRegs[] = +{ + { &latchAddr, 2, "ADDR" }, + { &latchData, 1, "DATA" }, + { 0 } +}; + +static void Mapper354_Sync(void) +{ + int prg = latchData & 0x3F | latchAddr << 2 & 0x40 | latchAddr >> 5 & 0x80; + switch (latchAddr & 7) + { + case 0: case 4: + setprg32(0x8000, prg >> 1); + break; + case 1: + setprg16(0x8000, prg); + setprg16(0xC000, prg | 7); + break; + case 2: case 6: + setprg8(0x8000, prg << 1 | latchData >> 7); + setprg8(0xA000, prg << 1 | latchData >> 7); + setprg8(0xC000, prg << 1 | latchData >> 7); + setprg8(0xE000, prg << 1 | latchData >> 7); + break; + case 3: case 7: + setprg16(0x8000, prg); + setprg16(0xC000, prg); + break; + case 5: + setprg8(0x6000, prg << 1 | latchData >> 7); + setprg32(0x8000, prg >> 1 | 3); + break; + } + SetupCartCHRMapping(0, CHRptr[0], CHRsize[0], (latchAddr & 8) ? 0 : 1); + setchr8(0); + setmirror(latchData & 0x40 ? MI_H : MI_V); +} + +static DECLFW(Mapper354_WriteLatch) +{ + latchData = V; + latchAddr = A & 0xFFFF; + Mapper354_Sync(); +} + +static void Mapper354_Reset(void) +{ + latchAddr = latchData = 0; + Mapper354_Sync(); +} + +static void Mapper354_Power(void) +{ + latchAddr = latchData = 0; + SetReadHandler(0x6000, 0xFFFF, CartBR); + SetWriteHandler(submapper == 1 ? 0xE000 : 0xF000, 0xFFFF, Mapper354_WriteLatch); + Mapper354_Sync(); +} + +static void StateRestore(int version) { + Mapper354_Sync(); +} + +void Mapper354_Init(CartInfo *info) +{ + submapper = info->submapper; + info->Power = Mapper354_Power; + info->Reset = Mapper354_Reset; + GameStateRestore = StateRestore; + AddExState(StateRegs, ~0, 0, 0); +} diff --git a/source/fceultra/boards/413.cpp b/source/fceultra/boards/413.cpp new file mode 100644 index 0000000..3eb25cf --- /dev/null +++ b/source/fceultra/boards/413.cpp @@ -0,0 +1,147 @@ +/* FCEUmm - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2024 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mapinc.h" +#include "../ines.h" + +static uint8 reg[4]; +static uint8 IRQCount; +static uint8 IRQReload; +static uint8 IRQa; +static uint8 serialControl; +static uint32 serialAddress; + +static SFORMAT StateRegs[] = { + { reg, 4, "REGS" }, + { &IRQCount, 1, "IRQC" }, + { &IRQReload, 1, "IRQR" }, + { &IRQa, 1, "IRQA" }, + { &serialAddress, 4, "ADDR" }, + { &serialControl, 1, "CTRL" }, + { 0 } +}; + +static void Sync(void) { + setprg4(0x5000, 0x01); + setprg8(0x6000, reg[0]); + + setprg8(0x8000, reg[1]); + setprg8(0xA000, reg[2]); + setprg4(0xD000, 0x07); + setprg8(0xE000, 0x04); + + setchr4(0x0000, reg[3]); + setchr4(0x1000, ~0x02); +} + +static uint64 lreset; +static uint32 laddr; +static DECLFR(M413ReadPCM) { + uint8 ret = X.DB; + if ((A == laddr) && ((timestampbase + timestamp) < (lreset + 4))) { + return ret; + } + if (serialControl & 0x02) { + ret = MiscROM[serialAddress++ & (MiscROM_size - 1)]; + } else { + ret = MiscROM[serialAddress & (MiscROM_size - 1)]; + } + laddr = A; + lreset = timestampbase + timestamp; + return ret; +} + +static DECLFW(M413Write) { + switch (A & 0xF000) { + case 0x8000: + IRQReload = V; + break; + case 0x9000: + IRQCount = 0; + break; + case 0xA000: + case 0xB000: + IRQa = (A & 0x1000) != 0; + if (!IRQa) { + X6502_IRQEnd(FCEU_IQEXT); + } + break; + case 0xC000: + serialAddress = (serialAddress << 1) | (V >> 7); + break; + case 0xD000: + serialControl = V; + break; + case 0xE000: + case 0xF000: + reg[V >> 6] = V & 0x3F; + Sync(); + break; + } +} + +static void M413Power(void) { + serialAddress = 0; + serialControl = 0; + + IRQCount = 0; + IRQReload = 0; + IRQa = 0; + + reg[0] = 0; + reg[1] = 0; + reg[2] = 0; + reg[3] = 0; + + laddr = 0; + lreset = 0; + + Sync(); + + SetReadHandler(0x4800, 0x4FFF, M413ReadPCM); + SetReadHandler(0x5000, 0x7FFF, CartBR); + SetReadHandler(0x8000, 0xBFFF, CartBR); + SetReadHandler(0xC000, 0xCFFF, M413ReadPCM); + SetReadHandler(0xD000, 0xFFFF, CartBR); + + SetWriteHandler(0x8000, 0xFFFF, M413Write); +} + +static void M413IRQHook(void) { + if (IRQCount == 0) { + IRQCount = IRQReload; + } else { + IRQCount--; + } + if ((IRQCount == 0) && IRQa) { + X6502_IRQBegin(FCEU_IQEXT); + } +} + +static void StateRestore(int version) { + Sync(); +} + +void Mapper413_Init(CartInfo *info) { + info->Power = M413Power; + GameHBIRQHook = M413IRQHook; + GameStateRestore = StateRestore; + AddExState(&StateRegs, ~0, 0, 0); +} diff --git a/source/fceultra/boards/451.cpp b/source/fceultra/boards/451.cpp new file mode 100644 index 0000000..007fde2 --- /dev/null +++ b/source/fceultra/boards/451.cpp @@ -0,0 +1,216 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2024 negativeExponent + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* NES 2.0 Mapper 451 is used for the homebrew game Haratyler HP/MP. It is + * basically a homebrew TLROM-like circuit board that implements the MMC3 + * register's in an unusual fashion, and saves the high score to flash ROM */ + +#include "mapinc.h" +#include "mmc3.h" +#include "../ines.h" + +const int ROM_CHIP = 0x00; +const int CFI_CHIP = 0x10; +const int FLASH_CHIP = 0x11; + +const int FLASH_SECTOR_SIZE = 64 * 1024; +const int magic_addr1 = 0x0555; +const int magic_addr2 = 0x02AA; + +static uint8 flash_state, flash_id_mode; +static uint8 *flash_data; +static uint16 flash_buffer_a[10]; +static uint8 flash_buffer_v[10]; +static uint8 flash_id[2]; + +static DECLFW(M451FlashWrite) +{ + if (flash_state < sizeof(flash_buffer_a) / sizeof(flash_buffer_a[0])) { + flash_buffer_a[flash_state] = (A & 0xFFF); + flash_buffer_v[flash_state] = V; + flash_state++; + + // enter flash ID mode + if ((flash_state == 2) && + (flash_buffer_a[0] == magic_addr1) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == magic_addr2) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[1] == magic_addr1) && (flash_buffer_v[1] == 0x90)) { + flash_id_mode = 0; + flash_state = 0; + } + + // erase sector + if ((flash_state == 6) && + (flash_buffer_a[0] == magic_addr1) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == magic_addr2) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == magic_addr1) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == magic_addr1) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == magic_addr2) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x30)) { + int offset = &Page[A >> 11][A] - flash_data; + int sector = offset / FLASH_SECTOR_SIZE; + for (int i = sector * FLASH_SECTOR_SIZE; i < (sector + 1) * FLASH_SECTOR_SIZE; i++) + flash_data[i % PRGsize[ROM_CHIP]] = 0xFF; + FCEU_printf("Flash sector #%d is erased (0x%08x - 0x%08x).\n", sector, offset, offset + FLASH_SECTOR_SIZE); + } + + // erase chip + if ((flash_state == 6) && + (flash_buffer_a[0] == magic_addr1) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == magic_addr2) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == magic_addr1) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == magic_addr1) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == magic_addr2) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_a[4] == magic_addr1) && (flash_buffer_v[4] == 0x10)) { + memset(flash_data, 0xFF, PRGsize[ROM_CHIP]); + FCEU_printf("Flash chip erased.\n"); + flash_state = 0; + } + + // write byte + if ((flash_state == 4) && + (flash_buffer_a[0] == magic_addr1) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == magic_addr2) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == magic_addr1) && (flash_buffer_v[2] == 0xA0)) { + int offset = &Page[A >> 11][A] - flash_data; + if (CartBR(A) != 0xFF) { + FCEU_PrintError("Error: can't write to 0x%08x, flash sector is not erased.\n", offset); + } + else { + CartBW(A, V); + } + flash_state = 0; + } + } + + // not a command + if (((A & 0x00FF) != (magic_addr1 & 0x00FF)) && ((A & 0x00FF) != (magic_addr2 & 0x00FF))) { + flash_state = 0; + } + + // reset + if (V == 0xF0) { + flash_state = 0; + flash_id_mode = 0; + } + + FixMMC3PRG(MMC3_cmd); +} + +static void M451FixPRG(uint32 A, uint8 V) { + setprg8r(FLASH_CHIP, 0x8000, 0); + setprg8r(FLASH_CHIP, 0xA000, 0x10 | ((EXPREGS[0] << 2) & 0x08) | (EXPREGS[0] & 0x01)); + setprg8r(FLASH_CHIP, 0xC000, 0x20 | ((EXPREGS[0] << 2) & 0x08) | (EXPREGS[0] & 0x01)); + setprg8r(FLASH_CHIP, 0xE000, 0x30); +} + +static void M451FixCHR(uint32 A, uint8 V) { + setchr8(EXPREGS[0] & 0x01); +} + +static DECLFR(M451Read) { + if (flash_state == 0x90) { + return flash_id[A & 1]; + } + return CartBR(A); +} + +static DECLFW(M451Write) { + M451FlashWrite(A, V); + switch (A & 0xE000) { + case 0xA000: + MMC3_CMDWrite(0xA000, A & 0x01); + break; + case 0xC000: + A &= 0xFF; + MMC3_IRQWrite(0xC000, A - 1); + MMC3_IRQWrite(0xC001, 0); + MMC3_IRQWrite(0xE000 + ((A == 0xFF) ? 0x00 : 0x01), 0x00); + break; + case 0xE000: + EXPREGS[0] = A & 0x03; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + break; + } +} + +static void M451Power(void) { + GenMMC3Power(); + SetReadHandler(0x8000, 0xFFFF, M451Read); + SetWriteHandler(0x8000, 0xFFFF, M451Write); +} + +static void StateRestore(int version) { + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M451Close(void) { + if(flash_data) + FCEU_gfree(flash_data); + flash_data = NULL; +} + +static void M451FlashReset(void) +{ + if (flash_data) + { + size_t flash_size = PRGsize[ROM_CHIP]; + // Copy ROM to flash data + for (size_t i = 0; i < flash_size; i++) { + flash_data[i] = PRGptr[ROM_CHIP][i]; + } + } +} + +void Mapper451_Init(CartInfo *info) { + GenMMC3_Init(info, 512, 16, 0, 0); + pwrap = M451FixPRG; + cwrap = M451FixCHR; + + info->Power = M451Power; + info->Close = M451Close; + GameStateRestore = StateRestore; + + flash_state = 0; + flash_id_mode = 0; + info->battery = 1; + + // Allocate memory for flash + size_t flash_size = PRGsize[ROM_CHIP]; + flash_data = (uint8*)FCEU_gmalloc(flash_size); + // Copy ROM to flash data + for (size_t i = 0; i < flash_size; i++) { + flash_data[i] = PRGptr[ROM_CHIP][i]; + } + SetupCartPRGMapping(FLASH_CHIP, flash_data, flash_size, 1); + info->addSaveGameBuf( flash_data, flash_size, M451FlashReset ); + + flash_id[0] = 0x37; + flash_id[1] = 0x86; + SetupCartPRGMapping(CFI_CHIP, flash_id, sizeof(flash_id), 0); + + AddExState(flash_data, flash_size, 0, "FLSH"); + AddExState(&flash_state, sizeof(flash_state), 0, "FLST"); + AddExState(&flash_id_mode, sizeof(flash_id_mode), 0, "FLMD"); + AddExState(flash_buffer_a, sizeof(flash_buffer_a), 0, "FLBA"); + AddExState(flash_buffer_v, sizeof(flash_buffer_v), 0, "FLBV"); +} diff --git a/source/fceultra/boards/68.cpp b/source/fceultra/boards/68.cpp index 710f443..32adeb6 100644 --- a/source/fceultra/boards/68.cpp +++ b/source/fceultra/boards/68.cpp @@ -24,7 +24,7 @@ static uint8 chr_reg[4]; static uint8 kogame, prg_reg, nt1, nt2, mirr; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE, count; +static uint32 WRAMSIZE=0, count=0; static SFORMAT StateRegs[] = { @@ -156,8 +156,7 @@ void Mapper68_Init(CartInfo *info) { WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(WRAM, WRAMSIZE, 0, "WRAM"); AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/69.cpp b/source/fceultra/boards/69.cpp index bc25621..9975abf 100644 --- a/source/fceultra/boards/69.cpp +++ b/source/fceultra/boards/69.cpp @@ -25,7 +25,7 @@ static uint8 cmdreg, preg[4], creg[8], mirr; static uint8 IRQa; static int32 IRQCount; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static SFORMAT StateRegs[] = { @@ -266,8 +266,7 @@ void Mapper69_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } GameStateRestore = StateRestore; Mapper69_ESI(); diff --git a/source/fceultra/boards/72.cpp b/source/fceultra/boards/72.cpp index de5ec91..ea74dc8 100644 --- a/source/fceultra/boards/72.cpp +++ b/source/fceultra/boards/72.cpp @@ -17,13 +17,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * Moero!! Pro Tennis have ADPCM codec on-board, PROM isn't dumped, emulation isn't - * possible just now. + * Moero!! Pro Tennis and Moero!! Pro Yakyuu '88 Ketteiban have an ADPCM chip with + * internal ROM, used for voice samples (not dumped, so emulation isn't possible) */ #include "mapinc.h" static uint8 preg, creg; +static void (*Sync)(void); static SFORMAT StateRegs[] = { @@ -32,13 +33,19 @@ static SFORMAT StateRegs[] = { 0 } }; -static void Sync(void) { +static void M72Sync(void) { setprg16(0x8000, preg); setprg16(0xC000, ~0); setchr8(creg); } -static DECLFW(M72Write) { +static void M92Sync(void) { + setprg16(0x8000, 0); + setprg16(0xC000, preg); + setchr8(creg); +} + +static DECLFW(Write) { if (V & 0x80) preg = V & 0xF; if (V & 0x40) @@ -46,10 +53,10 @@ static DECLFW(M72Write) { Sync(); } -static void M72Power(void) { +static void Power(void) { Sync(); SetReadHandler(0x8000, 0xFFFF, CartBR); - SetWriteHandler(0x6000, 0xFFFF, M72Write); + SetWriteHandler(0x8000, 0xFFFF, Write); } static void StateRestore(int version) { @@ -57,7 +64,16 @@ static void StateRestore(int version) { } void Mapper72_Init(CartInfo *info) { - info->Power = M72Power; + Sync = M72Sync; + info->Power = Power; + GameStateRestore = StateRestore; + + AddExState(&StateRegs, ~0, 0, 0); +} + +void Mapper92_Init(CartInfo *info) { + Sync = M92Sync; + info->Power = Power; GameStateRestore = StateRestore; AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/80.cpp b/source/fceultra/boards/80.cpp index b99132c..5480aec 100644 --- a/source/fceultra/boards/80.cpp +++ b/source/fceultra/boards/80.cpp @@ -169,8 +169,7 @@ void Mapper80_Init(CartInfo *info) { GameStateRestore = StateRestore; if (info->battery) { - info->SaveGame[0] = wram; - info->SaveGameLen[0] = 256; + info->addSaveGameBuf( wram, sizeof(wram) ); } AddExState(&StateRegs80, ~0, 0, 0); diff --git a/source/fceultra/boards/82.cpp b/source/fceultra/boards/82.cpp index a1a68b6..01e4f7a 100644 --- a/source/fceultra/boards/82.cpp +++ b/source/fceultra/boards/82.cpp @@ -25,7 +25,7 @@ static uint8 regs[9], ctrl; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static SFORMAT StateRegs[] = { @@ -90,8 +90,7 @@ void Mapper82_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } GameStateRestore = StateRestore; AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/__dummy_mapper.cpp b/source/fceultra/boards/__dummy_mapper.cpp index 505f916..17cdba5 100644 --- a/source/fceultra/boards/__dummy_mapper.cpp +++ b/source/fceultra/boards/__dummy_mapper.cpp @@ -92,8 +92,7 @@ void MapperNNN_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } */ AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/addrlatch.cpp b/source/fceultra/boards/addrlatch.cpp index 05d05f0..4863210 100644 --- a/source/fceultra/boards/addrlatch.cpp +++ b/source/fceultra/boards/addrlatch.cpp @@ -26,7 +26,8 @@ static uint8 dipswitch; static void (*WSync)(void); static readfunc defread; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; +static uint8 hasBattery = 0; static DECLFW(LatchWrite) { latche = A; @@ -77,8 +78,7 @@ static void Latch_Init(CartInfo *info, void (*proc)(void), readfunc func, uint16 WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(WRAM, WRAMSIZE, 0, "WRAM"); } @@ -122,7 +122,7 @@ static void UNL43272Sync(void) { if ((latche & 0x81) == 0x81) { setprg32(0x8000, (latche & 0x38) >> 3); } else - FCEU_printf("unrecognized command %04!\n", latche); + FCEU_printf("unrecognized command %04x!\n", latche); setchr8(0); setmirror(0); } @@ -181,6 +181,7 @@ void Mapper59_Init(CartInfo *info) { } //------------------ Map 061 --------------------------- + static void M61Sync(void) { if (((latche & 0x10) << 1) ^ (latche & 0x20)) { setprg16(0x8000, ((latche & 0xF) << 1) | (((latche & 0x20) >> 4))); @@ -195,30 +196,21 @@ void Mapper61_Init(CartInfo *info) { Latch_Init(info, M61Sync, NULL, 0x0000, 0x8000, 0xFFFF, 0); } -//------------------ Map 092 --------------------------- -// Another two-in-one mapper, two Jaleco carts uses similar -// hardware, but with different wiring. -// Original code provided by LULU -// Additionally, PCB contains DSP extra sound chip, used for voice samples (unemulated) +//------------------ Map 174 --------------------------- -static void M92Sync(void) { - uint8 reg = latche & 0xF0; - setprg16(0x8000, 0); - if (latche >= 0x9000) { - switch (reg) { - case 0xD0: setprg16(0xc000, latche & 15); break; - case 0xE0: setchr8(latche & 15); break; - } +static void M174Sync(void) { + if (latche & 0x80) { + setprg32(0x8000, (latche >> 5) & 3); } else { - switch (reg) { - case 0xB0: setprg16(0xc000, latche & 15); break; - case 0x70: setchr8(latche & 15); break; - } + setprg16(0x8000, (latche >> 4) & 7); + setprg16(0xC000, (latche >> 4) & 7); } + setchr8((latche >> 1) & 7); + setmirror((latche & 1) ^ 1); } -void Mapper92_Init(CartInfo *info) { - Latch_Init(info, M92Sync, NULL, 0x80B0, 0x8000, 0xFFFF, 0); +void Mapper174_Init(CartInfo *info) { + Latch_Init(info, M174Sync, NULL, 0, 0x8000, 0xFFFF, 0); } //------------------ Map 200 --------------------------- @@ -342,20 +334,17 @@ void Mapper217_Init(CartInfo *info) { } //------------------ Map 227 --------------------------- - static void M227Sync(void) { uint32 S = latche & 1; uint32 p = ((latche >> 2) & 0x1F) + ((latche & 0x100) >> 3); uint32 L = (latche >> 9) & 1; -// ok, according to nesdev wiki (refrenced to the nesdev dumping thread) there is a CHR write protection bit7. -// however, this bit clearly determined a specific PRG layout for some game but does not meant to have additional -// functionality. as I see from the menu code, it disables the chr writing before run an actual game. -// this fix here makes happy both waixing rpgs and multigame menus at once. can't veryfy it on a hardware -// but if I find some i'll definitly do this. - - if ((latche & 0xF000) == 0xF000) - SetupCartCHRMapping(0, CHRptr[0], 0x2000, 0); +// Only Waixing appear to have battery flag enabled, while multicarts don't. +// Multicarts needs CHR-RAM protect in NROM modes, so only apply CHR-RAM protect +// on non battery-enabled carts. + if (!hasBattery && (latche & 0x80) == 0x80) + /* CHR-RAM write protect hack, needed for some multicarts */ + SetupCartCHRMapping(0, CHRptr[0], 0x2000, 0); else SetupCartCHRMapping(0, CHRptr[0], 0x2000, 1); @@ -393,6 +382,7 @@ static void M227Sync(void) { void Mapper227_Init(CartInfo *info) { Latch_Init(info, M227Sync, NULL, 0x0000, 0x8000, 0xFFFF, 1); + hasBattery = info->battery; } //------------------ Map 229 --------------------------- diff --git a/source/fceultra/boards/bandai.cpp b/source/fceultra/boards/bandai.cpp index d6670f5..9f0bbe1 100644 --- a/source/fceultra/boards/bandai.cpp +++ b/source/fceultra/boards/bandai.cpp @@ -29,7 +29,7 @@ static uint8 IRQa; static int16 IRQCount, IRQLatch; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static SFORMAT StateRegs[] = { @@ -178,9 +178,12 @@ static void x24c02_write(uint8 data) { x24c02_addr <<= 1; x24c02_addr |= sda; } else { - if (sda) // READ COMMAND + if ((x24c02_addr & 0x78) != 0x50) { // WRONG DEVICE ADDRESS + x24c02_out = 1; + x24c02_state = X24C0X_STANDBY; + } else if (sda) // READ COMMAND x24c02_state = X24C0X_READ; - else // WRITE COMMAND + else // WRITE COMMAND x24c02_state = X24C0X_WORD; } x24c02_bitcount++; @@ -317,8 +320,7 @@ void Mapper16_Init(CartInfo *info) { MapIRQHook = BandaiIRQHook; info->battery = 1; - info->SaveGame[0] = x24c0x_data + 256; - info->SaveGameLen[0] = 256; + info->addSaveGameBuf( x24c0x_data + 256, 256 ); AddExState(x24c0x_data, 256, 0, "DATA"); AddExState(&x24c02StateRegs, ~0, 0, 0); @@ -333,8 +335,7 @@ void Mapper159_Init(CartInfo *info) { MapIRQHook = BandaiIRQHook; info->battery = 1; - info->SaveGame[0] = x24c0x_data; - info->SaveGameLen[0] = 128; + info->addSaveGameBuf( x24c0x_data, 128 ); AddExState(x24c0x_data, 128, 0, "DATA"); AddExState(&x24c01StateRegs, ~0, 0, 0); @@ -378,8 +379,7 @@ void Mapper153_Init(CartInfo *info) { AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } GameStateRestore = StateRestore; @@ -599,8 +599,7 @@ void Mapper157_Init(CartInfo *info) { GameInfo->cspecial = SIS_DATACH; info->battery = 1; - info->SaveGame[0] = x24c0x_data; - info->SaveGameLen[0] = 512; + info->addSaveGameBuf( x24c0x_data, 512 ); AddExState(x24c0x_data, 512, 0, "DATA"); AddExState(&x24c01StateRegs, ~0, 0, 0); AddExState(&x24c02StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/cheapocabra.cpp b/source/fceultra/boards/cheapocabra.cpp index 3183781..5548301 100644 --- a/source/fceultra/boards/cheapocabra.cpp +++ b/source/fceultra/boards/cheapocabra.cpp @@ -252,8 +252,7 @@ void Mapper111_Init(CartInfo *info) { if (flash) { FLASHROM = (uint8*)FCEU_gmalloc(FLASHROMSIZE); - info->SaveGame[0] = FLASHROM; - info->SaveGameLen[0] = FLASHROMSIZE; + info->addSaveGameBuf( FLASHROM, FLASHROMSIZE ); AddExState(FLASHROM, FLASHROMSIZE, 0, "FROM"); AddExState(&FlashRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/coolboy.cpp b/source/fceultra/boards/coolboy.cpp index e220ae8..13a1e61 100644 --- a/source/fceultra/boards/coolboy.cpp +++ b/source/fceultra/boards/coolboy.cpp @@ -21,7 +21,7 @@ * * COOLBOY cartridges use registers at address $6xxx * MINDKIDS cartridges use a solder pad labelled "5/6K" to select between $5000 and $6000 - * + * * $xxx0 * 7 bit 0 * ---- ---- @@ -29,12 +29,11 @@ * |||| |||| * |||| |+++-- PRG offset (PRG A19, A18, A17) * |||| +----- Alternate CHR A17 - * ||++------- PRG offset (PRG A24, A23) + * ||++------- PRG offset (PRG A24, A23), CHR offset (CHR A19, A18) * |+--------- PRG mask (PRG A17 from 0: MMC3; 1: offset) * +---------- CHR mask (CHR A17 from 0: MMC3; 1: alternate) * * $xxx1 - * * 7 bit 0 * ---- ---- * GHIJ KKLx @@ -55,15 +54,16 @@ * $xxx3 * 7 bit 0 * ---- ---- - * NPxP QQRx - * || | ||| - * || | +++--- PRG offset for GNROM mode (PRG A16, A15, A14) - * || +------- 1: GNROM mode; 0: MMC3 mode - * || | (1: PRG A16...13 from QQ, L, R, CPU A14, A13 + CHR A16...10 from MMMM, PPU A12...10; - * || | 0: PRG A16...13 from MMC3 + CHR A16...A10 from MMC3 ) + * NPZP QQRx + * |||| ||| + * |||| +++--- PRG offset for GNROM mode (PRG A16, A15, A14) + * |||+------- 1: GNROM mode; 0: MMC3 mode + * |||| (1: PRG A16...13 from QQ, L, R, CPU A14, A13 + CHR A16...10 from MMMM, PPU A12...10; + * |||| 0: PRG A16...13 from MMC3 + CHR A16...A10 from MMC3 ) + * ||+-------- 1: Also enable PRG RAM in $5000-$5FFF * |+-+------- Banking mode * |+--------- "Weird MMC3 mode" - * +---------- Lockout (prevent further writes to these four registers, only works in MMC3 mode) + * +---------- Lockout (prevent further writes to all registers but the one at $xxx2, only works in MMC3 mode) * * Also some new cartridges from MINDKIDS have /WE and /OE pins connected to mapper, * which allows you to rewrite flash memory without soldering. @@ -75,25 +75,90 @@ #include "mapinc.h" #include "mmc3.h" +#include "../ines.h" -static void COOLBOYCW(uint32 A, uint8 V) { - uint32 mask = 0xFF ^ (EXPREGS[0] & 0x80); - if (EXPREGS[3] & 0x10) { - if (EXPREGS[3] & 0x40) { // Weird mode +const int ROM_CHIP = 0x00; +const int CFI_CHIP = 0x11; +const int FLASH_CHIP = 0x12; + +const uint32 FLASH_SECTOR_SIZE = 128 * 1024; + +extern uint8* WRAM; +static uint8* CFI = NULL; +static uint8* Flash = NULL; + +static uint8 flash_save = 0; +static uint8 flash_state = 0; +static uint16 flash_buffer_a[10]; +static uint8 flash_buffer_v[10]; +static uint8 cfi_mode = 0; + +static uint16 regs_base = 0; +static uint8 flag23 = 0; +static uint8 flag45 = 0; +static uint8 flag67 = 0; +static uint8 flag89 = 0; + +// Macronix 256-mbit memory CFI data +const uint8 cfi_data[] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x51, 0x52, 0x59, 0x02, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x36, 0x00, 0x00, 0x03, + 0x06, 0x09, 0x13, 0x03, 0x05, 0x03, 0x02, 0x19, + 0x02, 0x00, 0x06, 0x00, 0x01, 0xFF, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0x50, 0x52, 0x49, 0x31, 0x33, 0x14, 0x02, 0x01, + 0x00, 0x08, 0x00, 0x00, 0x02, 0x95, 0xA5, 0x05, + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static void AA6023CW(uint32 A, uint8 V) { + if (flag89) { + /* + $xxx0 + 7 bit 0 + ---- ---- + AB.C DEEE + || | |||| + || | |+++-- PRG offset (PRG A19, A18, A17) + || | +----- Alternate CHR A17 + || +------- 1=Write-protect CHR-RAM + |+--------- PRG mask (PRG A17 from 0: MMC3; 1: offset) + +---------- CHR mask (CHR A17 from 0: MMC3; 1: alternate) + */ + if (EXPREGS[0] & 0b00010000) + SetupCartCHRMapping(0, VROM, CHRsize[0], 0); // write-protect CHR-RAM + else + SetupCartCHRMapping(0, VROM, CHRsize[0], 1); // allow CHR writes + } + + uint32 mask = 0xFF ^ (EXPREGS[0] & 0b10000000); + if (EXPREGS[3] & 0b00010000) { + if (EXPREGS[3] & 0b01000000) { // Weird mode int cbase = (MMC3_cmd & 0x80) << 5; switch (cbase ^ A) { // Don't even try do understand case 0x0400: case 0x0C00: V &= 0x7F; break; } } - // Highest bit goes from MMC3 registers when EXPREGS[3]&0x80==0 or from EXPREGS[0]&0x08 otherwise + // Highest bit goes from MMC3 registers when EXPREGS[0]&0x80==0 or from EXPREGS[0]&0x08 otherwise setchr1(A, - (V & 0x80 & mask) | ((((EXPREGS[0] & 0x08) << 4) & ~mask)) // 7th bit + (V & 0x80 & mask) | ((((EXPREGS[0] & 0b00001000) << 4) & ~mask)) // 7th bit | ((EXPREGS[2] & 0x0F) << 3) // 6-3 bits | ((A >> 10) & 7) // 2-0 bits + | ((EXPREGS[0] & 0b00110000) << 4) // There are some ROMs with 1 MiB CHR-ROM ); - } else { - if (EXPREGS[3] & 0x40) { // Weird mode, again + } + else { + if (EXPREGS[3] & 0b01000000) { // Weird mode, again int cbase = (MMC3_cmd & 0x80) << 5; switch (cbase ^ A) { // Don't even try do understand case 0x0000: V = DRegBuf[0]; break; @@ -103,18 +168,81 @@ static void COOLBOYCW(uint32 A, uint8 V) { } } // Simple MMC3 mode - // Highest bit goes from MMC3 registers when EXPREGS[3]&0x80==0 or from EXPREGS[0]&0x08 otherwise - setchr1(A, (V & mask) | (((EXPREGS[0] & 0x08) << 4) & ~mask)); + // Highest bit goes from MMC3 registers when EXPREGS[0]&0x80==0 or from EXPREGS[0]&0x08 otherwise + setchr1(A, + (V & mask) + | (((EXPREGS[0] & 0x08) << 4) & ~mask) + | ((EXPREGS[0] & 0b00110000) << 4)); // There are some ROMs with 1 MiB CHR-ROM } } -static void COOLBOYPW(uint32 A, uint8 V) { - uint32 mask = ((0x3F | (EXPREGS[1] & 0x40) | ((EXPREGS[1] & 0x20) << 2)) ^ ((EXPREGS[0] & 0x40) >> 2)) ^ ((EXPREGS[1] & 0x80) >> 2); - uint32 base = ((EXPREGS[0] & 0x07) >> 0) | ((EXPREGS[1] & 0x10) >> 1) | ((EXPREGS[1] & 0x0C) << 2) | ((EXPREGS[0] & 0x30) << 2); +static void AA6023PW(uint32 A, uint8 V) { + uint8 CREGS[] = {EXPREGS[0], EXPREGS[1], EXPREGS[2], EXPREGS[3]}; + // Submappers has scrambled bits + if (flag23) { + /* + $xxx1 + 7 bit 0 + ---- ---- + GHIL JKKx + |||| ||| + |||| +++--- PRG offset (in order: PRG A20, A21, A22) + |||+------- GNROM mode bank PRG size (0: 32 KiB bank, PRG A14=CPU A14; 1: 16 KiB bank, PRG A14=offset A14) + ||+-------- PRG mask (PRG A20 from 0: offset; 1: MMC3) + |+--------- PRG mask (PRG A19 from 0: offset; 1: MMC3) + +---------- PRG mask (PRG A18 from 0: MMC3; 1: offset) + */ + CREGS[1] = (CREGS[1] & 0b11100101) + | ((CREGS[1] & 0b00001000) << 1) // PRG A20 + | ((CREGS[1] & 0b00000010) << 2) // PRG A22 + | ((((CREGS[1] ^ 0b00010000) & 0b00010000) >> 3)); // GNROM mode bank PRG size + } + if (flag45) { + /* + $xxx0 + 7 bit 0 + ---- ---- + ABCC DEEE + |||| |||| + |||| |+++-- PRG offset (PRG A19, A18, A17) + |||| +----- Alternate CHR A17 + ||++------- PRG offset (PRG A21, A20) + |+--------- PRG mask (PRG A17 from 0: MMC3; 1: offset) + +---------- CHR mask (CHR A17 from 0: MMC3; 1: alternate) + $xxx1 + 7 bit 0 + ---- ---- + GHIx xxLx + ||| | + ||| +--- GNROM mode bank PRG size (1: 32 KiB bank, PRG A14=CPU A14; 0: 16 KiB bank, PRG A14=offset A14) + ||+-------- PRG mask (PRG A20 from 0: offset; 1: MMC3) + |+--------- PRG mask (PRG A19 from 0: offset; 1: MMC3) + +---------- PRG mask (PRG A18 from 0: MMC3; 1: offset) + */ + CREGS[1] = (CREGS[1] & 0b11100011) + | ((CREGS[0] & 0b00100000) >> 3) // PRG A21 + | (CREGS[0] & 0b00010000); // PRG A20 + CREGS[0] &= 0b11001111; + } + + uint32 mask = ((0b00111111 | (CREGS[1] & 0b01000000) | ((CREGS[1] & 0b00100000) << 2)) ^ ((CREGS[0] & 0b01000000) >> 2)) ^ ((CREGS[1] & 0b10000000) >> 2); + uint32 base = ((CREGS[0] & 0b00000111) >> 0) | ((CREGS[1] & 0b00010000) >> 1) | ((CREGS[1] & 0b00001100) << 2) | ((CREGS[0] & 0b00110000) << 2); + + if (flash_save && cfi_mode) { + setprg32r(CFI_CHIP, 0x8000, 0); + return; + } + + int chip = !flash_save ? ROM_CHIP : FLASH_CHIP; + // There are ROMs with multiple PRG ROM chips + int chip_offset = 0; + if (flag67 && EXPREGS[0] & 0b00001000) { + chip_offset += ROM_size; + } // Very weird mode // Last banks are first in this mode, ignored when MMC3_cmd&0x40 - if ((EXPREGS[3] & 0x40) && (V >= 0xFE) && !((MMC3_cmd & 0x40) != 0)) { + if ((CREGS[3] & 0b01000000) && (V >= 0xFE) && !((MMC3_cmd & 0x40) != 0)) { switch (A & 0xE000) { case 0xC000: case 0xE000: @@ -123,91 +251,234 @@ static void COOLBOYPW(uint32 A, uint8 V) { } } - // Regular MMC3 mode, internal ROM size can be up to 2048kb! - if (!(EXPREGS[3] & 0x10)) - setprg8(A, (((base << 4) & ~mask)) | (V & mask)); - else { // NROM mode + if (!(CREGS[3] & 0x10)) { + // Regular MMC3 mode but can be extended to 2MiB + setprg8r(chip, A, ((((base << 4) & ~mask)) | (V & mask)) + chip_offset); + } + else { + // NROM mode mask &= 0xF0; uint8 emask; - if ((((EXPREGS[1] & 2) != 0))) // 32kb mode - emask = (EXPREGS[3] & 0x0C) | ((A & 0x4000) >> 13); + if (CREGS[1] & 0b00000010) // 32kb mode + emask = (CREGS[3] & 0b00001100) | ((A & 0x4000) >> 13); else // 16kb mode - emask = EXPREGS[3] & 0x0E; - setprg8(A, ((base << 4) & ~mask) // 7-4 bits are from base (see below) - | (V & mask) // ... or from MM3 internal regs, depends on mask - | emask // 3-1 (or 3-2 when (EXPREGS[3]&0x0C is set) from EXPREGS[3] - | ((A & 0x2000) >> 13)); // 0th just as is + emask = CREGS[3] & 0b00001110; + setprg8r(chip, A, ( + ((base << 4) & ~mask) // 7-4 bits are from base + | (V & mask) // ... or from MM3 internal regs, depends on mask + | emask // 3-1 (or 3-2 when (EXPREGS[3]&0x0C is set) from EXPREGS[3] + | ((A & 0x2000) >> 13) // 0th just as is + ) + chip_offset); // For multi-chip ROMs } } -static DECLFW(COOLBOYWrite) { - if(A001B & 0x80) - CartBW(A,V); +static DECLFW(AA6023WramWrite) { + if (A001B & 0x80) + CartBW(A, V); +} + +static DECLFW(AA6023Write) { + if (A >= 0x6000) { + AA6023WramWrite(A, V); + } // Deny any further writes when 7th bit is 1 AND 4th is 0 if ((EXPREGS[3] & 0x90) != 0x80) { EXPREGS[A & 3] = V; - FixMMC3PRG(MMC3_cmd); - FixMMC3CHR(MMC3_cmd); } + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); } -static void COOLBOYReset(void) { +static DECLFW(AA6023FlashWrite) { + if (A < 0xC000) + MMC3_CMDWrite(A, V); + else + MMC3_IRQWrite(A, V); + + if (!flash_save) return; + if (flash_state < sizeof(flash_buffer_a) / sizeof(flash_buffer_a[0])) { + flash_buffer_a[flash_state] = A & 0xFFF; + flash_buffer_v[flash_state] = V; + flash_state++; + + // enter CFI mode + if ((flash_state == 1) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0x98)) { + cfi_mode = 1; + flash_state = 0; + } + + // erase sector + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x0AAA) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x0555) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x30)) { + int offset = &Page[A >> 11][A] - Flash; + int sector = offset / FLASH_SECTOR_SIZE; + for (uint32 i = sector * FLASH_SECTOR_SIZE; i < (sector + 1) * FLASH_SECTOR_SIZE; i++) + Flash[i % PRGsize[ROM_CHIP]] = 0xFF; + FCEU_printf("Flash sector #%d is erased (0x%08x - 0x%08x).\n", sector, offset, offset + FLASH_SECTOR_SIZE); + flash_state = 0; + } + + // erase chip, lol + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x0AAA) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x0555) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x10)) { + memset(Flash, 0xFF, PRGsize[ROM_CHIP]); + FCEU_printf("Flash chip erased.\n"); + flash_state = 0; + } + + // write byte + if ((flash_state == 4) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0xA0)) { + int offset = &Page[A >> 11][A] - Flash; + if (CartBR(A) != 0xFF) { + FCEU_PrintError("Error: can't write to 0x%08x, flash sector is not erased.\n", offset); + } + else { + CartBW(A, V); + } + flash_state = 0; + } + } + + // not a command + if (((A & 0xFFF) != 0x0AAA) && ((A & 0xFFF) != 0x0555)) { + flash_state = 0; + } + + // reset + if (V == 0xF0) { + flash_state = 0; + cfi_mode = 0; + } + + FixMMC3PRG(MMC3_cmd); +} + +static void AA6023Reset(void) { MMC3RegReset(); EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; + flash_state = 0; + cfi_mode = 0; FixMMC3PRG(MMC3_cmd); FixMMC3CHR(MMC3_cmd); } -static void COOLBOYPower(void) { +static void AA6023Power(void) { GenMMC3Power(); EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; FixMMC3PRG(MMC3_cmd); FixMMC3CHR(MMC3_cmd); - SetWriteHandler(0x5000, 0x5fff, CartBW); // some games access random unmapped areas and crashes because of KT-008 PCB hack in MMC3 source lol - SetWriteHandler(0x6000, 0x6fff, COOLBOYWrite); + if (regs_base != 0x5000) + SetWriteHandler(0x5000, 0x5fff, CartBW); // some games access random unmapped areas and crashes because of KT-008 PCB hack in MMC3 source lol + SetWriteHandler(0x6000, 0x7fff, AA6023WramWrite); + SetWriteHandler(regs_base, regs_base + 0x0fff, AA6023Write); + SetWriteHandler(0x8000, 0xFFFF, AA6023FlashWrite); + SetReadHandler(0x8000, 0xFFFF, CartBR); } -static void MINDKIDSPower(void) { - GenMMC3Power(); - EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; +static void AA6023Restore(int version) { FixMMC3PRG(MMC3_cmd); FixMMC3CHR(MMC3_cmd); - SetWriteHandler(0x5000, 0x5fff, COOLBOYWrite); +} + +static void AA6023Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + if (Flash) + FCEU_gfree(Flash); + if (CFI) + FCEU_gfree(CFI); + WRAM = Flash = CFI = NULL; +} + +void CommonInit(CartInfo* info, int submapper) +{ + GenMMC3_Init(info, 2048, info->vram_size / 1024, !info->ines2 ? 8 : (info->wram_size + info->battery_wram_size) / 1024, info->battery); + pwrap = AA6023PW; + cwrap = AA6023CW; + + switch (submapper) + { + case 2: + regs_base = 0x7000; + break; + case 0: + case 4: + case 6: + case 8: + regs_base = 0x6000; + break; + case 1: + case 3: + case 5: + case 7: + case 9: + regs_base = 0x5000; + break; + default: + FCEU_PrintError("Submapper #%d is not supported", submapper); + } + flag23 = (submapper == 2) || (submapper == 3); + flag45 = (submapper == 4) || (submapper == 5); + flag67 = (submapper == 6) || (submapper == 7); + flag89 = (submapper == 8) || (submapper == 9); + info->Power = AA6023Power; + info->Reset = AA6023Reset; + info->Close = AA6023Close; + GameStateRestore = AA6023Restore; + + flash_save = info->battery; + + if (flash_save) { + CFI = (uint8*)FCEU_gmalloc(sizeof(cfi_data) * 2); + for (size_t i = 0; i < sizeof(cfi_data); i++) { + CFI[i * 2] = CFI[i * 2 + 1] = cfi_data[i]; + } + SetupCartPRGMapping(CFI_CHIP, CFI, sizeof(cfi_data) * 2, 0); + Flash = (uint8*)FCEU_gmalloc(PRGsize[ROM_CHIP]); + for (unsigned int i = 0; i < PRGsize[ROM_CHIP]; i++) { + Flash[i] = PRGptr[ROM_CHIP][i % PRGsize[ROM_CHIP]]; + } + SetupCartPRGMapping(FLASH_CHIP, Flash, PRGsize[ROM_CHIP], 1); + info->addSaveGameBuf( Flash, PRGsize[ROM_CHIP] ); + } + + AddExState(EXPREGS, 4, 0, "EXPR"); + if (flash_save) + { + AddExState(&flash_state, sizeof(flash_state), 0, "FLST"); + AddExState(flash_buffer_a, sizeof(flash_buffer_a), 0, "FLBA"); + AddExState(flash_buffer_v, sizeof(flash_buffer_v), 0, "FLBV"); + AddExState(&cfi_mode, sizeof(cfi_mode), 0, "CFIM"); + AddExState(Flash, PRGsize[ROM_CHIP], 0, "FLAS"); + } } // Registers at $6xxx -void COOLBOY_Init(CartInfo *info) { - GenMMC3_Init(info, 2048, 256, 8, 1); - pwrap = COOLBOYPW; - cwrap = COOLBOYCW; - info->Power = COOLBOYPower; - info->Reset = COOLBOYReset; - AddExState(EXPREGS, 4, 0, "EXPR"); +void COOLBOY_Init(CartInfo* info) { + CommonInit(info, 0); } // Registers at $5xxx -void MINDKIDS_Init(CartInfo *info) { - GenMMC3_Init(info, 2048, 256, 8, 1); - pwrap = COOLBOYPW; - cwrap = COOLBOYCW; - info->Power = MINDKIDSPower; - info->Reset = COOLBOYReset; - AddExState(EXPREGS, 4, 0, "EXPR"); +void MINDKIDS_Init(CartInfo* info) { + CommonInit(info, 1); } // For NES 2.0 loader -void SMD132_SMD133_Init(CartInfo *info) { - switch (info->submapper) - { - case 0: - COOLBOY_Init(info); - break; - case 1: - MINDKIDS_Init(info); - break; - default: - FCEU_PrintError("Unknown submapper: #%d.", info->submapper); - break; - } +void AA6023_Init(CartInfo* info) { + CommonInit(info, info->submapper); } diff --git a/source/fceultra/boards/coolgirl.cpp b/source/fceultra/boards/coolgirl.cpp new file mode 100644 index 0000000..e6187ef --- /dev/null +++ b/source/fceultra/boards/coolgirl.cpp @@ -0,0 +1,2352 @@ +/* FCE Ultra - NES/Famicom Emulator +* +* Copyright notice for this file: +* Copyright (C) 2022 Cluster +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Very complicated homebrew multicart mapper with. +* The code is so obscured and weird because it's ported from Verilog CPLD source code: +* https://github.com/ClusterM/coolgirl-famicom-multicart/blob/master/CoolGirl_mappers.vh +* +* Range: $5000-$5FFF +* +* Mask: $5007 +* +* All registers are $00 on power-on and reset. +* +* $5xx0 +* 7 bit 0 +* ---- ---- +* PPPP PPPP +* |||| |||| +* ++++-++++-- PRG base offset (A29-A22) +* +* $5xx1 +* 7 bit 0 +* ---- ---- +* PPPP PPPP +* |||| |||| +* ++++-++++-- PRG base offset (A21-A14) +* +* $5xx2 +* 7 bit 0 +* ---- ---- +* AMMM MMMM +* |||| |||| +* |+++-++++-- PRG mask (A20-A14, inverted+anded with PRG address) +* +---------- CHR mask (A18, inverted+anded with CHR address) +* +* $5xx3 +* 7 bit 0 +* ---- ---- +* BBBC CCCC +* |||| |||| +* |||+-++++-- CHR bank A (bits 7-3) +* +++-------- PRG banking mode (see below) +* +* $5xx4 +* 7 bit 0 +* ---- ---- +* DDDE EEEE +* |||| |||| +* |||+-++++-- CHR mask (A17-A13, inverted+anded with CHR address) +* +++-------- CHR banking mode (see below) +* +* $5xx5 +* 7 bit 0 +* ---- ---- +* CDDE EEWW +* |||| |||| +* |||| ||++-- 8KiB WRAM page at $6000-$7FFF +* |+++-++---- PRG bank A (bits 5-1) +* +---------- CHR bank A (bit 8) +* +* $5xx6 +* 7 bit 0 +* ---- ---- +* FFFM MMMM +* |||| |||| +* |||+ ++++-- Mapper code (bits 4-0, see below) +* +++-------- Flags 2-0, functionality depends on selected mapper +* +* $5xx7 +* 7 bit 0 +* ---- ---- +* LMTR RSNO +* |||| |||+-- Enable WRAM (read and write) at $6000-$7FFF +* |||| ||+--- Allow writes to CHR RAM +* |||| |+---- Allow writes to flash chip +* |||+-+----- Mirroring (00=vertical, 01=horizontal, 10=1Sa, 11=1Sb) +* ||+-------- Enable four-screen mode +* |+-- ------ Mapper code (bit 5, see below) +* +---------- Lockout bit (prevent further writes to all registers) +* +*/ + +#include "mapinc.h" + +const uint32 SAVE_FLASH_SIZE = 1024 * 1024 * 8; +const uint32 FLASH_SECTOR_SIZE = 128 * 1024; +const int ROM_CHIP = 0x00; +const int WRAM_CHIP = 0x10; +const int FLASH_CHIP = 0x11; +const int CFI_CHIP = 0x13; + +static int CHR_SIZE = 0; +static uint32 WRAM_SIZE = 0; +static uint8* WRAM = NULL; +static uint8* SAVE_FLASH = NULL; +static uint8* CFI; + +static uint8 sram_enabled = 0; +static uint8 sram_page = 0; // [1:0] +static uint8 can_write_chr = 0; +static uint8 map_rom_on_6000 = 0; +static uint8 flags = 0; // [2:0] +static uint8 mapper = 0; // [5:0] +static uint8 can_write_flash = 0; +static uint8 mirroring = 0; // [1:0] +static uint8 four_screen = 0; +static uint8 lockout = 0; + +static uint32 prg_base = 0; // [26:14] +static uint32 prg_mask = 0b11111000 << 14; // 11111000, 128KB // [20:14] +static uint8 prg_mode = 0; // [2:0] +static uint8 prg_bank_6000 = 0; // [7:0] +static uint8 prg_bank_a = 0; // [7:0] +static uint8 prg_bank_b = 1; // [7:0] +static uint8 prg_bank_c = ~1; // [7:0] +static uint8 prg_bank_d = ~0; // [7:0] + +static uint32 chr_mask = 0; // [18:13] +static uint8 chr_mode = 0; // [2:0] +static uint16 chr_bank_a = 0; // [8:0] +static uint16 chr_bank_b = 1; // [8:0] +static uint16 chr_bank_c = 2; // [8:0] +static uint16 chr_bank_d = 3; // [8:0] +static uint16 chr_bank_e = 4; // [8:0] +static uint16 chr_bank_f = 5; // [8:0] +static uint16 chr_bank_g = 6; // [8:0] +static uint16 chr_bank_h = 7; // [8:0] + +static uint8 TKSMIR[8]; + +static uint32 prg_bank_6000_mapped = 0; +static uint32 prg_bank_a_mapped = 0; +static uint32 prg_bank_b_mapped = 0; +static uint32 prg_bank_c_mapped = 0; +static uint32 prg_bank_d_mapped = 0; + +// for MMC2/MMC4 +static uint8 ppu_latch0 = 0; +static uint8 ppu_latch1 = 0; +// for MMC1 +static uint64 lreset = 0; +static uint8 mmc1_load_register = 0; // [5:0] +// for MMC3 +static uint8 mmc3_internal = 0; // [2:0] +// for mapper #69 +static uint8 mapper69_internal = 0; // [3:0] +// for mapper #112 +static uint8 mapper112_internal = 0; // [2:0] +// for mapper #163 +static uint8 mapper_163_latch = 0; +static uint8 mapper163_r0 = 0; // [7:0] +static uint8 mapper163_r1 = 0; // [7:0] +static uint8 mapper163_r2 = 0; // [7:0] +static uint8 mapper163_r3 = 0; // [7:0] +static uint8 mapper163_r4 = 0; // [7:0] +static uint8 mapper163_r5 = 0; // [7:0] + +// For mapper #90 +static uint8 mul1 = 0; +static uint8 mul2 = 0; + +// for MMC3 scanline-based interrupts, counts A12 rises after long A12 falls +static uint8 mmc3_irq_enabled = 0; // register to enable/disable counter +static uint8 mmc3_irq_latch = 0; // [7:0], stores counter reload latch value +static uint8 mmc3_irq_counter = 0; // [7:0], counter itself (downcounting) +static uint8 mmc3_irq_reload = 0; // flag to reload counter from latch +// for MMC5 scanline-based interrupts, counts dummy PPU reads +static uint8 mmc5_irq_enabled = 0; // register to enable/disable counter +static uint8 mmc5_irq_line = 0; // [7:0], scanline on which IRQ will be triggered +static uint8 mmc5_irq_out = 0; // stores 1 when IRQ is triggered +// for mapper #18 +static uint16 mapper18_irq_value = 0; // [15:0], counter itself (downcounting) +static uint8 mapper18_irq_control = 0; // [3:0], IRQ settings +static uint16 mapper18_irq_latch = 0; // [15:0], stores counter reload latch value +// for mapper #65 +static uint8 mapper65_irq_enabled = 0; // register to enable/disable IRQ +static uint16 mapper65_irq_value = 0; // [15:0], counter itself (downcounting) +static uint16 mapper65_irq_latch = 0; // [15:0], stores counter reload latch value +// reg mapper65_irq_out = 0; +// for Sunsoft FME-7 +static uint8 mapper69_irq_enabled = 0; // register to enable/disable IRQ +static uint8 mapper69_counter_enabled = 0; // register to enable/disable counter +static uint16 mapper69_irq_value = 0; // counter itself (downcounting) +// for VRC4 CPU-based interrupts +static uint8 vrc4_irq_value = 0; // [7:0], counter itself (upcounting) +static uint8 vrc4_irq_control = 0; // [2:0]� IRQ settings +static uint8 vrc4_irq_latch = 0; // [7:0], stores counter reload latch value +static uint8 vrc4_irq_prescaler = 0; // [6:0], prescaler counter for VRC4 +static uint8 vrc4_irq_prescaler_counter = 0; // prescaler cicles counter for VRC4 +// for VRC3 CPU-based interrupts +static uint16 vrc3_irq_value = 0; // [15:0], counter itself (upcounting) +static uint8 vrc3_irq_control = 0; // [3:0], IRQ settings +static uint16 vrc3_irq_latch = 0; // [15:0], stores counter reload latch value +// for mapper #42 (only Baby Mario) +static uint8 mapper42_irq_enabled = 0; // register to enable/disable counter +static uint16 mapper42_irq_value = 0; // [14:0], counter itself (upcounting) +// for mapper #83 +static uint8 mapper83_irq_enabled_latch = 0; +static uint8 mapper83_irq_enabled = 0; +static uint16 mapper83_irq_counter = 0; +// for mapper #90 +static uint8 mapper90_xor = 0; +// for mapper #67 +static uint8 mapper67_irq_enabled = 0; +static uint8 mapper67_irq_latch = 0; +static uint16 mapper67_irq_counter = 0; + +static uint8 flash_state = 0; +static uint16 flash_buffer_a[10]; +static uint8 flash_buffer_v[10]; +static uint8 cfi_mode = 0; + +// Micron 4-gbit memory CFI data +static const uint8 cfi_data[] = +{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x52, 0x59, 0x02, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x36, 0x00, 0x00, 0x06, + 0x06, 0x09, 0x13, 0x03, 0x05, 0x03, 0x02, 0x1E, + 0x02, 0x00, 0x06, 0x00, 0x01, 0xFF, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0x50, 0x52, 0x49, 0x31, 0x33, 0x14, 0x02, 0x01, + 0x00, 0x08, 0x00, 0x00, 0x02, 0xB5, 0xC5, 0x05, + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +#define SET_BITS(target, target_bits, source, source_bits) target = set_bits(target, target_bits, get_bits(source, source_bits)) + +static inline uint8 string_to_bits(const char* bitsstr, int* bits) +{ + uint8 bit1, bit2, count = 0; + for (int i = 0; i < 32; i++) + bits[i] = -1; + while (*bitsstr) + { + bit1 = 0; + bit2 = 0; + if (isdigit(*bitsstr)) + { + while (isdigit(*bitsstr)) + { + bit1 *= 10; + bit1 += *bitsstr - '0'; + bitsstr++; + } + if (*bitsstr == ':') + { + bitsstr++; + while (isdigit(*bitsstr)) + { + bit2 *= 10; + bit2 += *bitsstr - '0'; + bitsstr++; + } + if (bit2 < bit1) + for (int i = bit1; i >= bit2; i--) + { + bits[count] = i; + count++; + } + else + for (int i = bit1; i <= bit2; i++) + { + bits[count] = i; + count++; + } + } + else { + bits[count] = bit1; + count++; + } + } + else { + bitsstr++; + } + } + return count; +} + +static inline uint32 get_bits(const uint32 V, const char* bitsstr) +{ + uint32 result = 0; + int bits[32]; + string_to_bits(bitsstr, bits); + for (int i = 0; bits[i] >= 0; i++) + { + result <<= 1; + result |= (V >> bits[i]) & 1; + } + return result; +} + +static inline uint32 set_bits(uint32 V, const char* bitsstr, const uint32 new_bits) +{ + int bits[32]; + uint8 count = string_to_bits(bitsstr, bits); + for (int i = 0; i < count; i++) + { + if ((new_bits >> (count - i - 1)) & 1) + V |= 1 << bits[i]; + else + V &= ~(1 << bits[i]); + } + return V; +} + +static void COOLGIRL_Sync_PRG(void) { + prg_bank_6000_mapped = (prg_base >> 13) | (prg_bank_6000 & ((~(prg_mask >> 13) & 0xFE) | 1)); + prg_bank_a_mapped = (prg_base >> 13) | (prg_bank_a & ((~(prg_mask >> 13) & 0xFE) | 1)); + prg_bank_b_mapped = (prg_base >> 13) | (prg_bank_b & ((~(prg_mask >> 13) & 0xFE) | 1)); + prg_bank_c_mapped = (prg_base >> 13) | (prg_bank_c & ((~(prg_mask >> 13) & 0xFE) | 1)); + prg_bank_d_mapped = (prg_base >> 13) | (prg_bank_d & ((~(prg_mask >> 13) & 0xFE) | 1)); + uint8 REG_A_CHIP = (SAVE_FLASH != NULL && prg_bank_a_mapped >= 0x20000 - SAVE_FLASH_SIZE / 1024 / 8) ? FLASH_CHIP : ROM_CHIP; + uint8 REG_B_CHIP = (SAVE_FLASH != NULL && prg_bank_b_mapped >= 0x20000 - SAVE_FLASH_SIZE / 1024 / 8) ? FLASH_CHIP : ROM_CHIP; + uint8 REG_C_CHIP = (SAVE_FLASH != NULL && prg_bank_c_mapped >= 0x20000 - SAVE_FLASH_SIZE / 1024 / 8) ? FLASH_CHIP : ROM_CHIP; + uint8 REG_D_CHIP = (SAVE_FLASH != NULL && prg_bank_d_mapped >= 0x20000 - SAVE_FLASH_SIZE / 1024 / 8) ? FLASH_CHIP : ROM_CHIP; + + if (!cfi_mode || !SAVE_FLASH) + { + switch (prg_mode & 7) + { + default: + case 0: + setprg16r(REG_A_CHIP, 0x8000, prg_bank_a_mapped >> 1); + setprg16r(REG_C_CHIP, 0xC000, prg_bank_c_mapped >> 1); + break; + case 1: + setprg16r(REG_C_CHIP, 0x8000, prg_bank_c_mapped >> 1); + setprg16r(REG_A_CHIP, 0xC000, prg_bank_a_mapped >> 1); + break; + case 4: + setprg8r(REG_A_CHIP, 0x8000, prg_bank_a_mapped); + setprg8r(REG_B_CHIP, 0xA000, prg_bank_b_mapped); + setprg8r(REG_C_CHIP, 0xC000, prg_bank_c_mapped); + setprg8r(REG_D_CHIP, 0xE000, prg_bank_d_mapped); + break; + case 5: + setprg8r(REG_C_CHIP, 0x8000, prg_bank_c_mapped); + setprg8r(REG_B_CHIP, 0xA000, prg_bank_b_mapped); + setprg8r(REG_A_CHIP, 0xC000, prg_bank_a_mapped); + setprg8r(REG_D_CHIP, 0xE000, prg_bank_d_mapped); + break; + case 6: + setprg32r(REG_A_CHIP, 0x8000, prg_bank_b_mapped >> 2); + break; + case 7: + setprg32r(REG_A_CHIP, 0x8000, prg_bank_a_mapped >> 2); + break; + } + } + else { + setprg32r(CFI_CHIP, 0x8000, 0); + } + + if (!map_rom_on_6000 && WRAM) + setprg8r(WRAM_CHIP, 0x6000, sram_page); // Select SRAM page + else if (map_rom_on_6000) + setprg8(0x6000, prg_bank_6000_mapped); // Map ROM on $6000-$7FFF +} + +static void COOLGIRL_Sync_CHR(void) { + // calculate CHR shift + // wire shift_chr_right = ENABLE_MAPPER_021_022_023_025 && ENABLE_MAPPER_022 && (mapper == 6'b011000) && flags[1]; + int chr_shift_right = ((mapper == 0b011000) && (flags & 0b010)) ? 1 : 0; + int chr_shift_left = 0; + + // enable or disable writes to CHR RAM, setup CHR mask + SetupCartCHRMapping(0, UNIFchrrama, ((((~(chr_mask >> 13) & 0x3F) + 1) * 0x2000 - 1) & (CHR_SIZE - 1)) + 1, can_write_chr); + + switch (chr_mode & 7) + { + default: + case 0: + setchr8(chr_bank_a >> 3 >> chr_shift_right << chr_shift_left); + break; + case 1: + setchr4(0x0000, mapper_163_latch >> chr_shift_right << chr_shift_left); + setchr4(0x1000, mapper_163_latch >> chr_shift_right << chr_shift_left); + break; + case 2: + setchr2(0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left); + TKSMIR[0] = TKSMIR[1] = chr_bank_a; + setchr2(0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left); + TKSMIR[2] = TKSMIR[3] = chr_bank_c; + setchr1(0x1000, chr_bank_e >> chr_shift_right << chr_shift_left); + TKSMIR[4] = chr_bank_e; + setchr1(0x1400, chr_bank_f >> chr_shift_right << chr_shift_left); + TKSMIR[5] = chr_bank_f; + setchr1(0x1800, chr_bank_g >> chr_shift_right << chr_shift_left); + TKSMIR[6] = chr_bank_g; + setchr1(0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left); + TKSMIR[7] = chr_bank_h; + break; + case 3: + setchr1(0x0000, chr_bank_e >> chr_shift_right << chr_shift_left); + TKSMIR[0] = chr_bank_e; + setchr1(0x0400, chr_bank_f >> chr_shift_right << chr_shift_left); + TKSMIR[1] = chr_bank_f; + setchr1(0x0800, chr_bank_g >> chr_shift_right << chr_shift_left); + TKSMIR[2] = chr_bank_g; + setchr1(0x0C00, chr_bank_h >> chr_shift_right << chr_shift_left); + TKSMIR[3] = chr_bank_h; + setchr2(0x1000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left); + TKSMIR[4] = TKSMIR[5] = chr_bank_a; + setchr2(0x1800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left); + TKSMIR[6] = TKSMIR[7] = chr_bank_c; + break; + case 4: + setchr4(0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left); + setchr4(0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left); + break; + case 5: + if (!ppu_latch0) + setchr4(0x0000, chr_bank_a >> 2 >> chr_shift_right << chr_shift_left); + else + setchr4(0x0000, chr_bank_b >> 2 >> chr_shift_right << chr_shift_left); + if (!ppu_latch1) + setchr4(0x1000, chr_bank_e >> 2 >> chr_shift_right << chr_shift_left); + else + setchr4(0x1000, chr_bank_f >> 2 >> chr_shift_right << chr_shift_left); + break; + case 6: + setchr2(0x0000, chr_bank_a >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x0800, chr_bank_c >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x1000, chr_bank_e >> 1 >> chr_shift_right << chr_shift_left); + setchr2(0x1800, chr_bank_g >> 1 >> chr_shift_right << chr_shift_left); + break; + case 7: + setchr1(0x0000, chr_bank_a >> chr_shift_right << chr_shift_left); + setchr1(0x0400, chr_bank_b >> chr_shift_right << chr_shift_left); + setchr1(0x0800, chr_bank_c >> chr_shift_right << chr_shift_left); + setchr1(0x0C00, chr_bank_d >> chr_shift_right << chr_shift_left); + setchr1(0x1000, chr_bank_e >> chr_shift_right << chr_shift_left); + setchr1(0x1400, chr_bank_f >> chr_shift_right << chr_shift_left); + setchr1(0x1800, chr_bank_g >> chr_shift_right << chr_shift_left); + setchr1(0x1C00, chr_bank_h >> chr_shift_right << chr_shift_left); + break; + } +} + +static void COOLGIRL_Sync_Mirroring(void) { + if (!four_screen) + { + if (!((mapper == 0b010100) && (flags & 1))) // Mapper #189? + setmirror((mirroring < 2) ? (mirroring ^ 1) : mirroring); + } + else { // four screen mode + vnapage[0] = UNIFchrrama + 0x3F000; + vnapage[1] = UNIFchrrama + 0x3F400; + vnapage[2] = UNIFchrrama + 0x3F800; + vnapage[3] = UNIFchrrama + 0x3FC00; + } +} + +static void COOLGIRL_Sync(void) { + COOLGIRL_Sync_PRG(); + COOLGIRL_Sync_CHR(); + COOLGIRL_Sync_Mirroring(); +} + +static DECLFW(COOLGIRL_Flash_Write) { + if (flash_state < sizeof(flash_buffer_a) / sizeof(flash_buffer_a[0])) + { + flash_buffer_a[flash_state] = A & 0xFFF; + flash_buffer_v[flash_state] = V; + flash_state++; + + // enter CFI mode + if ((flash_state == 1) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0x98)) + { + cfi_mode = 1; + flash_state = 0; + } + + // sector erase + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x0AAA) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x0555) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x30)) + { + int sector = prg_bank_a_mapped * 0x2000 / FLASH_SECTOR_SIZE; + uint32 sector_address = sector * FLASH_SECTOR_SIZE; + for (uint32 i = sector_address; i < sector_address + FLASH_SECTOR_SIZE; i++) + SAVE_FLASH[i % SAVE_FLASH_SIZE] = 0xFF; + flash_state = 0; + } + + // write byte + if ((flash_state == 4) && + (flash_buffer_a[0] == 0x0AAA) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x0555) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x0AAA) && (flash_buffer_v[2] == 0xA0)) + { + //int sector = prg_bank_a_mapped * 0x2000 / FLASH_SECTOR_SIZE; + uint32 flash_addr = prg_bank_a_mapped * 0x2000 + (A % 0x8000); + if (SAVE_FLASH[flash_addr % SAVE_FLASH_SIZE] != 0xFF) { + FCEU_PrintError("Error: can't write to 0x%08x, flash sector is not erased.\n", flash_addr); + } + else { + SAVE_FLASH[flash_addr % SAVE_FLASH_SIZE] = V; + } + flash_state = 0; + } + } + + // not a command + if (((A & 0xFFF) != 0x0AAA) && ((A & 0xFFF) != 0x0555)) { + flash_state = 0; + } + + // reset + if (V == 0xF0) { + flash_state = 0; + cfi_mode = 0; + } + + COOLGIRL_Sync_PRG(); +} + +static DECLFW(COOLGIRL_WRITE) { + if (sram_enabled && A >= 0x6000 && A < 0x8000 && !map_rom_on_6000) + CartBW(A, V); // SRAM is enabled and writable + if (SAVE_FLASH && can_write_flash && A >= 0x8000) // writing flash + COOLGIRL_Flash_Write(A, V); + + // block two writes in a row + if ((timestampbase + timestamp) < (lreset + 2)) return; + lreset = timestampbase + timestamp; + + if (A >= 0x5000 && A < 0x6000 && !lockout) + { + switch (A & 7) + { + case 0: + // {prg_base[26:22]} = cpu_data_in[4:0]; + // use bits 29-27 to simulate flash memory + SET_BITS(prg_base, "29:22", V, "7:0"); + break; + case 1: + // prg_base[21:14] = cpu_data_in[7:0]; + SET_BITS(prg_base, "21:14", V, "7:0"); + break; + case 2: + // {chr_mask[18], prg_mask[20:14]} = cpu_data_in[7:0]; + SET_BITS(chr_mask, "18", V, "7"); + SET_BITS(prg_mask, "20:14", V, "6:0"); + break; + case 3: + // {prg_mode[2:0], chr_bank_a[7:3]} = cpu_data_in[7:0]; + SET_BITS(prg_mode, "2:0", V, "7:5"); + SET_BITS(chr_bank_a, "7:3", V, "4:0"); + break; + case 4: + // {chr_mode[2:0], chr_mask[17:13]} = cpu_data_in[7:0]; + SET_BITS(chr_mode, "2:0", V, "7:5"); + SET_BITS(chr_mask, "17:13", V, "4:0"); + break; + case 5: + // {chr_bank_a[8], prg_bank_a[5:1], sram_page[1:0]} = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "8", V, "7"); + SET_BITS(prg_bank_a, "5:1", V, "6:2"); + SET_BITS(sram_page, "1:0", V, "1:0"); + break; + case 6: + // {flags[2:0], mapper[4:0]} = cpu_data_in[7:0]; + SET_BITS(flags, "2:0", V, "7:5"); + SET_BITS(mapper, "4:0", V, "4:0"); + break; + case 7: + // {lockout, mapper[5], four_screen, mirroring[1:0], prg_write_enabled, chr_write_enabled, sram_enabled} = cpu_data_in[7:0]; + SET_BITS(lockout, "0", V, "7"); + SET_BITS(mapper, "5", V, "6"); + SET_BITS(four_screen, "0", V, "5"); + SET_BITS(mirroring, "1:0", V, "4:3"); + SET_BITS(can_write_flash, "0", V, "2"); + SET_BITS(can_write_chr, "0", V, "1"); + SET_BITS(sram_enabled, "0", V, "0"); + if (mapper == 0b010001) prg_bank_b = ~2; // if (USE_MAPPER_009_010 && mapper == 6'b010001) prg_bank_b = 8'b11111101; + if (mapper == 0b010111) map_rom_on_6000 = 1; // if (ENABLE_MAPPER_042 && (mapper == 6'b010111)) map_rom_on_6000 <= 1; + if (mapper == 0b001110) prg_bank_b = 1; // if (USE_MAPPER_065 && mapper == 6'b001110) prg_bank_b = 1; + break; + } + } + + if (A < 0x8000) // $0000-$7FFF + { + // Mapper #163 + if (mapper == 0b000110) + { + if (A == 0x5101) // if (cpu_addr_in[14:0] == 15'h5101) + { + if (mapper163_r4 && !V) // if ((mapper163_r4 != 0) && (cpu_data_in == 0)) + mapper163_r5 ^= 1; // mapper163_r5[0] = ~mapper163_r5[0]; + mapper163_r4 = V; + } + else if (A == 0x5100 && V == 6) // if ((cpu_addr_in[14:0] == 15'h5100) && (cpu_data_in == 6)) + { + SET_BITS(prg_mode, "0", 0, "0"); // prg_mode[0] = 0; + prg_bank_b = 0b1100; // prb_bank_b = 4'b1100; + } + else { + if (get_bits(A, "14:12") == 0b101) // if (cpu_addr_in[14:12] == 3'b101) begin + { + switch (get_bits(A, "9:8")) // case (cpu_addr_in[9:8]) + { + case 2: + SET_BITS(prg_mode, "0", 1, "0"); // prg_mode[0] = 1; + SET_BITS(prg_bank_a, "7:6", V, "1:0"); // prg_bank_a[7:6] = cpu_data_in[1:0]; + mapper163_r0 = V; + break; + case 0: + SET_BITS(prg_mode, "0", 1, "0"); // prg_mode[0] = 1; + SET_BITS(prg_bank_a, "5:2", V, "3:0"); // prg_bank_a[5:2] = cpu_data_in[3:0]; + SET_BITS(chr_mode, "0", V, "7"); // chr_mode[0] = cpu_data_in[7]; + mapper163_r1 = V; + break; + case 3: + mapper163_r2 = V; // mapper163_r2 = cpu_data_in; + break; + case 1: + mapper163_r3 = V; // mapper163_r3 = cpu_data_in; + break; + } + } + } + } + + // Mapper #87 + if (mapper == 0b001100) + { + if (get_bits(A, "14:13") == 0b11) // if (cpu_addr_in[14:13] == 2'b11) // $6000-$7FFF + { + // chr_bank_a[4:3] = {cpu_data_in[0], cpu_data_in[1]}; + SET_BITS(chr_bank_a, "4:3", V, "0,1"); + } + } + + // Mapper #90 - JY + /* + if (mapper == 0b001101) + { + switch (A) + { + case 0x5800: mul1 = V; break; + case 0x5801: mul2 = V; break; + } + } + */ + + // MMC5 (not really) + if (mapper == 0b001111) + { + // case (cpu_addr_in[14:0]) + switch (get_bits(A, "14:0")) + { + case 0x5105: + if (V == 0xFF) + { + four_screen = 1; + } + else { + four_screen = 0; + // case ({cpu_data_in[4], cpu_data_in[2]}) + switch (get_bits(V, "4,2")) + { + case 0b00: // 2'b00: mirroring = 2'b10; + mirroring = 0b10; break; + case 0b01: // 2'b01: mirroring = 2'b00; + mirroring = 0b00; break; + case 0b10: // 2'b10: mirroring = 2'b01; + mirroring = 0b01; break; + case 0b11: // 2'b11: mirroring = 2'b11; + mirroring = 0b11; break; + } + } + break; + case 0x5115: + // prg_bank_a[4:0] = { cpu_data_in[4:1], 1'b0}; + SET_BITS(prg_bank_a, "4:1", V, "4:1"); + SET_BITS(prg_bank_a, "0", 0, "0"); + // prg_bank_b[4:0] = { cpu_data_in[4:1], 1'b1}; + SET_BITS(prg_bank_b, "4:1", V, "4:1"); + SET_BITS(prg_bank_b, "0", 1, "0"); + break; + case 0x5116: + // prg_bank_c[4:0] = cpu_data_in[4:0]; + SET_BITS(prg_bank_c, "4:0", V, "4:0"); + break; + case 0x5117: + // prg_bank_d[4:0] = cpu_data_in[4:0]; + SET_BITS(prg_bank_d, "4:0", V, "4:0"); + break; + case 0x5120: + // chr_bank_a[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "7:0", V, "7:0"); + break; + case 0x5121: + // chr_bank_b[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_b, "7:0", V, "7:0"); + break; + case 0x5122: + // chr_bank_c[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_c, "7:0", V, "7:0"); + break; + case 0x5123: + // chr_bank_d[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_d, "7:0", V, "7:0"); + break; + case 0x5128: + // chr_bank_e[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_e, "7:0", V, "7:0"); + break; + case 0x5129: + // chr_bank_f[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_f, "7:0", V, "7:0"); + break; + case 0x512A: + // chr_bank_g[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_g, "7:0", V, "7:0"); + break; + case 0x512B: + // chr_bank_h[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_h, "7:0", V, "7:0"); + break; + case 0x5203: + // mmc5_irq_ack = 1; + X6502_IRQEnd(FCEU_IQEXT); + mmc5_irq_out = 0; + // mmc5_irq_line[7:0] = cpu_data_in[7:0]; + SET_BITS(mmc5_irq_line, "7:0", V, "7:0"); + break; + case 0x5204: + // mmc5_irq_ack = 1; + X6502_IRQEnd(FCEU_IQEXT); + mmc5_irq_out = 0; + // mmc5_irq_enabled = cpu_data_in[7]; + mmc5_irq_enabled = get_bits(V, "7"); + break; + } + } + + // Mapper #189 + if ((mapper == 0b010100) && (flags & 2)) + { + if (A >= 0x4120) // if (cpu_addr_in[14:0] >= 15'h4120) // $4120-$7FFF + { + // prg_bank_a[5:2] = cpu_data_in[3:0] | cpu_data_in[7:4]; + prg_bank_a = set_bits(prg_bank_a, "5:2", + get_bits(V, "7:4") | get_bits(V, "3:0")); + } + } + + // Mappers #79 and #146 - NINA-03/06 and Sachen 3015: (flag0 = 1) + if (mapper == 0b011011) + { + // if ({cpu_addr_in[14:13], cpu_addr_in[8]} == 3'b101) + if (get_bits(A, "14:13,8") == 0b101) + { + //chr_bank_a[5:3] = cpu_data_in[2:0]; + SET_BITS(chr_bank_a, "5:3", V, "2:0"); + //prg_bank_a[2] = cpu_data_in[3]; + SET_BITS(prg_bank_a, "2", V, "3"); + } + } + + // Mapper #133 + if (mapper == 0b011100) + { + // if ({cpu_addr_in[14:13], cpu_addr_in[8]} == 3'b101) + if (get_bits(A, "14:13,8") == 0b101) + { + //chr_bank_a[4:3] = cpu_data_in[1:0]; + SET_BITS(chr_bank_a, "4:3", V, "1:0"); + //prg_bank_a[2] = cpu_data_in[2]; + SET_BITS(prg_bank_a, "2", V, "2"); + } + } + + // Mapper #184 + if (mapper == 0b011111) + { + if (get_bits(A, "14:13") == 0b11) // if (cpu_addr_in[14:13] == 2'b11) + { + // chr_bank_a[4:2] = cpu_data_in[2:0]; + SET_BITS(chr_bank_a, "4:2", V, "2:0"); + // chr_bank_e[4:2] = {1'b1, cpu_data_in[5:4]}; + SET_BITS(chr_bank_e, "3:2", V, "5:4"); + SET_BITS(chr_bank_e, "4", 1, "0"); + } + } + + // Mapper #38 + if (mapper == 0b100000) + { + // if (cpu_addr_in[14:12] == 3'b111) + if (get_bits(A, "14:12") == 0b111) + { + // prg_bank_a[3:2] = cpu_data_in[1:0]; + SET_BITS(prg_bank_a, "3:2", V, "1:0"); + // chr_bank_a[4:3] = cpu_data_in[3:2]; + SET_BITS(chr_bank_a, "4:3", V, "3:2"); + } + } + } + else // $8000-$FFFF + { + // Mapper #2 - UxROM + // flag0 - mapper #71 - for Fire Hawk only. + // other mapper-#71 games are UxROM + if (mapper == 0b000001) + { + // if (!ENABLE_MAPPER_071 | ~flags[0] | (cpu_addr_in[14:12] != 3'b001)) + if (!(flags & 1) || (get_bits(A, "14:12") != 0b001)) + { + // prg_bank_a[UxROM_BITSIZE+1:1] = cpu_data_in[UxROM_BITSIZE:0]; + // UxROM_BITSIZE = 4 + SET_BITS(prg_bank_a, "5:1", V, "4:0"); + // if (ENABLE_MAPPER_030 && flags[1]) + if (flags & 2) + { + // One screen mirroring select, CHR RAM bank, PRG ROM bank + // mirroring[1:0] = { 1'b1, cpu_data_in[7]}; + SET_BITS(mirroring, "1", 1, "0"); + SET_BITS(mirroring, "0", V, "7"); + // chr_bank_a[1:0] = cpu_data_in[6:5]; + SET_BITS(chr_bank_a, "1:0", V, "6:5"); + } + } + else { + // CodeMasters, blah. Mirroring control used only by Fire Hawk + // mirroring[1:0] = {1'b1, cpu_data_in[4]}; + SET_BITS(mirroring, "1", 1, "0"); + SET_BITS(mirroring, "0", V, "4"); + } + } + + // Mapper #3 - CNROM + if (mapper == 0b000010) + { + // chr_bank_a[7:3] = cpu_data_in[4:0]; + SET_BITS(chr_bank_a, "7:3", V, "4:0"); + } + + // Mapper #78 - Holy Diver + if (mapper == 0b000011) + { + // prg_bank_a[3:1] = cpu_data_in[2:0]; + SET_BITS(prg_bank_a, "3:1", V, "2:0"); + // chr_bank_a[6:3] = cpu_data_in[7:4]; + SET_BITS(chr_bank_a, "6:3", V, "7:4"); + // mirroring = { 1'b0, ~cpu_data_in[3]}; + mirroring = get_bits(V, "3") ^ 1; + } + + // Mapper #97 - Irem's TAM-S1 + if (mapper == 0b000100) + { + // prg_bank_a[5:1] = cpu_data_in[4:0]; + SET_BITS(prg_bank_a, "5:1", V, "4:0"); + // mirroring = { 1'b0, ~cpu_data_in[7]}; + mirroring = get_bits(V, "7") ^ 1; + } + + // Mapper #93 - Sunsoft-2 + if (mapper == 0b000101) + { + // prg_bank_a[3:1] = { cpu_data_in[6:4] }; + SET_BITS(prg_bank_a, "3:1", V, "6:4"); + // chr_write_enabled = cpu_data_in[0]; + can_write_chr = V & 1; + } + + // Mapper #18 + if (mapper == 0b000111) + { + // case ({cpu_addr_in[14:12], cpu_addr_in[1:0]}) + switch (get_bits(A, "14:12,1:0")) + { + case 0b00000: // 5'b00000: prg_bank_a[3:0] = cpu_data_in[3:0]; // $8000 + SET_BITS(prg_bank_a, "3:0", V, "3:0"); break; + case 0b00001: // 5'b00001: prg_bank_a[7:4] = cpu_data_in[3:0]; // $8001 + SET_BITS(prg_bank_a, "7:4", V, "3:0"); break; + case 0b00010: // 5'b00010: prg_bank_b[3:0] = cpu_data_in[3:0]; // $8002 + SET_BITS(prg_bank_b, "3:0", V, "3:0"); break; + case 0b00011: // 5'b00011: prg_bank_b[7:4] = cpu_data_in[3:0]; // $8003 + SET_BITS(prg_bank_b, "7:4", V, "3:0"); break; + case 0b00100: // 5'b00100: prg_bank_c[3:0] = cpu_data_in[3:0]; // $9000 + SET_BITS(prg_bank_c, "3:0", V, "3:0"); break; + case 0b00101: // 5'b00101: prg_bank_c[7:4] = cpu_data_in[3:0]; // $9001 + SET_BITS(prg_bank_c, "7:4", V, "3:0"); break; + case 0b00110: + break; + case 0b00111: + break; + case 0b01000: // 5'b01000: chr_bank_a[3:0] = cpu_data_in[3:0]; // $A000 + SET_BITS(chr_bank_a, "3:0", V, "3:0"); break; + case 0b01001: // 5'b01001: chr_bank_a[7:4] = cpu_data_in[3:0]; // $A001 + SET_BITS(chr_bank_a, "7:4", V, "3:0"); break; + case 0b01010: // 5'b01010: chr_bank_b[3:0] = cpu_data_in[3:0]; // $A002 + SET_BITS(chr_bank_b, "3:0", V, "3:0"); break; + case 0b01011: // 5'b01011: chr_bank_b[7:4] = cpu_data_in[3:0]; // $A003 + SET_BITS(chr_bank_b, "7:4", V, "3:0"); break; + case 0b01100: // 5'b01100: chr_bank_c[3:0] = cpu_data_in[3:0]; // $B000 + SET_BITS(chr_bank_c, "3:0", V, "3:0"); break; + case 0b01101: // 5'b01101: chr_bank_c[7:4] = cpu_data_in[3:0]; // $B001 + SET_BITS(chr_bank_c, "7:4", V, "3:0"); break; + case 0b01110: // 5'b01110: chr_bank_d[3:0] = cpu_data_in[3:0]; // $B002 + SET_BITS(chr_bank_d, "3:0", V, "3:0"); break; + case 0b01111: // 5'b01111: chr_bank_d[7:4] = cpu_data_in[3:0]; // $B003 + SET_BITS(chr_bank_d, "7:4", V, "3:0"); break; + case 0b10000: // 5'b10000: chr_bank_e[3:0] = cpu_data_in[3:0]; // $C000 + SET_BITS(chr_bank_e, "3:0", V, "3:0"); break; + case 0b10001: // 5'b10001: chr_bank_e[7:4] = cpu_data_in[3:0]; // $C001 + SET_BITS(chr_bank_e, "7:4", V, "3:0"); break; + case 0b10010: // 5'b10010: chr_bank_f[3:0] = cpu_data_in[3:0]; // $C002 + SET_BITS(chr_bank_f, "3:0", V, "3:0"); break; + case 0b10011: // 5'b10011: chr_bank_f[7:4] = cpu_data_in[3:0]; // $C003 + SET_BITS(chr_bank_f, "7:4", V, "3:0"); break; + case 0b10100: // 5'b10100: chr_bank_g[3:0] = cpu_data_in[3:0]; // $D000 + SET_BITS(chr_bank_g, "3:0", V, "3:0"); break; + case 0b10101: // 5'b10101: chr_bank_g[7:4] = cpu_data_in[3:0]; // $D001 + SET_BITS(chr_bank_g, "7:4", V, "3:0"); break; + case 0b10110: // 5'b10110: chr_bank_h[3:0] = cpu_data_in[3:0]; // $D002 + SET_BITS(chr_bank_h, "3:0", V, "3:0"); break; + case 0b10111: // 5'b10111: chr_bank_h[7:4] = cpu_data_in[3:0]; // $D003 + SET_BITS(chr_bank_h, "7:4", V, "3:0"); break; + case 0b11000: // 5'b11000: mapper18_irq_latch[3:0] = cpu_data_in[3:0]; // $E000 + SET_BITS(mapper18_irq_latch, "3:0", V, "3:0"); break; + case 0b11001: // 5'b11001: mapper18_irq_latch[7:4] = cpu_data_in[3:0]; // $E001 + SET_BITS(mapper18_irq_latch, "7:4", V, "3:0"); break; + case 0b11010: // 5'b11010: mapper18_irq_latch[11:8] = cpu_data_in[3:0]; // $E002 + SET_BITS(mapper18_irq_latch, "11:8", V, "3:0"); break; + case 0b11011: // 5'b11011: mapper18_irq_latch[15:12] = cpu_data_in[3:0]; // $E003 + SET_BITS(mapper18_irq_latch, "15:12", V, "3:0"); break; + case 0b11100: // 5'b11100: begin // $F000 + // mapper18_irq_out = 0; // ack + X6502_IRQEnd(FCEU_IQEXT); + // mapper18_irq_value[15:0] = mapper18_irq_latch[15:0]; + mapper18_irq_value = mapper18_irq_latch; break; // irq_cpu_out = 0; + case 0b11101: // 5'b11101: begin // $F001 + X6502_IRQEnd(FCEU_IQEXT); // irq_cpu_control[3:0] = cpu_data_in[3:0]; + SET_BITS(mapper18_irq_control, "3:0", V, "3:0"); break; + case 0b11110: // 5'b11110 + switch (get_bits(V, "1:0")) // case (cpu_data_in[1:0]) + { + case 0b00: mirroring = 0b01; break; //2'b00: mirroring = 2'b01; // Horz + case 0b01: mirroring = 0b00; break; //2'b01: mirroring = 2'b00; // Vert + case 0b10: mirroring = 0b10; break; //2'b10: mirroring = 2'b10; // 1SsA + case 0b11: mirroring = 0b11; break; //2'b11: mirroring = 2'b11; // 1SsB + } + case 0b11111: + break; // sound + } + } + + // Mapper #7 - AxROM, mapper #241 - BNROM + if (mapper == 0b001000) + { + // AxROM_BxROM_BITSIZE = 3 + //prg_bank_a[AxROM_BxROM_BITSIZE + 2:2] = cpu_data_in[AxROM_BxROM_BITSIZE:0]; + SET_BITS(prg_bank_a, "5:2", V, "3:0"); + //if (!ENABLE_MAPPER_034_241_BxROM || !flags[0]) // BxROM? + // mirroring = { 1'b1, cpu_data_in[4]}; + if (!(flags & 1)) + mirroring = (1 << 1) | get_bits(V, "4"); + } + + // Mapper #228 - Cheetahmen II + if (mapper == 0b001001) + { + // prg_bank_a[5:2] = cpu_addr_in[10:7]; + SET_BITS(prg_bank_a, "5:2", A, "10:7"); + // chr_bank_a[7:3] = { cpu_addr_in[2:0], cpu_data_in[1:0] }; // only 256k, sorry + SET_BITS(chr_bank_a, "7:5", A, "2:0"); + SET_BITS(chr_bank_a, "4:3", V, "1:0"); + // mirroring = { 1'b0, cpu_addr_in[13]}; + mirroring = get_bits(A, "13"); + } + + // Mapper #11 - ColorDreams + if (mapper == 0b001010) + { + // prg_bank_a[3:2] = cpu_data_in[1:0]; + SET_BITS(prg_bank_a, "3:2", V, "1:0"); + // chr_bank_a[6:3] = cpu_data_in[7:4]; + SET_BITS(chr_bank_a, "6:3", V, "7:4"); + } + + // Mapper #66 - GxROM + if (mapper == 0b001011) + { + // prg_bank_a[3:2] = cpu_data_in[5:4]; + SET_BITS(prg_bank_a, "3:2", V, "5:4"); + // chr_bank_a[4:3] = cpu_data_in[1:0]; + SET_BITS(chr_bank_a, "4:3", V, "1:0"); + } + + // Mapper #90 - JY + if (mapper == 0b001101) + { + // if (cpu_addr_in[14:12] == 3'b000) // $800x + if (get_bits(A, "14:12") == 0b000) + { + // case (cpu_addr_in[1:0]) + switch (get_bits(A, "1:0")) + { + // 2'b00: prg_bank_a[5:0] = cpu_data_in[5:0]; + case 0b00: SET_BITS(prg_bank_a, "5:0", V, "5:0"); break; + // 2'b01: prg_bank_b[5:0] = cpu_data_in[5:0]; + case 0b01: SET_BITS(prg_bank_b, "5:0", V, "5:0"); break; + // 2'b10: prg_bank_c[5:0] = cpu_data_in[5:0]; + case 0b10: SET_BITS(prg_bank_c, "5:0", V, "5:0"); break; + // 2'b11: prg_bank_d[5:0] = cpu_data_in[5:0]; + case 0b11: SET_BITS(prg_bank_d, "5:0", V, "5:0"); break; + } + } + + // if (cpu_addr_in[14:12] == 3'b001) // $900x + if (get_bits(A, "14:12") == 0b001) + { + // case (cpu_addr_in[2:0]) + switch (get_bits(A, "2:0")) + { + case 0b000: // 3'b000: chr_bank_a[7:0] = cpu_data_in[7:0]; // $9000 + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b001: // 3'b001: chr_bank_b[7:0] = cpu_data_in[7:0]; // $9001 + SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; + case 0b010: // 3'b010: chr_bank_c[7:0] = cpu_data_in[7:0]; // $9002 + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b011: // 3'b011: chr_bank_d[7:0] = cpu_data_in[7:0]; // $9003 + SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; + case 0b100: // 3'b100: chr_bank_e[7:0] = cpu_data_in[7:0]; // $9004 + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b101: // 3'b101: chr_bank_f[7:0] = cpu_data_in[7:0]; // $9005 + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b110: // 3'b110: chr_bank_g[7:0] = cpu_data_in[7:0]; // $9006 + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b111: // 3'b111: chr_bank_h[7:0] = cpu_data_in[7:0]; // $9007 + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + } + } + + // if ({cpu_addr_in[14:12], cpu_addr_in[1:0]} == 5'b10101) // $D001 + if (get_bits(A, "14:12,1:0") == 0b10101) + { + // mirroring = cpu_data_in[1:0]; + SET_BITS(mirroring, "1:0", V, "1:0"); + } + + // use MMC3's IRQs + // if (cpu_addr_in[14:12] == 3'b100) // $C00x + if (get_bits(A, "14:12") == 0b100) + { + // case (cpu_addr_in[2:0]) + switch (get_bits(A, "2:0")) + { + case 0b000: + // 3'b000: mmc3_irq_enabled = cpu_data_in[0]; + if (V & 1) + { + mmc3_irq_enabled = 1; + } + else { + X6502_IRQEnd(FCEU_IQEXT); + mmc3_irq_enabled = 0; + } + break; + case 0b001: + break; // who cares about this shit? + case 0b010: + // 3'b010: mmc3_irq_enabled = 0; + mmc3_irq_enabled = 0; + X6502_IRQEnd(FCEU_IQEXT); + break; + case 0b011: + // 3'b011: mmc3_irq_enabled = 1; + mmc3_irq_enabled = 1; + break; + case 0b100: + break; // prescaler? who cares? + case 0b101: + // mmc3_irq_latch = cpu_data_in ^ mapper90_xor; + mmc3_irq_latch = V ^ mapper90_xor; + mmc3_irq_reload = 1; + break; + case 0b110: + // mapper90_xor = cpu_data_in; + mapper90_xor = V; + break; + case 0b111: + break; // meh + } + } + } + + // Mapper #65 - Irem's H3001 + if (mapper == 0b001110) + { + // case ({cpu_addr_in[14:12], cpu_addr_in[2:0]}) + switch (get_bits(A, "14:12,2:0")) + { + case 0b000000: + // 6'b000000: prg_bank_a[5:0] = cpu_data_in[5:0]; // $8000 + SET_BITS(prg_bank_a, "5:0", V, "5:0"); + break; + case 0b001001: + // 6'b001001: mirroring = {1'b0, cpu_data_in[7]}; // $9001, mirroring + mirroring = get_bits(V, "7"); + break; + case 0b001011: + // mapper65_irq_out = 0; // ack + X6502_IRQEnd(FCEU_IQEXT); // irq_cpu_out = 0; + // mapper65_irq_enabled = cpu_data_in[7]; // $9003, enable IRQ + mapper65_irq_enabled = get_bits(V, "7"); + break; + case 0b001100: + X6502_IRQEnd(FCEU_IQEXT); // mapper65_irq_out = 0; // ack + mapper65_irq_value = mapper65_irq_latch; // $9004, IRQ reload + break; + case 0b001101: // mapper65_irq_latch[15:8] = cpu_data_in; // $9005, IRQ high value + SET_BITS(mapper65_irq_latch, "15:8", V, "7:0"); break; + case 0b001110: // mapper65_irq_latch[7:0] = cpu_data_in; // $9006, IRQ low value + SET_BITS(mapper65_irq_latch, "7:0", V, "7:0"); break; + case 0b010000: // prg_bank_b[5:0] = cpu_data_in[5:0]; // $A000 + prg_bank_b = (prg_bank_b & 0b11000000) | (V & 0b00111111); break; + case 0b011000: // chr_bank_a[7:0] = cpu_data_in; // $B000 + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b011001: // chr_bank_b[7:0] = cpu_data_in[7:0]; // $B001 + SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; + case 0b011010: // chr_bank_c[7:0] = cpu_data_in[7:0]; // $B002 + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b011011: // chr_bank_d[7:0] = cpu_data_in[7:0]; // $B003 + SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; + case 0b011100: // chr_bank_e[7:0] = cpu_data_in[7:0]; // $B004 + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b011101: // chr_bank_f[7:0] = cpu_data_in[7:0]; // $B005 + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b011110: // chr_bank_g[7:0] = cpu_data_in[7:0]; // $B006 + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b011111: // chr_bank_h[7:0] = cpu_data_in[7:0]; // $B007 + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + case 0b100000: // 6'b100000: prg_bank_c[5:0] = cpu_data_in[5:0]; // $C000 + SET_BITS(prg_bank_c, "5:0", V, "5:0"); break; + } + } + + // Mapper #1 - MMC1 + /* + flag0 - 16KB of SRAM (SOROM) + */ + if (mapper == 0b010000) + { + if (V & 0x80) // reset + { + mmc1_load_register = set_bits(mmc1_load_register, "5:0", 0b100000); // mmc1_load_register[5:0] = 6'b100000; + prg_mode = 0; // 0x4000 (A) + fixed last (C) + prg_bank_c = set_bits(prg_bank_c, "4:0", 0b11110); // prg_bank_c[4:0] = 5'b11110; + } + else { + // mmc1_load_register[5:0] = { cpu_data_in[0], mmc1_load_register[5:1] }; + SET_BITS(mmc1_load_register, "4:0", mmc1_load_register, "5:1"); + SET_BITS(mmc1_load_register, "5", V, "0"); + // if (mmc1_load_register[0] == 1) + if (mmc1_load_register & 1) + { + switch ((A >> 13) & 3) + { + case 0b00: // 2'b00: begin // $8000-$9FFF + if (get_bits(mmc1_load_register, "4:3") == 0b11) // if (mmc1_load_register[4:3] == 2'b11) + { + prg_mode = 0; // prg_mode = 3'b000; // 0x4000 (A) + fixed last (C) + prg_bank_c = set_bits(prg_bank_c, "4:1", 0b1111); // prg_bank_c[4:1] = 4'b1111; + } + //else if (mmc1_load_register[4:3] == 2'b10) + else if (get_bits(mmc1_load_register, "4:3") == 0b10) + { + prg_mode = 0b001; // prg_mode = 3'b001; // fixed first (C) + 0x4000 (A) + prg_bank_c = set_bits(prg_bank_c, "4:1", 0b0000); // prg_bank_c[4:0] = 4'b0000; + } + else + prg_mode = 0b111; // prg_mode = 3'b111; // 0x8000 (A) + if (get_bits(mmc1_load_register, "5")) + chr_mode = 0b100; + else + chr_mode = 0b000; + mirroring = set_bits(mirroring, "1:0", get_bits(mmc1_load_register, "2:1") ^ 0b10); + break; + case 0b01: // 2'b01 + SET_BITS(chr_bank_a, "6:2", mmc1_load_register, "5:1"); // chr_bank_a[6:2] = mmc1_load_register[5:1]; + if (flags & 1) // (flags[0]) - 16KB of SRAM + { + // PRG RAM page #2 is battery backed + sram_page = 2 | get_bits(mmc1_load_register, "4") ^ 1; // sram_page <= {1'b1, ~mmc1_load_register[4]}; + } + SET_BITS(prg_bank_a, "5", mmc1_load_register, "5"); // prg_bank_a[5] = mmc1_load_register[5]; // for SUROM, 512k PRG support + SET_BITS(prg_bank_c, "5", mmc1_load_register, "5"); // prg_bank_c[5] = mmc1_load_register[5]; // for SUROM, 512k PRG support + break; + case 0b10: // 2'b10: chr_bank_e[6:2] = mmc1_load_register[5:1]; // $C000-$DFFF + SET_BITS(chr_bank_e, "6:2", mmc1_load_register, "5:1"); + break; + case 0b11: // 2'b11 + // prg_bank_a[4:1] = mmc1_load_register[4:1]; + SET_BITS(prg_bank_a, "4:1", mmc1_load_register, "4:1"); + // sram_enabled = ~mmc1_load_register[5]; + sram_enabled = get_bits(mmc1_load_register, "5") ^ 1; + break; + } + mmc1_load_register = 0b100000; // mmc1_load_register[5:0] = 6'b100000; + } + } + } + + // Mapper #9 and #10 - MMC2 and MMC4 + // flag0 - 0=MMC2, 1=MMC4 + if (mapper == 0b010001) + { + switch ((A >> 12) & 7) + { + case 2: // $A000-$AFFF + if (!(flags & 1)) { + // MMC2 + SET_BITS(prg_bank_a, "3:0", V, "3:0"); + } + else { + // MMC4 + SET_BITS(prg_bank_a, "4:1", V, "3:0"); // prg_bank_a[4:0] = { cpu_data_in[3:0], 1'b0}; + prg_bank_a &= ~1; + } + break; + case 3: // $B000-$BFFF + SET_BITS(chr_bank_a, "6:2", V, "4:0"); // chr_bank_a[6:2] = cpu_data_in[4:0]; + break; + case 4: // $C000-$CFFF + SET_BITS(chr_bank_b, "6:2", V, "4:0"); // chr_bank_b[6:2] = cpu_data_in[4:0]; + break; + case 5: // $D000-$DFFF + SET_BITS(chr_bank_e, "6:2", V, "4:0"); // chr_bank_e[6:2] = cpu_data_in[4:0]; + break; + case 6: // $E000-$EFFF + SET_BITS(chr_bank_f, "6:2", V, "4:0"); // chr_bank_f[6:2] = cpu_data_in[4:0]; + break; + case 7: // $F000-$FFFF + mirroring = V & 1; + break; + } + } + + // Mapper #152 + if (mapper == 0b010010) + { + SET_BITS(chr_bank_a, "6:3", V, "3:0"); // chr_bank_a[6:3] = cpu_data_in[3:0]; + SET_BITS(prg_bank_a, "3:1", V, "6:4"); // prg_bank_a[3:1] = cpu_data_in[6:4]; + if (flags & 1) + mirroring = 2 | get_bits(V, "7"); // mirroring = {1'b1, cpu_data_in[7]}; // Mapper #152 + else + SET_BITS(prg_bank_a, "4", V, "7"); //prg_bank_a[4] <= cpu_data_in[7]; // Mapper #70 + } + + // Mapper #73 - VRC3 + if (mapper == 0b010011) + { + switch (get_bits(A, "14:12")) // case (cpu_addr_in[14:12]) + { + case 0b000: // 3'b000: vrc3_irq_latch[3:0] = cpu_data_in[3:0]; // $8000-$8FFF + SET_BITS(vrc3_irq_latch, "3:0", V, "3:0"); + break; + case 0b001: // 3'b001: vrc3_irq_latch[7:4] = cpu_data_in[3:0]; // $9000-$9FFF + SET_BITS(vrc3_irq_latch, "7:4", V, "3:0"); + break; + case 0b010: // 3'b010: vrc3_irq_latch[11:8] = cpu_data_in[3:0]; // $A000-$AFFF + SET_BITS(vrc3_irq_latch, "11:8", V, "3:0"); + break; + case 0b011: // 3'b011: vrc3_irq_latch[15:12] = cpu_data_in[3:0]; // $B000-$BFFF + SET_BITS(vrc3_irq_latch, "15:12", V, "3:0"); + break; + case 0b100: // // $C000-$CFFF + X6502_IRQEnd(FCEU_IQEXT); // vrc3_irq_out = 0; // ack + SET_BITS(vrc3_irq_control, "2:0", V, "2:0"); // vrc3_irq_control[2:0] = cpu_data_in[2:0]; // mode, enabled, enabled after ack + if (vrc3_irq_control & 2) // if (vrc3_irq_control[1]) // if E is set + vrc3_irq_value = vrc3_irq_latch; // vrc3_irq_value[15:0] = vrc3_irq_latch[15:0]; // reload with latch + break; + case 0b101: // // $D000-$DFFF + X6502_IRQEnd(FCEU_IQEXT); // vrc3_irq_out = 0; // ack + SET_BITS(vrc3_irq_control, "1", vrc3_irq_control, "0"); // vrc3_irq_control[1] = vrc3_irq_control[0]; + break; + case 0b110: // $E000-$EFFF + break; + case 0b111: // 3'b111: prg_bank_a[3:1] = cpu_data_in[2:0]; // $F000-$FFFF + SET_BITS(prg_bank_a, "3:1", V, "2:0"); + break; + } + } + + // Mapper #4 - MMC3/MMC6 + /* + flag0 - TxSROM + flag1 - mapper #189 + */ + if (mapper == 0b010100) + { + // case ({cpu_addr_in[14:13], cpu_addr_in[0]}) + switch (get_bits(A, "14:13,0")) + { + case 0b000: // $8000-$9FFE, even + SET_BITS(mmc3_internal, "2:0", V, "2:0"); // mmc3_internal[2:0] = cpu_data_in[2:0]; + if (!(flags & 2) && !(flags & 4)) // if ((!USE_MAPPER_189 | ~flags[1]) & (!USE_MAPPER_206 | ~flags[2])) + { + if (get_bits(V, "6")) // if (cpu_data_in[6]) + prg_mode = 0b101; + else + prg_mode = 0b100; + } + if (!(flags & 4)) // if (!USE_MAPPER_206 | ~flags[2]) // disabled for mapper #206 + { + if (V & 0x80) // if (cpu_data_in[7]) + chr_mode = 0b011; + else + chr_mode = 0b010; + } + break; + case 0b001: // $8001-$9FFF, odd + switch (get_bits(mmc3_internal, "2:0")) + { + case 0b000: // 3'b000: chr_bank_a[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b001: // 3'b001: chr_bank_c[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b010: // 3'b010: chr_bank_e[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b011: // 3'b011: chr_bank_f[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b100: // 3'b100: chr_bank_g[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b101: // 3'b101: chr_bank_h[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + case 0b110: // 3'b110: if (!ENABLE_MAPPER_189 | ~flags[1]) prg_bank_a[(MMC3_BITSIZE-1):0] = cpu_data_in[(MMC3_BITSIZE-1):0]; + if (!(flags & 2)) + SET_BITS(prg_bank_a, "7:0", V, "7:0"); + break; + case 0b111: // 3'b111: if (!ENABLE_MAPPER_189 | ~flags[1]) prg_bank_b[(MMC3_BITSIZE-1):0] = cpu_data_in[(MMC3_BITSIZE-1):0]; + if (!(flags & 2)) + SET_BITS(prg_bank_b, "7:0", V, "7:0"); + break; + } + break; + case 0b010: // $A000-$BFFE, even (mirroring) + // if (!ENABLE_MAPPER_206 | ~flags[2]) // disabled for mapper #206 + if (!(flags & 4)) + mirroring = V & 1; // mirroring = {1'b0, cpu_data_in[0]}; + break; + case 0b011: // RAM protect... no + break; + case 0b100: // 3'b100: mmc3_irq_latch = cpu_data_in; // $C000-$DFFE, even (IRQ latch) + mmc3_irq_latch = V; break; + case 0b101: // 3'b101: mmc3_irq_reload = 1; // $C001-$DFFF, odd + mmc3_irq_reload = 1; break; + case 0b110: // 3'b110: mmc3_irq_enabled = 0; // $E000-$FFFE, even + X6502_IRQEnd(FCEU_IQEXT); + mmc3_irq_enabled = 0; + break; + case 0b111: // $E001-$FFFF, odd + if (!(flags & 4)) // if (!USE_MAPPER_206 | ~flags[2]) // disabled for mapper #206 + mmc3_irq_enabled = 1; // mmc3_irq_enabled = 1; + break; + } + } + + // Mapper #112 + if (mapper == 0b010101) + { + switch (get_bits(A, "14:13")) + { + case 0b00: // $8000-$9FFF + SET_BITS(mapper112_internal, "2:0", V, "2:0"); // mapper112_internal[2:0] = cpu_data_in[2:0]; + break; + case 0b01: // $A000-BFFF + switch (get_bits(mapper112_internal, "2:0")) + { + case 0b000: // 3'b000: prg_bank_a[5:0] = cpu_data_in[5:0]; + SET_BITS(prg_bank_a, "5:0", V, "5:0"); break; + case 0b001: // 3'b001: prg_bank_b[5:0] = cpu_data_in[5:0]; + SET_BITS(prg_bank_b, "5:0", V, "5:0"); break; + case 0b010: // 3'b010: chr_bank_a[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b011: // 3'b011: chr_bank_c[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b100: // 3'b100: chr_bank_e[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b101: // 3'b101: chr_bank_f[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b110: // 3'b110: chr_bank_g[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b111: // 3'b111: chr_bank_h[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + } + break; + case 0b10: // $C000-$DFFF + break; + case 0b11: // 2'b11: mirroring = {1'b0, cpu_data_in[0]}; // $E000-$FFFF + mirroring = V & 1; break; + } + } + + // Mappers #33 + #48 - Taito + // flag0=0 - #33, flag0=1 - #48 + if (mapper == 0b010110) + { + // case ({cpu_addr_in[14:13], cpu_addr_in[1:0]}) + switch (get_bits(A, "14:13,1:0")) + { + case 0b0000: + SET_BITS(prg_bank_a, "5:0", V, "5:0"); // prg_bank_a[5:0] = cpu_data_in[5:0]; // $8000, PRG Reg 0 (8k @ $8000) + if (!(flags & 1)) // if (~flags[0]) // 33 + mirroring = get_bits(V, "6"); // mirroring = {1'b0, cpu_data_in[6]}; + break; + case 0b0001: // 4'b0001: prg_bank_b[5:0] = cpu_data_in[5:0]; // $8001, PRG Reg 1 (8k @ $A000) + SET_BITS(prg_bank_b, "5:0", V, "5:0"); break; + case 0b0010: // 4'b0010: chr_bank_a[7:1] = cpu_data_in[6:0]; // $8002, CHR Reg 0 (2k @ $0000) + SET_BITS(chr_bank_a, "7:1", V, "6:0"); break; + case 0b0011: // 4'b0011: chr_bank_c[7:1] = cpu_data_in[6:0]; // $8003, CHR Reg 1 (2k @ $0800) + SET_BITS(chr_bank_c, "7:1", V, "6:0"); break; + case 0b0100: // 4'b0100: chr_bank_e[7:0] = cpu_data_in[7:0]; // $A000, CHR Reg 2 (1k @ $1000) + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b0101: // 4'b0101: chr_bank_f[7:0] = cpu_data_in[7:0]; // $A001, CHR Reg 2 (1k @ $1400) + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b0110: // 4'b0110: chr_bank_g[7:0] = cpu_data_in[7:0]; // $A002, CHR Reg 2 (1k @ $1800) + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b0111: // 4'b0111: chr_bank_h[7:0] = cpu_data_in[7:0]; // $A003, CHR Reg 2 (1k @ $1C00) + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + case 0b1100: // 4'b1100: if (flags[0]) mirroring = {1'b0, cpu_data_in[6]}; // $E000, mirroring, for mapper #48 + if (flags & 1) // 48 + mirroring = get_bits(V, "6"); // mirroring = cpu_data_in[6]; + break; + case 0b1000: // 4'b1000: irq_scanline_latch = ~cpu_data_in; // $C000, IRQ latch + mmc3_irq_latch = set_bits(mmc3_irq_latch, "7:0", get_bits(V, "7:0") ^ 0b11111111); + break; + case 0b1001: // 4'b1001: mmc3_irq_reload = 1; // $C001, IRQ reload + mmc3_irq_reload = 1; break; + case 0b1010: // 4'b1010: mmc3_irq_enabled = 1; // $C002, IRQ enable + mmc3_irq_enabled = 1; break; + case 0b1011: // 4'b1011: mmc3_irq_enabled = 0; // $C003, IRQ disable & ack + mmc3_irq_enabled = 0; + X6502_IRQEnd(FCEU_IQEXT); // irq_cpu_out = 0; // ack + break; + } + } + + // Mapper #42 + if (mapper == 0b010111) + { + // case ({cpu_addr_in[14], cpu_addr_in[1:0]}) + switch (get_bits(A, "14,1:0")) + { + case 0: // 3'b000: chr_bank_a[7:3] = cpu_data_in[4:0]; // $8000, CHR Reg (8k @ $8000) + SET_BITS(chr_bank_a, "7:3", V, "4:0"); + break; + case 4: // 3'b100: prg_bank_6000[3:0] = cpu_data_in[3:0]; // $E000, PRG Reg (8k @ $6000) + SET_BITS(prg_bank_6000, "3:0", V, "3:0"); + break; + case 5: // 3'b101: mirroring = {1'b0, cpu_data_in[3]}; // Mirroring + mirroring = get_bits(V, "3"); + break; + case 6: // 3'b110: ... // IRQ + mapper42_irq_enabled = get_bits(V, "1"); // mapper42_irq_enabled = cpu_data_in[1]; + if (!mapper42_irq_enabled) // if (!mapper42_irq_enabled) + { + X6502_IRQEnd(FCEU_IQEXT); // irq_cpu_out = 0; + mapper42_irq_value = 0; // mapper42_irq_value = 0; + } + break; + } + } + + // Mapper #23 - VRC2/4 + /* + flag0 - switches A0 and A1 lines. 0=A0,A1 like VRC2b (mapper #23), 1=A1,A0 like VRC2a(#22), VRC2c(#25) + flag1 - divides CHR bank select by two (mapper #22, VRC2a) + */ + if (mapper == 0b011000) + { + uint8 vrc_2b_hi = + (flags & 5) == 0 ? // (!flags[0] && !flags[2]) ? + (get_bits(A, "7") | get_bits(A, "2")) // | cpu_addr_in[7] | cpu_addr_in[2]) // mapper #21 + : (flags & 5) == 1 ? //: (flags[0] && !flags[2]) ? + get_bits(A, "0") // (cpu_addr_in[0]) // mapper #22 + : (flags & 5) == 4 ? // : (!flags[0] && flags[2]) ? + (get_bits(A, "5") | get_bits(A, "3") | get_bits(A, "1")) // (cpu_addr_in[5] | cpu_addr_in[3] | cpu_addr_in[1]) // mapper #23 + : (get_bits(A, "2") | get_bits(A, "0")); // : (cpu_addr_in[2] | cpu_addr_in[0]); // mapper #25 + uint8 vrc_2b_low = + (flags & 5) == 0 ? // (!flags[0] && !flags[2]) ? + (get_bits(A, "6") | get_bits(A, "1")) // (cpu_addr_in[6] | cpu_addr_in[1]) // mapper #21 + : (flags & 5) == 1 ? // : (flags[0] && !flags[2]) ? + get_bits(A, "1") // (cpu_addr_in[1]) // mapper #22 + : (flags & 5) == 4 ?// : (!flags[0] && flags[2]) ? + (get_bits(A, "4") | get_bits(A, "2") | get_bits(A, "0")) // (cpu_addr_in[4] | cpu_addr_in[2] | cpu_addr_in[0]) // mapper #23 + : (get_bits(A, "3") | get_bits(A, "1")); // : (cpu_addr_in[3] | cpu_addr_in[1]); // mapper #25 + + // case ({ cpu_addr_in[14:12], vrc_2b_hi, vrc_2b_low }) + switch ((get_bits(A, "14:12") << 2) | (vrc_2b_hi << 1) | vrc_2b_low) + { + case 0b00000: // $8000-$8003, PRG0 + case 0b00001: + case 0b00010: + case 0b00011: + SET_BITS(prg_bank_a, "4:0", V, "4:0"); // prg_bank_a[4:0] = cpu_data_in[4:0]; + break; + case 0b00100: // $9000-$9001, mirroring + case 0b00101: + // VRC2 - using games are usually well - behaved and only write 0 or 1 to this register, + // but Wai Wai World in one instance writes $FF instead + if (V != 0b11111111) // if (cpu_data_in != 8'b11111111) mirroring = cpu_data_in[1:0]; // $9000-$9001, mirroring + SET_BITS(mirroring, "1:0", V, "1:0"); + break; + case 0b00110: // $9002-$9004, PRG swap + case 0b00111: + SET_BITS(prg_mode, "0", V, "1"); // prg_mode[0] = cpu_data_in[1]; + break; + case 0b01000: // $A000-$A003, PRG1 + case 0b01001: + case 0b01010: + case 0b01011: + SET_BITS(prg_bank_b, "4:0", V, "4:0"); // prg_bank_b[4:0] = cpu_data_in[4:0]; + break; + case 0b01100: // 5'b01100: chr_bank_a[3:0] = cpu_data_in[3:0]; // $B000, CHR0 low + SET_BITS(chr_bank_a, "3:0", V, "3:0"); break; + case 0b01101: // 5'b01101: chr_bank_a[7:4] = cpu_data_in[3:0]; // $B001, CHR0 hi + SET_BITS(chr_bank_a, "7:4", V, "3:0"); break; + case 0b01110: // 5'b01110: chr_bank_b[3:0] = cpu_data_in[3:0]; // $B002, CHR1 low + SET_BITS(chr_bank_b, "3:0", V, "3:0"); break; + case 0b01111: // 5'b01111: chr_bank_b[7:4] = cpu_data_in[3:0]; // $B003, CHR1 hi + SET_BITS(chr_bank_b, "7:4", V, "3:0"); break; + case 0b10000: // 5'b10000: chr_bank_c[3:0] = cpu_data_in[3:0]; // $C000, CHR2 low + SET_BITS(chr_bank_c, "3:0", V, "3:0"); break; + case 0b10001: // 5'b10001: chr_bank_c[7:4] = cpu_data_in[3:0]; // $C001, CHR2 hi + SET_BITS(chr_bank_c, "7:4", V, "3:0"); break; + case 0b10010: // 5'b10010: chr_bank_d[3:0] = cpu_data_in[3:0]; // $C002, CHR3 low + SET_BITS(chr_bank_d, "3:0", V, "3:0"); break; + case 0b10011: // 5'b10011: chr_bank_d[7:4] = cpu_data_in[3:0]; // $C003, CHR3 hi + SET_BITS(chr_bank_d, "7:4", V, "3:0"); break; + case 0b10100: // 5'b10100: chr_bank_e[3:0] = cpu_data_in[3:0]; // $D000, CHR4 low + SET_BITS(chr_bank_e, "3:0", V, "3:0"); break; + case 0b10101: // 5'b10101: chr_bank_e[7:4] = cpu_data_in[3:0]; // $D001, CHR4 hi + SET_BITS(chr_bank_e, "7:4", V, "3:0"); break; + case 0b10110: // 5'b10110: chr_bank_f[3:0] = cpu_data_in[3:0]; // $D002, CHR5 low + SET_BITS(chr_bank_f, "3:0", V, "3:0"); break; + case 0b10111: // 5'b10111: chr_bank_f[7:4] = cpu_data_in[3:0]; // $D003, CHR5 hi + SET_BITS(chr_bank_f, "7:4", V, "3:0"); break; + case 0b11000: // 5'b11000: chr_bank_g[3:0] = cpu_data_in[3:0]; // $E000, CHR6 low + SET_BITS(chr_bank_g, "3:0", V, "3:0"); break; + case 0b11001: // 5'b11001: chr_bank_g[7:4] = cpu_data_in[3:0]; // $E001, CHR6 hi + SET_BITS(chr_bank_g, "7:4", V, "3:0"); break; + case 0b11010: // 5'b11010: chr_bank_h[3:0] = cpu_data_in[3:0]; // $E002, CHR7 low + SET_BITS(chr_bank_h, "3:0", V, "3:0"); break; + case 0b11011: // 5'b11011: chr_bank_h[7:4] = cpu_data_in[3:0]; // $E003, CHR7 hi + SET_BITS(chr_bank_h, "7:4", V, "3:0"); break; + } + + // if (cpu_addr_in[14:12] == 3'b111) + if (get_bits(A, "14:12") == 0b111) + { + // case (vrc_2b_hi, vrc_2b_low}) + switch ((vrc_2b_hi << 1) | vrc_2b_low) + { + case 0b00: // 2'b00: vrc4_irq_latch[3:0] = cpu_data_in[3:0]; // IRQ latch low + SET_BITS(vrc4_irq_latch, "3:0", V, "3:0"); break; + case 0b01: // 2'b01: vrc4_irq_latch[7:4] = cpu_data_in[3:0]; // IRQ latch hi + SET_BITS(vrc4_irq_latch, "7:4", V, "3:0"); break; + case 0b10: // 2'b10 // IRQ control + X6502_IRQEnd(FCEU_IQEXT); // vrc4_irq_out = 0; // ack + SET_BITS(vrc4_irq_control, "2:0", V, "2:0"); // vrc4_irq_control[2:0] = cpu_data_in[2:0]; // mode, enabled, enabled after ack + if (vrc4_irq_control & 2) // if (vrc4_irq_control[1]) begin // if E is set + { + vrc4_irq_prescaler_counter = 0; // vrc4_irq_prescaler_counter[1:0] = 2'b00; // reset prescaler + vrc4_irq_prescaler = 0; // vrc4_irq_prescaler[6:0] = 7'b0000000; + SET_BITS(vrc4_irq_value, "7:0", vrc4_irq_latch, "7:0"); // vrc4_irq_value[7:0] = vrc4_irq_latch[7:0]; // reload with latch + } + break; + case 0b11: // 2'b11 // IRQ ack + X6502_IRQEnd(FCEU_IQEXT); // vrc4_irq_out = 0; + SET_BITS(vrc4_irq_control, "1", vrc4_irq_control, "0"); // vrc4_irq_control[1] = vrc4_irq_control[0]; + break; + } + } + } + + // Mapper #69 - Sunsoft FME-7 + if (mapper == 0b011001) + { + // if (cpu_addr_in[14:13] == 2'b00) mapper69_internal[3:0] = cpu_data_in[3:0]; + if (get_bits(A, "14:13") == 0b00) SET_BITS(mapper69_internal, "3:0", V, "3:0"); + // if (cpu_addr_in[14:13] == 2'b01) + if (get_bits(A, "14:13") == 0b01) + { + switch (get_bits(mapper69_internal, "3:0")) // case (mapper69_internal[3:0]) + { + case 0b0000: // 4'b0000: chr_bank_a[7:0] = cpu_data_in[7:0]; // CHR0 + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b0001: // 4'b0001: chr_bank_b[7:0] = cpu_data_in[7:0]; // CHR1 + SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; + case 0b0010: // 4'b0010: chr_bank_c[7:0] = cpu_data_in[7:0]; // CHR2 + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b0011: // 4'b0011: chr_bank_d[7:0] = cpu_data_in[7:0]; // CHR3 + SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; + case 0b0100: // 4'b0100: chr_bank_e[7:0] = cpu_data_in[7:0]; // CHR4 + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b0101: // 4'b0101: chr_bank_f[7:0] = cpu_data_in[7:0]; // CHR5 + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b0110: // 4'b0110: chr_bank_g[7:0] = cpu_data_in[7:0]; // CHR6 + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b0111: // 4'b0111: chr_bank_h[7:0] = cpu_data_in[7:0]; // CHR7 + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + case 0b1000: // 4'b1000: {sram_enabled, map_rom_on_6000, prg_bank_6000} = {cpu_data_in[7], ~cpu_data_in[6], cpu_data_in[5:0]}; // PRG0 + sram_enabled = (V >> 7) & 1; + map_rom_on_6000 = ((V >> 6) & 1) ^ 1; + prg_bank_6000 = V & 0x3F; + break; + case 0b1001: // 4'b1001: prg_bank_a[5:0] = cpu_data_in[5:0]; // PRG1 + SET_BITS(prg_bank_a, "5:0", V, "5:0"); break; + case 0b1010: // 4'b1010: prg_bank_b[5:0] = cpu_data_in[5:0]; // PRG2 + SET_BITS(prg_bank_b, "5:0", V, "5:0"); break; + case 0b1011: // 4'b1011: prg_bank_c[5:0] = cpu_data_in[5:0]; // PRG3 + SET_BITS(prg_bank_c, "5:0", V, "5:0"); break; + case 0b1100: // 4'b1100: mirroring[1:0] = cpu_data_in[1:0]; // mirroring + SET_BITS(mirroring, "1:0", V, "1:0"); break; + case 0b1101: // 4'b1101 + X6502_IRQEnd(FCEU_IQEXT); // fme7_irq_out = 0; // ack + mapper69_counter_enabled = get_bits(V, "7"); // fme7_counter_enabled = cpu_data_in[7]; + mapper69_irq_enabled = get_bits(V, "0"); // fme7_irq_enabled = cpu_data_in[0]; + break; + case 0b1110: // 4'b1110: fme7_irq_value[7:0] = cpu_data_in[7:0]; // IRQ low + SET_BITS(mapper69_irq_value, "7:0", V, "7:0"); break; + case 0b1111: // fme7_irq_value[15:8] = cpu_data_in[7:0]; // IRQ high + SET_BITS(mapper69_irq_value, "15:8", V, "7:0"); break; + } + } + } + + // Mapper #32 - Irem's G-101 + if (mapper == 0b011010) + { + switch (get_bits(A, "14:12")) + { + case 0b000: // 2'b00: prg_bank_a[5:0] = cpu_data_in[5:0]; // $8000-$8FFF, PRG0 + SET_BITS(prg_bank_a, "5:0", V, "5:0"); + break; + case 0b001: // 2'b01: {prg_mode[0], mirroring} = {cpu_data_in[1], 1'b0, cpu_data_in[0]}; // $9000-$9FFF, PRG mode, mirroring + SET_BITS(prg_mode, "0", V, "1"); + mirroring = V & 1; + break; + case 0b010: // 2'b10: prg_bank_b[5:0] = cpu_data_in[5:0]; // $A000-$AFFF, PRG1 + SET_BITS(prg_bank_b, "5:0", V, "5:0"); + break; + case 0b011: // $B000-$BFFF, CHR regs + switch (get_bits(A, "2:0")) + { + case 0b000: // 3'b000: chr_bank_a[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; + case 0b001: // 3'b001: chr_bank_b[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; + case 0b010: // 3'b010: chr_bank_c[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; + case 0b011: // 3'b011: chr_bank_d[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; + case 0b100: // 3'b100: chr_bank_e[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; + case 0b101: // 3'b101: chr_bank_f[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; + case 0b110: // 3'b110: chr_bank_g[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; + case 0b111: // 3'b111: chr_bank_h[7:0] = cpu_data_in[7:0]; + SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; + } + break; + } + } + + // Mapper #36 is assigned to TXC's PCB 01-22000-400 + if (mapper == 0b011101) + { + if (get_bits(A, "14:1") != 0b11111111111111) // (cpu_addr_in[14:1] != 14'b11111111111111) + { + // prg_bank_a[5:2] = cpu_data_in[7:4]; + SET_BITS(prg_bank_a, "5:2", V, "7:4"); + // chr_bank_a[6:3] = cpu_data_in[3:0]; + SET_BITS(chr_bank_a, "6:3", V, "3:0"); + } + } + + // Mapper #70 + if (mapper == 0b011110) + { + // prg_bank_a[4:1] = cpu_data_in[7:4]; + SET_BITS(prg_bank_a, "4:1", V, "7:4"); + // chr_bank_a[6:3] = cpu_data_in[3:0]; + SET_BITS(chr_bank_a, "6:3", V, "3:0"); + } + + // Mapper #75 - VRC1 + if (mapper == 0b100010) + { + // case (cpu_addr_in[14:12]) + switch (get_bits(A, "14:12")) + { + case 0b000: // 3'b000: prg_bank_a[3:0] = cpu_data_in[3:0]; // $8000-$8FFF + SET_BITS(prg_bank_a, "3:0", V, "3:0"); break; + case 0b001: + mirroring = V & 1; // mirroring = {1'b0, cpu_data_in[0]}; + SET_BITS(chr_bank_a, "6", V, "1"); // chr_bank_a[6] = cpu_data_in[1]; + SET_BITS(chr_bank_e, "6", V, "2"); // chr_bank_e[6] = cpu_data_in[2]; + break; + case 0b010: // 3'b010: prg_bank_b[3:0] = cpu_data_in[3:0]; // $A000-$AFFF + SET_BITS(prg_bank_b, "3:0", V, "3:0"); break; + case 0b100: // 3'b100: prg_bank_c[3:0] = cpu_data_in[3:0]; // $C000-$CFFF + SET_BITS(prg_bank_c, "3:0", V, "3:0"); break; + case 0b110: // 3'b110: �hr_bank_a[5:2] = cpu_data_in[3:0]; // $E000-$EFFF + SET_BITS(chr_bank_a, "5:2", V, "3:0"); break; + case 0b111: // 3'b111: chr_bank_e[5:2] = cpu_data_in[3:0]; // $F000-$FFFF + SET_BITS(chr_bank_e, "5:2", V, "3:0"); break; + } + } + + // Mapper #83 - Cony/Yoko + if (mapper == 0b100011) + { + // case (cpu_addr_in[9:8]) + switch (get_bits(A, "9:8")) + { + case 0b00: // $80xx + SET_BITS(prg_bank_a, "4:1", V, "3:0"); + break; + case 0b01: // $81xx + mirroring = get_bits(V, "1:0"); // mirroring <= cpu_data_in[1:0]; + SET_BITS(prg_mode, "2", V, "4"); // prg_mode[2] <= cpu_data_in[4]; + map_rom_on_6000 = get_bits(V, "5"); // map_rom_on_6000 <= cpu_data_in[5]; + mapper83_irq_enabled_latch = get_bits(V, "7"); // mapper83_irq_enabled_latch <= cpu_data_in[7]; + break; + case 0b10: // 82xx + if (!get_bits(A, "0")) // if (!cpu_addr_in[0]) + { + X6502_IRQEnd(FCEU_IQEXT); // mapper83_irq_out <= 0; + SET_BITS(mapper83_irq_counter, "7:0", V, "7:0"); // mapper83_irq_counter[7:0] <= cpu_data_in[7:0]; + } + else { + mapper83_irq_enabled = mapper83_irq_enabled_latch; //mapper83_irq_enabled <= mapper83_irq_enabled_latch; + SET_BITS(mapper83_irq_counter, "15:8", V, "7:0"); // mapper83_irq_counter[15:8] <= cpu_data_in[7:0]; + } + break; + case 0b11: + if (!get_bits(A, "4")) // if (!cpu_addr_in[4]) + { + switch (get_bits(A, "1:0")) // case (cpu_addr_in[1:0]) + { + case 0b00: SET_BITS(prg_bank_a, "7:0", V, "7:0"); break; // 2'b00: prg_bank_a[7:0] <= cpu_data_in[7:0]; + case 0b01: SET_BITS(prg_bank_b, "7:0", V, "7:0"); break; // 2'b01: prg_bank_b[7:0] <= cpu_data_in[7:0]; + case 0b10: SET_BITS(prg_bank_b, "7:0", V, "7:0"); break; // 2'b10: prg_bank_c[7:0] <= cpu_data_in[7:0]; + case 0b11: SET_BITS(prg_bank_6000, "7:0", V, "7:0"); break; //2'b11: prg_bank_6000[7:0] <= cpu_data_in[7:0]; + } + } + else { + if (!(flags & 0b100)) + { + switch (get_bits(A, "2:0")) // case (cpu_addr_in[2:0]) + { + case 0b000: SET_BITS(chr_bank_a, "7:0", V, "7:0"); break; // 3'b000: chr_bank_a[7:0] <= cpu_data_in[7:0]; + case 0b001: SET_BITS(chr_bank_b, "7:0", V, "7:0"); break; // 3'b001: chr_bank_b[7:0] <= cpu_data_in[7:0]; + case 0b010: SET_BITS(chr_bank_c, "7:0", V, "7:0"); break; // 3'b010: chr_bank_c[7:0] <= cpu_data_in[7:0]; + case 0b011: SET_BITS(chr_bank_d, "7:0", V, "7:0"); break; // 3'b011: chr_bank_d[7:0] <= cpu_data_in[7:0]; + case 0b100: SET_BITS(chr_bank_e, "7:0", V, "7:0"); break; // 3'b100: chr_bank_e[7:0] <= cpu_data_in[7:0]; + case 0b101: SET_BITS(chr_bank_f, "7:0", V, "7:0"); break; // 3'b101: chr_bank_f[7:0] <= cpu_data_in[7:0]; + case 0b110: SET_BITS(chr_bank_g, "7:0", V, "7:0"); break; // 3'b110: chr_bank_g[7:0] <= cpu_data_in[7:0]; + case 0b111: SET_BITS(chr_bank_h, "7:0", V, "7:0"); break; // 3'b111: chr_bank_h[7:0] <= cpu_data_in[7:0]; + } + } + else { + switch (get_bits(A, "2:0")) // case (cpu_addr_in[2:0]) + { + case 0b000: + SET_BITS(chr_bank_a, "8:1", V, "7:0"); break; // 3'b000: chr_bank_a[8:1] <= cpu_data_in[7:0]; + case 0b001: + SET_BITS(chr_bank_c, "8:1", V, "7:0"); break; // 3'b001: chr_bank_c[8:1] <= cpu_data_in[7:0]; + case 0b110: + SET_BITS(chr_bank_e, "8:1", V, "7:0"); break; // 3'b110: chr_bank_e[8:1] <= cpu_data_in[7:0]; + case 0b111: + SET_BITS(chr_bank_g, "8:1", V, "7:0"); break; // 3'b111: chr_bank_g[8:1] <= cpu_data_in[7:0]; + } + } + } + break; + } + } + + // Mapper #67 - Sunsoft-3 + if (mapper == 0b100100) + { + if (get_bits(A, "11")) // if (cpu_addr_in[11]) + { + switch (get_bits(A, "14:12")) // case (cpu_addr_in[14:12]) + { + case 0b000: // 3'b000: chr_bank_a[6:1] <= cpu_data_in[5:0]; // $8800 + SET_BITS(chr_bank_a, "6:1", V, "5:0"); break; + case 0b001: // 3'b001: chr_bank_c[6:1] <= cpu_data_in[5:0]; // $9800 + SET_BITS(chr_bank_c, "6:1", V, "5:0"); break; + case 0b010: // 3'b010: chr_bank_e[6:1] <= cpu_data_in[5:0]; // $A800 + SET_BITS(chr_bank_e, "6:1", V, "5:0"); break; + case 0b011: // 3'b011: chr_bank_g[6:1] <= cpu_data_in[5:0]; // $B800 + SET_BITS(chr_bank_g, "6:1", V, "5:0"); break; + case 0b100: // 3'b100: begin // $C800, IRQ load + mapper67_irq_latch = ~mapper67_irq_latch; + if (mapper67_irq_latch) + SET_BITS(mapper67_irq_counter, "15:8", V, "7:0"); // mapper67_irq_counter[15:8] <= cpu_data_in[7:0]; + else + SET_BITS(mapper67_irq_counter, "7:0", V, "7:0"); // mapper67_irq_counter[7:0] <= cpu_data_in[7:0]; + break; + case 0b101: // 3'b101: begin // $D800, IRQ enable + mapper67_irq_latch = 0; // mapper67_irq_latch <= 0; + SET_BITS(mapper67_irq_enabled, "0", V, "4"); // mapper67_irq_enabled <= cpu_data_in[4]; + break; + case 0b110: // 3'b110: mirroring[1:0] <= cpu_data_in[1:0]; // $E800 + SET_BITS(mirroring, "1:0", V, "1:0"); + break; + case 0b111: // 3'b111: prg_bank_a[4:1] <= cpu_data_in[3:0]; // $F800 + SET_BITS(prg_bank_a, "4:1", V, "3:0"); + break; + } + } + else { + // Interrupt Acknowledge ($8000) + X6502_IRQEnd(FCEU_IQEXT); // mapper67_irq_out <= 0; + } + } + + // Mapper #89 - Sunsoft-2 chip on the Sunsoft-3 board + if (mapper == 0b100101) + { + // prg_bank_a[3:1] <= cpu_data_in[6:4]; + SET_BITS(prg_bank_a, "3:1", V, "6:4"); + // chr_bank_a[6:3] <= {cpu_data_in[7], cpu_data_in[2:0]}; + SET_BITS(chr_bank_a, "6:3", V, "7,2:0"); + // mirroring[1:0] <= {1'b1, cpu_data_in[3]}; + SET_BITS(mirroring, "1", 1, "0"); + SET_BITS(mirroring, "0", V, "3"); + } + } + + COOLGIRL_Sync(); +} + +static DECLFR(MAFRAM) { + if ((mapper == 0b000000) && (A >= 0x5000) && (A < 0x6000)) + return 0; + + // Mapper #163 + // (USE_MAPPER_163 && (mapper == 5'b00110) && m2 && romsel && cpu_rw_in && ((cpu_addr_in[14:8] & 7'h77) == 7'h51)) ? + if ((mapper == 0b000110) && ((A & 0x7700) == 0x5100)) + return mapper163_r2 | mapper163_r0 | mapper163_r1 | ~mapper163_r3; // {1'b1, r2 | r0 | r1 | ~r3} + // (USE_MAPPER_163 && (mapper == 5'b00110) && m2 && romsel && cpu_rw_in && ((cpu_addr_in[14:8] & 7'h77) == 7'h55)) ? + if ((mapper == 0b000110) && ((A & 0x7700) == 0x5500)) + return (mapper163_r5 & 1) ? mapper163_r2 : mapper163_r1; // {1'b1, r5[0] ? r2 : r1} + + // MMC5 + if ((mapper == 0b001111) && (A == 0x5204)) + { + int ppuon = (PPU[1] & 0x18); + uint8 r = (!ppuon || scanline + 1 >= 241) ? 0 : 1; + uint8 p = mmc5_irq_out; + X6502_IRQEnd(FCEU_IQEXT); + mmc5_irq_out = 0; + return (p << 7) | (r << 6); + } + + // Mapper #36 is assigned to TXC's PCB 01-22000-400 + if ((mapper == 0b011101) && ((A & 0xE100) == 0x4100)) // (USE_MAPPER_036 && mapper == 5'b11101 && {cpu_addr_in[14:13], cpu_addr_in[8]} == 3'b101) ? + { + return (prg_bank_a & 0x0C) << 2; // {1'b1, 2'b00, prg_bank_a[3:2], 4'b00} + } + + // Mapper #83 - Cony/Yoko + if ((mapper == 0b100011) && ((A & 0x7000) == 0x5000)) return flags & 3; + + // Mapper #90 - JY + if ((mapper == 0b001101) && (A == 0x5800)) return (mul1 * mul2) & 0xFF; + if ((mapper == 0b001101) && (A == 0x5801)) return ((mul1 * mul2) >> 8) & 0xFF; + + if (sram_enabled && !map_rom_on_6000 && (A >= 0x6000) && (A < 0x8000)) + return CartBR(A); // SRAM + if (map_rom_on_6000 && (A >= 0x6000) && (A < 0x8000)) + return CartBR(A); // PRG + + return X.DB; // Open bus +} + +static void COOLGIRL_ScanlineCounter(void) { + // for MMC3 and MMC3-based + if (mmc3_irq_reload || !mmc3_irq_counter) + { + mmc3_irq_counter = mmc3_irq_latch; + mmc3_irq_reload = 0; + } + else + mmc3_irq_counter--; + if (!mmc3_irq_counter && mmc3_irq_enabled) + X6502_IRQBegin(FCEU_IQEXT); + + // for MMC5 + if (mmc5_irq_line == scanline + 1) + { + if (mmc5_irq_enabled) + { + X6502_IRQBegin(FCEU_IQEXT); + mmc5_irq_out = 1; + } + } + + // for mapper #163 + if (scanline == 239) + { + mapper_163_latch = 0; + COOLGIRL_Sync_CHR(); + } + else if (scanline == 127) + { + mapper_163_latch = 1; + COOLGIRL_Sync_CHR(); + } +} + +static void COOLGIRL_CpuCounter(int a) { + while (a--) + { + // Mapper #23 - VRC4 + if (vrc4_irq_control & 2) // if (ENABLE_MAPPER_021_022_023_025 & ENABLE_VRC4_INTERRUPTS & (vrc4_irq_control[1])) + { + vrc4_irq_prescaler++; // vrc4_irq_prescaler = vrc4_irq_prescaler + 1'b1; // count prescaler + // if ((vrc4_irq_prescaler_counter[1] == 0 && vrc4_irq_prescaler == 114) + // || (vrc4_irq_prescaler_counter[1] == 1 && vrc4_irq_prescaler == 113)) // 114, 114, 113 + if ((!(vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 114) || ((vrc4_irq_prescaler_counter & 2) && vrc4_irq_prescaler == 113)) + { + vrc4_irq_prescaler = 0; // vrc4_irq_prescaler = 0; + vrc4_irq_prescaler_counter++; // vrc4_irq_prescaler_counter = vrc4_irq_prescaler_counter + 1'b1; + if (vrc4_irq_prescaler_counter == 0b11) vrc4_irq_prescaler_counter = 0; // if (vrc4_irq_prescaler_counter == 2'b11) vrc4_irq_prescaler_counter = 2'b00; + vrc4_irq_value++; // {carry, vrc4_irq_value[7:0]} = vrc4_irq_value[7:0] + 1'b1; + if (vrc4_irq_value == 0) // f (carry) + { + X6502_IRQBegin(FCEU_IQEXT); + vrc4_irq_value = vrc4_irq_latch; // irq_cpu_value[7:0] = vrc4_irq_latch[7:0]; + } + } + } + + // Mapper #73 - VRC3 + if (vrc3_irq_control & 2) + { + if (vrc3_irq_control & 4) // if (vrc3_irq_control[2]) + { // 8-bit mode + //vrc3_irq_value = set_bits(vrc3_irq_value, "7:0", get_bits(vrc3_irq_value, "7:0") + 1); // vrc3_irq_value[7:0] = vrc3_irq_value[7:0] + 1'b1; + vrc3_irq_value = (vrc3_irq_value & 0xFF00) | ((vrc3_irq_value + 1) & 0xFF); + //if (get_bits(vrc3_irq_value, "7:0") == 0) // if (vrc3_irq_value[7:0] == 0) + if ((vrc3_irq_value & 0xFF) == 0) + { + X6502_IRQBegin(FCEU_IQEXT); // vrc3_irq_out = 1; + //SET_BITS(vrc3_irq_value, "7:0", vrc3_irq_latch, "7:0"); // vrc3_irq_value[7:0] = vrc3_irq_latch[7:0]; + vrc3_irq_value = (vrc3_irq_value & 0xFF00) | (vrc3_irq_latch & 0xFF); + } + } + else { // 16-bit + //vrc3_irq_value = set_bits(vrc3_irq_value, "15:0", get_bits(vrc3_irq_value, "15:0") + 1); // vrc3_irq_value[15:0] = vrc3_irq_value[15:0] + 1'b1; + vrc3_irq_value += 1; + if (vrc3_irq_value == 0) // if (vrc3_irq_value[15:0] == 0) + { + X6502_IRQBegin(FCEU_IQEXT); // vrc3_irq_out = 1; + //SET_BITS(vrc3_irq_value, "15:0", vrc3_irq_latch, "15:0"); // vrc3_irq_value[15:0] = vrc3_irq_latch[15:0]; + vrc3_irq_value = vrc3_irq_latch; + } + } + } + + // Mapper #69 - Sunsoft FME-7 + // if (ENABLE_MAPPER_069 & fme7_counter_enabled) + if (mapper69_counter_enabled) + { + // {carry, fme7_irq_value[15:0]} = {0'b0, fme7_irq_value[15:0]} - 1'b1; + mapper69_irq_value--; + // if (fme7_irq_enabled && carry) fme7_irq_out = 1; + if (mapper69_irq_value == 0xFFFF) X6502_IRQBegin(FCEU_IQEXT); + } + + // Mapper #18 + if (mapper18_irq_control & 1) // if (mapper18_irq_control[0]) + { + uint8 carry; + //carry = get_bits(mapper18_irq_value, "3:0") - 1; + carry = (mapper18_irq_value & 0x0F) - 1; + //SET_BITS(mapper18_irq_value, "3:0", carry, "3:0"); + mapper18_irq_value = (mapper18_irq_value & 0xFFF0) | (carry & 0x0F); + carry = (carry >> 4) & 1; + // if (mapper18_irq_control[3] == 1'b0) + if (!(mapper18_irq_control & 0b1000)) + { + //carry = get_bits(mapper18_irq_value, "7:4") - carry; + carry = ((mapper18_irq_value >> 4) & 0x0F) - carry; + //SET_BITS(mapper18_irq_value, "7:4", carry, "3:0"); + mapper18_irq_value = (mapper18_irq_value & 0xFF0F) | ((carry & 0x0F) << 4); + carry = (carry >> 4) & 1; + } + // if (mapper18_irq_control[3:2] == 2'b00) + if (!(mapper18_irq_control & 0b1100)) + { + //carry = get_bits(mapper18_irq_value, "11:8") - carry; + carry = ((mapper18_irq_value >> 8) & 0x0F) - carry; + //SET_BITS(mapper18_irq_value, "11:8", carry, "3:0"); + mapper18_irq_value = (mapper18_irq_value & 0xF0FF) | ((carry & 0x0F) << 8); + carry = (carry >> 4) & 1; + } + // if (mapper18_irq_control[3:1] == 3'b000) + if (!(mapper18_irq_control & 0b1110)) + { + //carry = get_bits(mapper18_irq_value, "15:12") - carry; + carry = ((mapper18_irq_value >> 12) & 0x0F) - carry; + //SET_BITS(mapper18_irq_value, "15:12", carry, "3:0"); + mapper18_irq_value = (mapper18_irq_value & 0x0FFF) | ((carry & 0x0F) << 12); + carry = (carry >> 4) & 1; + } + if (carry) + X6502_IRQBegin(FCEU_IQEXT); + } + + // Mapper #65 - Irem's H3001 + // if (mapper65_irq_enabled) + if (mapper65_irq_enabled) + { + if (mapper65_irq_value != 0) // if (mapper65_irq_value[15:0] != 0) + { + mapper65_irq_value--; // mapper65_irq_value[15:0] = mapper65_irq_value[15:0] - 1; + if (!mapper65_irq_value) + X6502_IRQBegin(FCEU_IQEXT); // if (mapper65_irq_value[15:0] == 0) irq_cpu_out = 1; + } + } + + // Mapper #42 + if (mapper42_irq_enabled) + { + //mapper42_irq_value = set_bits(mapper42_irq_value, "14:0", get_bits(mapper42_irq_value, "14:0") + 1); + //if (get_bits(mapper42_irq_value, "14:13") == 0b11) + mapper42_irq_value++; + if (mapper42_irq_value >> 15) mapper42_irq_value = 0; + if (((mapper42_irq_value >> 13) & 0b11) == 0b11) + X6502_IRQBegin(FCEU_IQEXT); + else + X6502_IRQEnd(FCEU_IQEXT); + } + + // Mapper #83 - Cony/Yoko + if (mapper83_irq_enabled) + { + if (mapper83_irq_counter == 0) // if (mapper83_irq_counter == 0) + X6502_IRQBegin(FCEU_IQEXT); // mapper83_irq_out <= 1; + mapper83_irq_counter--; // mapper83_irq_counter = mapper83_irq_counter - 1'b1; + } + + // Mapper #67 - Sunsoft-3 + if (mapper67_irq_enabled) + { + mapper67_irq_counter--; // mapper67_irq_counter = mapper67_irq_counter - 1'b1; + if (mapper67_irq_counter == 0xFFFF) // if (mapper67_irq_counter == 16'hFFFF) + { + X6502_IRQBegin(FCEU_IQEXT); // mapper67_irq_out <= 1; // fire IRQ + mapper67_irq_enabled = 0; // mapper67_irq_enabled <= 0; // disable IRQ + } + } + } +} + +static void COOLGIRL_PPUHook(uint32 A) { + // For TxROM + if ((mapper == 0b010100) && (flags & 1)) + { + setmirror(MI_0 + (TKSMIR[(A & 0x1FFF) >> 10] >> 7)); + } + + // Mapper #9 and #10 - MMC2 and MMC4 + if (mapper == 0b010001) + { + if ((A >> 4) == 0xFD) // if (ppu_addr_in[13:3] == 11'b00111111011) ppu_latch0 = 0; + { + ppu_latch0 = 0; + COOLGIRL_Sync_CHR(); + } + if ((A >> 4) == 0xFE) // if (ppu_addr_in[13:3] == 11'b00111111101) ppu_latch0 = 1; + { + ppu_latch0 = 1; + COOLGIRL_Sync_CHR(); + } + if ((A >> 4) == 0x1FD) // if (ppu_addr_in[13:3] == 11'b01111111011) ppu_latch1 = 0; + { + ppu_latch1 = 0; + COOLGIRL_Sync_CHR(); + } + if ((A >> 4) == 0x1FE) // if (ppu_addr_in[13:3] == 11'b01111111101) ppu_latch1 = 1; + { + ppu_latch1 = 1; + COOLGIRL_Sync_CHR(); + } + } +} + +static void COOLGIRL_Reset(void) { + sram_enabled = 0; + sram_page = 0; + can_write_chr = 0; + map_rom_on_6000 = 0; + flags = 0; + mapper = 0; + can_write_flash = 0; + mirroring = 0; + four_screen = 0; + lockout = 0; + prg_base = 0; + prg_mask = 0b11111000 << 14; + prg_mode = 0; + prg_bank_6000 = 0; + prg_bank_a = 0; + prg_bank_b = 1; + prg_bank_c = ~1; + prg_bank_d = ~0; + chr_mask = 0; + chr_mode = 0; + chr_bank_a = 0; + chr_bank_b = 1; + chr_bank_c = 2; + chr_bank_d = 3; + chr_bank_e = 4; + chr_bank_f = 5; + chr_bank_g = 6; + chr_bank_h = 7; + ppu_latch0 = 0; + ppu_latch1 = 0; + lreset = 0; + mmc1_load_register = 0; + mmc3_internal = 0; + mapper69_internal = 0; + mapper112_internal = 0; + mapper_163_latch = 0; + mapper163_r0 = 0; + mapper163_r1 = 0; + mapper163_r2 = 0; + mapper163_r3 = 0; + mapper163_r4 = 0; + mapper163_r5 = 0; + mul1 = 0; + mul2 = 0; + mmc3_irq_enabled = 0; + mmc3_irq_latch = 0; + mmc3_irq_counter = 0; + mmc3_irq_reload = 0; + mmc5_irq_enabled = 0; + mmc5_irq_line = 0; + mmc5_irq_out = 0; + mapper18_irq_value = 0; + mapper18_irq_control = 0; + mapper18_irq_latch = 0; + mapper65_irq_enabled = 0; + mapper65_irq_value = 0; + mapper65_irq_latch = 0; + mapper69_irq_enabled = 0; + mapper69_counter_enabled = 0; + mapper69_irq_value = 0; + vrc4_irq_value = 0; + vrc4_irq_control = 0; + vrc4_irq_latch = 0; + vrc4_irq_prescaler = 0; + vrc4_irq_prescaler_counter = 0; + vrc3_irq_value = 0; + vrc3_irq_control = 0; + vrc3_irq_latch = 0; + mapper42_irq_enabled = 0; + mapper42_irq_value = 0; + mapper90_xor = 0; + flash_state = 0; + cfi_mode = 0; + COOLGIRL_Sync(); +} + +static void COOLGIRL_Power(void) { + FCEU_CheatAddRAM(32, 0x6000, WRAM); + SetReadHandler(0x4020, 0x7FFF, MAFRAM); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x4020, 0xFFFF, COOLGIRL_WRITE); + GameHBIRQHook = COOLGIRL_ScanlineCounter; + MapIRQHook = COOLGIRL_CpuCounter; + PPU_hook = COOLGIRL_PPUHook; + COOLGIRL_Reset(); +} + +static void COOLGIRL_Close(void) { + if (WRAM) + FCEU_gfree(WRAM); + if (SAVE_FLASH) + FCEU_gfree(SAVE_FLASH); + if (CFI) + FCEU_gfree(CFI); + WRAM = SAVE_FLASH = CFI = NULL; +} + +static void COOLGIRL_Restore(int version) { + COOLGIRL_Sync(); + lreset = 0; +} + +#define ExState(var, varname) AddExState(&var, sizeof(var), 0, varname) + +void COOLGIRL_Init(CartInfo* info) { + CHR_SIZE = info->vram_size ? info->vram_size /* NES 2.0 */ : 256 * 1024 /* UNIF, fixed */; + + WRAM_SIZE = info->ines2 ? (info->wram_size + info->battery_wram_size) : (32 * 1024); + if (WRAM_SIZE > 0) { + WRAM = (uint8*)FCEU_gmalloc(WRAM_SIZE); + memset(WRAM, 0, WRAM_SIZE); + SetupCartPRGMapping(WRAM_CHIP, WRAM, WRAM_SIZE, 1); + AddExState(WRAM, 32 * 1024, 0, "SRAM"); + if (info->battery) + { + info->addSaveGameBuf(WRAM, 32 * 1024); + } + } + + if (info->battery) + { + SAVE_FLASH = (uint8*)FCEU_gmalloc(SAVE_FLASH_SIZE); + SetupCartPRGMapping(FLASH_CHIP, SAVE_FLASH, SAVE_FLASH_SIZE, 1); + AddExState(SAVE_FLASH, SAVE_FLASH_SIZE, 0, "SAVF"); + info->addSaveGameBuf(SAVE_FLASH, SAVE_FLASH_SIZE); + } + + CFI = (uint8*)FCEU_gmalloc(sizeof(cfi_data) * 2); + for (size_t i = 0; i < sizeof(cfi_data); i++) + { + CFI[i * 2] = CFI[i * 2 + 1] = cfi_data[i]; + } + SetupCartPRGMapping(CFI_CHIP, CFI, sizeof(cfi_data) * 2, 0); + + ExState(sram_enabled, "SREN"); + ExState(sram_page, "SRPG"); + ExState(can_write_chr, "SRWR"); + ExState(map_rom_on_6000, "MR6K"); + ExState(flags, "FLGS"); + ExState(mapper, "MPPR"); + ExState(can_write_flash, "FLWR"); + ExState(mirroring, "MIRR"); + ExState(four_screen, "4SCR"); + ExState(lockout, "LOCK"); + ExState(prg_base, "PBAS"); + ExState(prg_mask, "PMSK"); + ExState(prg_mode, "PMOD"); + ExState(prg_bank_6000, "P6BN"); + ExState(prg_bank_a, "PABN"); + ExState(prg_bank_b, "PBBN"); + ExState(prg_bank_c, "PCBN"); + ExState(prg_bank_d, "PDBN"); + ExState(chr_mask, "CMSK"); + ExState(chr_mode, "CMOD"); + ExState(chr_bank_a, "CABN"); + ExState(chr_bank_b, "CBBN"); + ExState(chr_bank_c, "CCBN"); + ExState(chr_bank_d, "CDBN"); + ExState(chr_bank_e, "CEBN"); + ExState(chr_bank_f, "CFBN"); + ExState(chr_bank_g, "CGBN"); + ExState(chr_bank_h, "CHBN"); + ExState(ppu_latch0, "PPU0"); + ExState(ppu_latch1, "PPU1"); + ExState(lreset, "LRST"); + ExState(mmc1_load_register, "M01R"); + ExState(mmc3_internal, "M01I"); + ExState(mapper69_internal, "M69I"); + ExState(mapper112_internal, "112I"); + ExState(mapper_163_latch, "163L"); + ExState(mapper163_r0, "1630"); + ExState(mapper163_r1, "1631"); + ExState(mapper163_r2, "1632"); + ExState(mapper163_r3, "1633"); + ExState(mapper163_r4, "1634"); + ExState(mapper163_r5, "1635"); + ExState(mul1, "MUL1"); + ExState(mul2, "MUL2"); + ExState(mmc3_irq_enabled, "M4IE"); + ExState(mmc3_irq_latch, "M4IL"); + ExState(mmc3_irq_counter, "M4IC"); + ExState(mmc3_irq_reload, "M4IR"); + ExState(mmc5_irq_enabled, "M5IE"); + ExState(mmc5_irq_line, "M5IL"); + ExState(mmc5_irq_out, "M5IO"); + ExState(mapper18_irq_value, "18IV"); + ExState(mapper18_irq_control, "18IC"); + ExState(mapper18_irq_latch, "18IL"); + ExState(mapper65_irq_enabled, "65IE"); + ExState(mapper65_irq_value, "65IV"); + ExState(mapper65_irq_latch, "65IL"); + ExState(mapper69_irq_enabled, "69IE"); + ExState(mapper69_counter_enabled, "69CE"); + ExState(mapper69_irq_value, "69IV"); + ExState(vrc4_irq_value, "V4IV"); + ExState(vrc4_irq_control, "V4IC"); + ExState(vrc4_irq_latch, "V4IL"); + ExState(vrc4_irq_prescaler, "V4PP"); + ExState(vrc4_irq_prescaler_counter, "V4PC"); + ExState(vrc3_irq_value, "V3IV"); + ExState(vrc3_irq_control, "V3IC"); + ExState(vrc3_irq_latch, "V3IL"); + ExState(mapper42_irq_enabled, "42IE"); + ExState(mapper42_irq_value, "42IV"); + ExState(mapper83_irq_enabled_latch, "M83L"); + ExState(mapper83_irq_enabled, "M83I"); + ExState(mapper83_irq_counter, "M83C"); + ExState(mapper90_xor, "90XR"); + ExState(mapper67_irq_enabled, "67IE"); + ExState(mapper67_irq_latch, "67IL"); + ExState(mapper67_irq_counter, "67IC"); + ExState(flash_state, "FLST"); + ExState(flash_buffer_a, "FLBA"); + ExState(flash_buffer_v, "FLBV"); + ExState(cfi_mode, "CFIM"); + + info->Power = COOLGIRL_Power; + info->Reset = COOLGIRL_Reset; + info->Close = COOLGIRL_Close; + GameStateRestore = COOLGIRL_Restore; +} diff --git a/source/fceultra/boards/datalatch.cpp b/source/fceultra/boards/datalatch.cpp index 026354d..de35ffc 100644 --- a/source/fceultra/boards/datalatch.cpp +++ b/source/fceultra/boards/datalatch.cpp @@ -21,11 +21,12 @@ #include "mapinc.h" #include "../ines.h" -static uint8 latche, latcheinit, bus_conflict; -static uint16 addrreg0, addrreg1; +static uint8 latche=0, latcheinit=0, bus_conflict=0; +static uint16 addrreg0=0, addrreg1=0; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; -static void (*WSync)(void); +static uint32 WRAMSIZE=0; +static void (*WSync)(void) = nullptr; +static uint8 submapper; static DECLFW(LatchWrite) { // FCEU_printf("bs %04x %02x\n",A,V); @@ -68,6 +69,7 @@ static void Latch_Init(CartInfo *info, void (*proc)(void), uint8 init, uint16 ad info->Power = LatchPower; info->Close = LatchClose; GameStateRestore = StateRestore; + submapper = info->submapper; if(info->ines2) if(info->battery_wram_size + info->wram_size > 0) wram = 1; @@ -85,14 +87,12 @@ static void Latch_Init(CartInfo *info, void (*proc)(void), uint8 init, uint16 ad //else if(!info->wram_size && info->battery_wram_size) //{ // SetupCartPRGMapping(0x10, WRAM, info->battery_wram_size, 1); - // info->SaveGame[0] = WRAM; - // info->SaveGameLen[0] = info->battery_wram_size; + // info->addSaveGameBuf( WRAM, info->battery_wram_size ); //} else { // //well, this is annoying // SetupCartPRGMapping(0x10, WRAM, info->wram_size, 1); // SetupCartPRGMapping(0x11, WRAM, info->battery_wram_size, 1); //? ? ? there probably isnt even a way to select this - // info->SaveGame[0] = WRAM + info->wram_size; - // info->SaveGameLen[0] = info->battery_wram_size; + // info->addSaveGameBuf( WRAM + info->wram_size, info->battery_wram_size ); //} //this is more likely the only practical scenario @@ -104,8 +104,7 @@ static void Latch_Init(CartInfo *info, void (*proc)(void), uint8 init, uint16 ad setprg8r(0x10, 0x6000, 0); if(info->battery_wram_size) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = 8192; + info->addSaveGameBuf( WRAM, 8192 ); } } else @@ -114,8 +113,7 @@ static void Latch_Init(CartInfo *info, void (*proc)(void), uint8 init, uint16 ad WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(WRAM, WRAMSIZE, 0, "WRAM"); } @@ -158,8 +156,7 @@ void NROM_Init(CartInfo *info) { WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(WRAM, WRAMSIZE, 0, "WRAM"); } @@ -300,7 +297,11 @@ static void M78Sync() { setprg16(0x8000, (latche & 7)); setprg16(0xc000, ~0); setchr8(latche >> 4); - setmirror(MI_0 + ((latche >> 3) & 1)); + if (submapper == 3) { + setmirror((latche >> 3) & 1); + } else { + setmirror(MI_0 + ((latche >> 3) & 1)); + } } void Mapper78_Init(CartInfo *info) { @@ -308,7 +309,8 @@ void Mapper78_Init(CartInfo *info) { } //------------------ Map 86 --------------------------- - +// Moero!! Pro Yakyuu has an ADPCM chip with internal ROM, +// used for voice samples (not dumped, so emulation isn't possible) static void M86Sync(void) { setprg32(0x8000, (latche >> 4) & 3); setchr8((latche & 3) | ((latche >> 4) & 4)); @@ -369,31 +371,14 @@ void Mapper94_Init(CartInfo *info) { //------------------ Map 97 --------------------------- static void M97Sync(void) { - setchr8(0); setprg16(0x8000, ~0); setprg16(0xc000, latche & 15); - switch (latche >> 6) { - case 0: break; - case 1: setmirror(MI_H); break; - case 2: setmirror(MI_V); break; - case 3: break; - } - setchr8(((latche >> 1) & 1) | ((latche << 1) & 2)); + setmirror((latche >> 7) & 1); + setchr8(0); } void Mapper97_Init(CartInfo *info) { - Latch_Init(info, M97Sync, ~0, 0x8000, 0xFFFF, 0, 0); -} - -//------------------ Map 101 --------------------------- - -static void M101Sync(void) { - setprg32(0x8000, 0); - setchr8(latche); -} - -void Mapper101_Init(CartInfo *info) { - Latch_Init(info, M101Sync, ~0, 0x6000, 0x7FFF, 0, 0); + Latch_Init(info, M97Sync, ~0, 0x8000, 0xBFFF, 0, 0); } //------------------ Map 107 --------------------------- @@ -474,6 +459,39 @@ void Mapper203_Init(CartInfo *info) { Latch_Init(info, M203Sync, 0, 0x8000, 0xFFFF, 0, 0); } +//------------------ Map 218 --------------------------- + +static void Mapper218_Power() +{ + //doesn't really matter + SetReadHandler(0x6000, 0xFFFF, &CartBROB); +} + +void Mapper218_Init(CartInfo *info) +{ + info->Power = &Mapper218_Power; + + //fixed PRG mapping + setprg32(0x8000, 0); + + //this mapper is supposed to interpret the iNES header bits specially + static const uint8 mirrorings[] = {MI_V,MI_H,MI_0,MI_1}; + SetupCartMirroring(mirrorings[info->mirrorAs2Bits],1,nullptr); + + //cryptic logic to effect the CHR RAM mappings by mapping 1k blocks to NTARAM according to how the pins are wired + //this could be done by bit logic, but this is self-documenting + static const uint8 mapping[] = { + 0,1,0,1,0,1,0,1, //mirrorAs2Bits==0 + 0,0,1,1,0,0,1,1, //mirrorAs2Bits==1 + 0,0,0,0,1,1,1,1, //mirrorAs2Bits==2 + 0,0,0,0,0,0,0,0 //mirrorAs2Bits==3 + }; + for(int i=0;i<8;i++) + VPageR[i] = &NTARAM[mapping[info->mirrorAs2Bits*8+i]]; + + PPUCHRRAM = 0xFF; +} + //------------------ Map 240 --------------------------- static void M240Sync(void) { diff --git a/source/fceultra/boards/edu2000.cpp b/source/fceultra/boards/edu2000.cpp index 36f1ad1..7c1270e 100644 --- a/source/fceultra/boards/edu2000.cpp +++ b/source/fceultra/boards/edu2000.cpp @@ -70,8 +70,7 @@ void UNLEDU2000_Init(CartInfo *info) { WRAM = (uint8*)FCEU_gmalloc(32768); SetupCartPRGMapping(0x10, WRAM, 32768, 1); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = 32768; + info->addSaveGameBuf( WRAM, 32768 ); } AddExState(WRAM, 32768, 0, "WRAM"); AddExState(StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/ffe.cpp b/source/fceultra/boards/ffe.cpp index 51e591e..674f0d2 100644 --- a/source/fceultra/boards/ffe.cpp +++ b/source/fceultra/boards/ffe.cpp @@ -141,8 +141,7 @@ void Mapper6_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/fns.cpp b/source/fceultra/boards/fns.cpp index 08bbe24..8b5c75e 100644 --- a/source/fceultra/boards/fns.cpp +++ b/source/fceultra/boards/fns.cpp @@ -28,17 +28,19 @@ static uint8 DRegs[4]; static uint8 Buffer, BufferShift; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static uint8 *WRAM = NULL; static int kanji_pos, kanji_page, r40C0; static int IRQa, IRQCount; +FCEU_MAYBE_UNUSED static DECLFW(MBWRAM) { if (!(DRegs[3] & 0x10)) Page[A >> 11][A] = V; } +FCEU_MAYBE_UNUSED static DECLFR(MAWRAM) { if (DRegs[3] & 0x10) return X.DB; @@ -254,8 +256,7 @@ void FNS_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); AddExState(DRegs, 4, 0, "DREG"); AddExState(&lreset, 8, 1, "LRST"); diff --git a/source/fceultra/boards/inx007t.cpp b/source/fceultra/boards/inx007t.cpp new file mode 100644 index 0000000..dfecb72 --- /dev/null +++ b/source/fceultra/boards/inx007t.cpp @@ -0,0 +1,88 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2022 Cluster + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * NES 2.0 Mapper 470 denotes the INX_007T_V01 multicart circuit board, + * used for the Retro-Bit re-release of Battletoads and Double Dragon. + * It is basically AOROM with an additional outer bank register at $5000-$5FFF + * whose data selects the 256 KiB outer bank. + * + * $5000-$5FFF + * 7 bit 0 + * ---- ---- + * xxxx xxOO + * || + * ++- Select outer 256 KB PRG ROM bank for CPU $8000-$FFFF + * + * $8000-$FFFF + * 7 bit 0 + * ---- ---- + * xxxM xPPP + * | ||| + * | +++- Select inner 32 KB PRG ROM bank for CPU $8000-$FFFF + * +------ Select 1 KB VRAM page for all 4 nametables + * + */ + +#include "mapinc.h" + +static uint8 latch_out, latch_in; + +static void INX_007T_Sync() { + setprg32(0x8000, (latch_in & 0b111) | (latch_out << 3)); + setmirror(MI_0 + ((latch_in >> 4) & 1)); + setchr8(0); +} + +static void StateRestore(int version) { + INX_007T_Sync(); +} + +static DECLFW(INX_007T_WriteLow) +{ + latch_out = V; + INX_007T_Sync(); +} + +static DECLFW(INX_007T_WriteHi) +{ + latch_in = V; + INX_007T_Sync(); +} + +static void INX_007T_Power(void) { + latch_in = latch_out = 0; + INX_007T_Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); + SetWriteHandler(0x5000,0x5FFF, INX_007T_WriteLow); + SetWriteHandler(0x8000, 0xFFFF, INX_007T_WriteHi); +} + +static void INX_007T_Reset(void) { + latch_in = latch_out = 0; + INX_007T_Sync(); +} + +void INX_007T_Init(CartInfo *info) { + info->Power = INX_007T_Power; + info->Reset = INX_007T_Reset; + GameStateRestore = StateRestore; + SetupCartMirroring(MI_0, 0, NULL); + AddExState(&latch_out, 1, 0, "LATO"); + AddExState(&latch_in, 1, 0, "LATI"); +} diff --git a/source/fceultra/boards/mmc1.cpp b/source/fceultra/boards/mmc1.cpp index bfe07a7..2265aa7 100644 --- a/source/fceultra/boards/mmc1.cpp +++ b/source/fceultra/boards/mmc1.cpp @@ -306,8 +306,7 @@ static void GenMMC1Init(CartInfo *info, int prg, int chr, int wram, int bram) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (bram) { - info->SaveGame[0] = WRAM + NONBRAMSIZE; - info->SaveGameLen[0] = bram * 1024; + info->addSaveGameBuf( WRAM + NONBRAMSIZE, bram * 1024 ); } } if (!chr) { diff --git a/source/fceultra/boards/mmc2and4.cpp b/source/fceultra/boards/mmc2and4.cpp index 5d9d73d..3bde6f7 100644 --- a/source/fceultra/boards/mmc2and4.cpp +++ b/source/fceultra/boards/mmc2and4.cpp @@ -25,7 +25,7 @@ static uint8 is10; static uint8 creg[4], latch0, latch1, preg, mirr; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static SFORMAT StateRegs[] = { @@ -130,8 +130,7 @@ void Mapper10_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } GameStateRestore = StateRestore; AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/mmc3.cpp b/source/fceultra/boards/mmc3.cpp index 880a607..f0eb52d 100644 --- a/source/fceultra/boards/mmc3.cpp +++ b/source/fceultra/boards/mmc3.cpp @@ -324,8 +324,7 @@ void GenMMC3_Init(CartInfo *info, int prg, int chr, int wram, int battery) { if (battery) { mmc3opts |= 2; - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } // KT-008 boards hack 2-in-1, TODO assign to new ines mapper, most dump of KT-boards on the net are mapper 4, so need database or goodnes fix support @@ -364,8 +363,10 @@ static void M4Power(void) { void Mapper4_Init(CartInfo *info) { int ws = 8; - if ((info->CRC32 == 0x93991433 || info->CRC32 == 0xaf65aa84)) { - FCEU_printf("Low-G-Man can not work normally in the iNES format.\nThis game has been recognized by its CRC32 value, and the appropriate changes will be made so it will run.\nIf you wish to hack this game, you should use the UNIF format for your hack.\n\n"); + if (info->ines2) { + ws = (info->wram_size + info->battery_wram_size) / 1024; + } else if ((info->CRC32 == 0x93991433 || info->CRC32 == 0xaf65aa84)) { + FCEU_printf("Low-G-Man can not work normally in the iNES format.\nThis game has been recognized by its CRC32 value, and the appropriate changes will be made so it will run.\nIf you wish to hack this game, you should use the NES 2.0 format for your hack.\n\n"); ws = 0; } GenMMC3_Init(info, 512, 256, ws, info->battery); @@ -499,38 +500,38 @@ void Mapper44_Init(CartInfo *info) { // ---------------------------- Mapper 45 ------------------------------- static void M45CW(uint32 A, uint8 V) { - if (!UNIFchrrama) { - uint32 NV = V; - if (EXPREGS[2] & 8) - NV &= (1 << ((EXPREGS[2] & 7) + 1)) - 1; - else - if (EXPREGS[2]) - NV &= 0; // hack ;( don't know exactly how it should be - NV |= EXPREGS[0] | ((EXPREGS[2] & 0xF0) << 4); - setchr1(A, NV); - } else -// setchr8(0); // i don't know what cart need this, but a new one need other lol - setchr1(A, V); + uint32 NV = V; + const int mask = ((EXPREGS[2] & 0x0F) > 7) + ? ((1 << (EXPREGS[2] & 0x0F) << 3) - 1) + : 0; + NV |= (EXPREGS[0] & mask) | ((EXPREGS[2] & 0xF0) << 4); + setchr1(A, NV); } static void M45PW(uint32 A, uint8 V) { - uint32 MV = V & ((EXPREGS[3] & 0x3F) ^ 0x3F); - MV |= EXPREGS[1]; - if(UNIFchrrama) - MV |= ((EXPREGS[2] & 0x40) << 2); + uint32 MV = V; + const int mask = (EXPREGS[3] & 0x3F) ^ 0x3F; + MV |= (EXPREGS[1] & 0x3F & mask) | (EXPREGS[1] & 0xC0); setprg8(A, MV); -// FCEU_printf("1:%02x 2:%02x 3:%02x A=%04x V=%03x\n",EXPREGS[1],EXPREGS[2],EXPREGS[3],A,MV); } static DECLFW(M45Write) { - if (EXPREGS[3] & 0x40) { - WRAM[A - 0x6000] = V; - return; + WRAM[A - 0x6000] = V; + if (!(A & 1)) + { + if (EXPREGS[3] & 0x40) { + WRAM[A - 0x6000] = V; + return; + } + EXPREGS[EXPREGS[4]] = V; + EXPREGS[4] = (EXPREGS[4] + 1) & 3; + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); + } + else { + // lock reset + EXPREGS[3] &= ~0x40; } - EXPREGS[EXPREGS[4]] = V; - EXPREGS[4] = (EXPREGS[4] + 1) & 3; - FixMMC3PRG(MMC3_cmd); - FixMMC3CHR(MMC3_cmd); } static DECLFR(M45Read) { @@ -551,7 +552,7 @@ static void M45Reset(void) { static void M45Power(void) { GenMMC3Power(); EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = EXPREGS[4] = EXPREGS[5] = 0; - SetWriteHandler(0x5000, 0x7FFF, M45Write); + SetWriteHandler(0x6000, 0x7FFF, M45Write); SetReadHandler(0x5000, 0x5FFF, M45Read); } @@ -861,32 +862,52 @@ void Mapper119_Init(CartInfo *info) { // ---------------------------- Mapper 134 ------------------------------ static void M134PW(uint32 A, uint8 V) { - setprg8(A, (V & 0x1F) | ((EXPREGS[0] & 2) << 4)); + if ((EXPREGS[1] & 0x88) == 0x80) + setprg32(A, (EXPREGS[0] & 0x10) | ((EXPREGS[1] & 2) << 2)); // NROM-256 + else if ((EXPREGS[1] & 0x88) == 0x88) + setprg16(0x8000 | (A & 0x4000), ((EXPREGS[0] & 0x10) << 1) | ((V & 0x10) >> 1) | ((EXPREGS[1] & 3) << 3)); // NROM-128 + else if (EXPREGS[1] & 4) + setprg8(A, ((EXPREGS[0] & 0x10) << 2) | (V & 0x0F) | ((EXPREGS[1] & 3) << 4)); // MMC3 128KB mask + else + setprg8(A, ((EXPREGS[0] & 0x10) << 2) | (V & 0x1F) | ((EXPREGS[1] & 2) << 4)); // MMC3 256KB mask } static void M134CW(uint32 A, uint8 V) { - setchr1(A, (V & 0xFF) | ((EXPREGS[0] & 0x20) << 3)); + // CNROM mode. Unclear. Untested. + if (EXPREGS[0] & 0x08) + setchr8(EXPREGS[2]); + else if (EXPREGS[1] & 0x40) + setchr1(A, ((EXPREGS[0] & 0x20) << 4) | (V & 0x7F) | ((EXPREGS[1] & 0x30) << 3)); // 128KB mask + else + setchr1(A, ((EXPREGS[0] & 0x20) << 4) | (V & 0xFF) | ((EXPREGS[1] & 0x20) << 3)); // 256KB mask } static DECLFW(M134Write) { - EXPREGS[0] = V; + if (EXPREGS[0] & 0x80) + { + // locked (except $6002.0-1) + if ((A & 3) == 2) + EXPREGS[2] = (EXPREGS[2] & 0xFC) | (V & 3); + return; + } + EXPREGS[A & 3] = V; FixMMC3CHR(MMC3_cmd); FixMMC3PRG(MMC3_cmd); } static void M134Power(void) { - EXPREGS[0] = 0; + EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; GenMMC3Power(); - SetWriteHandler(0x6001, 0x6001, M134Write); + SetWriteHandler(0x6000, 0x7FFF, M134Write); } static void M134Reset(void) { - EXPREGS[0] = 0; + EXPREGS[0] = EXPREGS[1] = EXPREGS[2] = EXPREGS[3] = 0; MMC3RegReset(); } void Mapper134_Init(CartInfo *info) { - GenMMC3_Init(info, 256, 256, 0, 0); + GenMMC3_Init(info, 512, 512, 0, 0); pwrap = M134PW; cwrap = M134CW; info->Power = M134Power; @@ -1137,20 +1158,73 @@ void Mapper198_Init(CartInfo *info) { info->Power = M195Power; } -// ---------------------------- Mapper 205 ------------------------------ -// GN-45 BOARD +/* ---------------------------- Mapper 205 ------------------------------ */ +/* UNIF boardname BMC-JC-016-2 +https://wiki.nesdev.com/w/index.php/INES_Mapper_205 */ + +/* 2023-02 : Update reg write logic and add solder pad */ static void M205PW(uint32 A, uint8 V) { -// GN-30A - íà÷àëüíàÿ ìàñêà äîëæíà áûòü 1F + àïïàðàòíûé ïåðåêëþ÷àòåëü íà øèíå àäðåñà - setprg8(A, (V & 0x0f) | EXPREGS[0]); + uint8 bank = V & ((EXPREGS[0] & 0x02) ? 0x0F : 0x1F); + if (PRGsize[1]) { // split-rom variant + setprg8r((EXPREGS[0] & 3) ? (EXPREGS[0] - 1) : 0, A, bank); + } else { + setprg8(A, EXPREGS[0] << 4 | bank); + } } static void M205CW(uint32 A, uint8 V) { -// GN-30A - íà÷àëüíàÿ ìàñêà äîëæíà áûòü FF + uint8 bank = V & ((EXPREGS[0] & 0x02) ? 0x7F : 0xFF); + if (CHRsize[1]) { // split-rom variant + setchr1r((EXPREGS[0] & 3) ? (EXPREGS[0] - 1) : 0, A, bank); + } else { + setchr1(A, (EXPREGS[0] << 7) | bank); + } +} + +static DECLFW(M205Write) { + EXPREGS[0] = V & 3; + if (V & 1) { + EXPREGS[0] |= EXPREGS[1]; + } + CartBW(A, V); + FixMMC3PRG(MMC3_cmd); + FixMMC3CHR(MMC3_cmd); +} + +static void M205Reset(void) { + EXPREGS[0] = 0; + EXPREGS[1] ^= 2; /* solder pad */ + MMC3RegReset(); +} + +static void M205Power(void) { + EXPREGS[0] = EXPREGS[1] = 0; + GenMMC3Power(); + SetWriteHandler(0x6000, 0x7FFF, M205Write); +} + +void Mapper205_Init(CartInfo *info) { + GenMMC3_Init(info, 256, 128, 0, 0); + pwrap = M205PW; + cwrap = M205CW; + info->Power = M205Power; + info->Reset = M205Reset; + AddExState(EXPREGS, 2, 0, "EXPR"); +} + +/* --------------------------- GN-45 BOARD ------------------------------ */ + +/* Mapper 361 and 366, previously assigned as Mapper 205 */ +static void GN45PW(uint32 A, uint8 V) { + setprg8(A, (V & 0x0f) | EXPREGS[0]); +} + +static void GN45CW(uint32 A, uint8 V) { setchr1(A, (V & 0x7F) | (EXPREGS[0] << 3)); } -static DECLFW(M205Write0) { +static DECLFW(GN45Write0) { if (EXPREGS[2] == 0) { EXPREGS[0] = A & 0x30; EXPREGS[2] = A & 0x80; @@ -1160,7 +1234,7 @@ static DECLFW(M205Write0) { CartBW(A, V); } -static DECLFW(M205Write1) { +static DECLFW(GN45Write1) { if (EXPREGS[2] == 0) { EXPREGS[0] = V & 0x30; FixMMC3PRG(MMC3_cmd); @@ -1169,23 +1243,23 @@ static DECLFW(M205Write1) { CartBW(A, V); } -static void M205Reset(void) { +static void GN45Reset(void) { EXPREGS[0] = EXPREGS[2] = 0; MMC3RegReset(); } -static void M205Power(void) { +static void GN45Power(void) { GenMMC3Power(); - SetWriteHandler(0x6000, 0x6fff, M205Write0); - SetWriteHandler(0x7000, 0x7fff, M205Write1); // OK-411 boards, the same logic, but data latched, 2-in-1 frankenstein + SetWriteHandler(0x6000, 0x6fff, GN45Write0); + SetWriteHandler(0x7000, 0x7fff, GN45Write1); /* OK-411 boards, the same logic, but data latched, 2-in-1 frankenstein */ } -void Mapper205_Init(CartInfo *info) { +void GN45_Init(CartInfo *info) { GenMMC3_Init(info, 128, 128, 8, 0); - pwrap = M205PW; - cwrap = M205CW; - info->Power = M205Power; - info->Reset = M205Reset; + pwrap = GN45PW; + cwrap = GN45CW; + info->Power = GN45Power; + info->Reset = GN45Reset; AddExState(EXPREGS, 1, 0, "EXPR"); } @@ -1376,6 +1450,7 @@ static DECLFW(M406IRQWrite) { MMC3_IRQWrite((A & 0xFFFE) | ((A & 2) >> 1), V); } +FCEU_MAYBE_UNUSED static DECLFW(M406Write) { } diff --git a/source/fceultra/boards/mmc5.cpp b/source/fceultra/boards/mmc5.cpp index ac98d57..33cd84b 100644 --- a/source/fceultra/boards/mmc5.cpp +++ b/source/fceultra/boards/mmc5.cpp @@ -307,8 +307,7 @@ cartdata MMC5CartList[] = #define MMC5_NOCARTS (sizeof(MMC5CartList) / sizeof(MMC5CartList[0])) int DetectMMC5WRAMSize(uint32 crc32) { - int x; - for (x = 0; x < MMC5_NOCARTS; x++) { + for (size_t x = 0; x < MMC5_NOCARTS; x++) { if (crc32 == MMC5CartList[x].crc32) { if(MMC5CartList[x].size > 1) FCEU_printf(" >8KB external WRAM present. Use UNIF if you hack the ROM image.\n"); @@ -425,7 +424,7 @@ static void MMC5PRG(void) { switch (mmc5psize & 3) { case 0: MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = MMC5ROMWrProtect[2] = MMC5ROMWrProtect[3] = 1; - setprg32(0x8000, ((PRGBanks[1] & 0x7F) >> 2)); + setprg32(0x8000, ((PRGBanks[3] & 0x7F) >> 2)); for (x = 0; x < 4; x++) MMC5MemIn[1 + x] = 1; break; @@ -904,14 +903,14 @@ static void GenMMC5Power(void) { PRGBanks.fill(0xFF); WRAMPage = 0; - CHRBanksA.fill(0xFF); - CHRBanksB.fill(0xFF); + CHRBanksA.fill(0); + CHRBanksB.fill(0); WRAMMaskEnable.fill(0xFF); mmc5ABMode = 0; IRQScanline = 0; IRQEnable = 0; CHRMode = 0; - NTAMirroring = NTFill = ATFill = 0xFF; + NTAMirroring = NTFill = ATFill = 0; MMC5IRQR = 0; MMC5LineCounter = 0; mmc5psize = mmc5vsize = 3; @@ -1022,22 +1021,23 @@ static void GenMMC5_Init(CartInfo *info, int wsize, int battery) { MMC5battery = battery; if (battery) { - info->SaveGame[0] = WRAM; + uint32 saveGameSize = 0; if (info->ines2) { - info->SaveGameLen[0] = info->battery_wram_size; + saveGameSize = info->battery_wram_size; } else { //this is more complex than it looks because it MUST BE, I guess. is there an assumption that only 8KB of 16KB is battery backed? That's NES mappers for you //I added 64KB for the new 64KB homebrews if (wsize <= 16) - info->SaveGameLen[0] = 8192; + saveGameSize = 8192; else if(wsize == 64) - info->SaveGameLen[0] = 64*1024; + saveGameSize = 64*1024; else - info->SaveGameLen[0] = 32768; + saveGameSize = 32768; } + info->addSaveGameBuf( WRAM, saveGameSize ); } MMC5HackVROMMask = CHRmask4[0]; diff --git a/source/fceultra/boards/n106.cpp b/source/fceultra/boards/n106.cpp index cbbbc79..59399e1 100644 --- a/source/fceultra/boards/n106.cpp +++ b/source/fceultra/boards/n106.cpp @@ -429,10 +429,8 @@ void Mapper19_Init(CartInfo *info) { AddExState(N106_StateRegs, ~0, 0, 0); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = 8192; - info->SaveGame[1] = IRAM; - info->SaveGameLen[1] = 128; + info->addSaveGameBuf( WRAM, 8192 ); + info->addSaveGameBuf( IRAM, 128 ); } } diff --git a/source/fceultra/boards/onebus.cpp b/source/fceultra/boards/onebus.cpp index 4336b77..4391394 100644 --- a/source/fceultra/boards/onebus.cpp +++ b/source/fceultra/boards/onebus.cpp @@ -320,8 +320,7 @@ void UNLOneBus_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } } } diff --git a/source/fceultra/boards/sa-9602b.cpp b/source/fceultra/boards/sa-9602b.cpp index a6a19bb..409e5dd 100644 --- a/source/fceultra/boards/sa-9602b.cpp +++ b/source/fceultra/boards/sa-9602b.cpp @@ -54,8 +54,7 @@ void SA9602B_Init(CartInfo *info) { GenMMC3_Init(info, 512, 0, 0, 0); pwrap = SA9602BPW; mmc3opts |= 2; - info->SaveGame[0] = UNIFchrrama; - info->SaveGameLen[0] = 32 * 1024; + info->addSaveGameBuf( UNIFchrrama, 32 * 1024 ); info->Power = SA9602BPower; AddExState(EXPREGS, 2, 0, "EXPR"); } diff --git a/source/fceultra/boards/sb-2000.cpp b/source/fceultra/boards/sb-2000.cpp index c2ea454..cf3ad0e 100644 --- a/source/fceultra/boards/sb-2000.cpp +++ b/source/fceultra/boards/sb-2000.cpp @@ -24,7 +24,7 @@ static uint8 preg[8]; static uint8 IRQa; static int16 IRQCount, IRQLatch; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; /* static uint8 *CHRRAM = NULL; static uint32 CHRRAMSIZE; @@ -187,8 +187,7 @@ void UNLSB2000_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/transformer.cpp b/source/fceultra/boards/transformer.cpp index b125255..642afbf 100644 --- a/source/fceultra/boards/transformer.cpp +++ b/source/fceultra/boards/transformer.cpp @@ -21,7 +21,7 @@ #include "mapinc.h" static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; unsigned int *GetKeyboard(void); // FIXME: 10/28 - now implemented in SDL as well. should we rename this to a FCEUI_* function? @@ -90,8 +90,7 @@ void Transformer_Init(CartInfo *info) { WRAM = (uint8*)FCEU_gmalloc(WRAMSIZE); SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(WRAM, WRAMSIZE, 0, "WRAM"); } diff --git a/source/fceultra/boards/unrom512.cpp b/source/fceultra/boards/unrom512.cpp index 973749e..b28ce7f 100644 --- a/source/fceultra/boards/unrom512.cpp +++ b/source/fceultra/boards/unrom512.cpp @@ -1,7 +1,7 @@ /* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: - * Copyright (C) 2014 CaitSith2 + * Copyright (C) 2014 CaitSith2, 2022 Cluster * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ */ /* - * Roms still using NES 1.0 format should be loaded as 32K CHR RAM. + * Roms still using NES 1.0 format should be loaded as 8K CHR RAM. * Roms defined under NES 2.0 should use the VRAM size field, defining 7, 8 or 9, based on how much VRAM should be present. * UNIF doesn't have this problem, because unique board names can define this information. * The UNIF names are UNROM-512-8K, UNROM-512-16K and UNROM-512-32K @@ -28,6 +28,7 @@ * Known games to use this board are: * Battle Kid 2: Mountain of Torment (512K PRG, 8K CHR RAM, Horizontal Mirroring, Flash disabled) * Study Hall (128K PRG (in 512K flash chip), 8K CHR RAM, Horizontal Mirroring, Flash enabled) + * Nix: The Paradox Relic (512 PRG, 8K CHR RAM, Vertical Mirroring, Flash enabled) * Although Xmas 2013 uses a different board, where LEDs can be controlled (with writes to the $8000-BFFF space), * it otherwise functions identically. */ @@ -35,211 +36,159 @@ #include "mapinc.h" #include "../ines.h" -static uint8 latche, latcheinit, bus_conflict, chrram_mask, software_id=false; +const int ROM_CHIP = 0x00; +const int CFI_CHIP = 0x10; +const int FLASH_CHIP = 0x11; + +const int FLASH_SECTOR_SIZE = 4 * 1024; + +static uint8 flash_save, flash_state, flash_id_mode, latche, bus_conflict; static uint16 latcha; -static uint8 *flashdata; -static uint32 *flash_write_count; -static uint8 *FlashPage[32]; -//static uint32 *FlashWriteCountPage[32]; -//static uint8 flashloaded = false; +static uint8 *flash_data; +static uint16 flash_buffer_a[10]; +static uint8 flash_buffer_v[10]; +static uint8 flash_id[2]; -static uint8 flash_save=0, flash_state=0, flash_mode=0, flash_bank; -static void (*WLSync)(void); -static void (*WHSync)(void); - -static INLINE void setfpageptr(int s, uint32 A, uint8 *p) { - uint32 AB = A >> 11; - int x; - - if (p) - for (x = (s >> 1) - 1; x >= 0; x--) { - FlashPage[AB + x] = p - A; - } +static void UNROM512_Sync() { + int chip; + if (flash_save) + chip = !flash_id_mode ? FLASH_CHIP : CFI_CHIP; else - for (x = (s >> 1) - 1; x >= 0; x--) { - FlashPage[AB + x] = 0; - } -} - -void setfprg16(uint32 A, uint32 V) { - if (PRGsize[0] >= 16384) { - V &= PRGmask16[0]; - setfpageptr(16, A, flashdata ? (&flashdata[V << 14]) : 0); - } else { - uint32 VA = V << 3; - int x; - - for (x = 0; x < 8; x++) - setfpageptr(2, A + (x << 11), flashdata ? (&flashdata[((VA + x) & PRGmask2[0]) << 11]) : 0); - } -} - -void inc_flash_write_count(uint8 bank, uint32 A) -{ - flash_write_count[(bank*4) + ((A&0x3000)>>12)]++; - if(!flash_write_count[(bank*4) + ((A&0x3000)>>12)]) - flash_write_count[(bank*4) + ((A&0x3000)>>12)]++; -} - -uint32 GetFlashWriteCount(uint8 bank, uint32 A) -{ - return flash_write_count[(bank*4) + ((A&0x3000)>>12)]; + chip = ROM_CHIP; + setprg16r(chip, 0x8000, latche); + setprg16r(chip, 0xc000, ~0); + setchr8(latche >> 5); + setmirror(MI_0 + ((latche >> 7) & 1)); } static void StateRestore(int version) { - WHSync(); + UNROM512_Sync(); } -static DECLFW(UNROM512LLatchWrite) +static DECLFW(UNROM512FlashWrite) { - latche = V; - latcha = A; - WLSync(); + if (flash_state < sizeof(flash_buffer_a) / sizeof(flash_buffer_a[0])) { + flash_buffer_a[flash_state] = (A & 0x3FFF) | ((latche & 1) << 14); + flash_buffer_v[flash_state] = V; + flash_state++; + + // enter flash ID mode + if ((flash_state == 2) && + (flash_buffer_a[0] == 0x5555) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x2AAA) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[1] == 0x5555) && (flash_buffer_v[1] == 0x90)) { + flash_id_mode = 0; + flash_state = 0; + } + + // erase sector + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x5555) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x2AAA) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x5555) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x5555) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x2AAA) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_v[5] == 0x30)) { + int offset = &Page[A >> 11][A] - flash_data; + int sector = offset / FLASH_SECTOR_SIZE; + for (int i = sector * FLASH_SECTOR_SIZE; i < (sector + 1) * FLASH_SECTOR_SIZE; i++) + flash_data[i % PRGsize[ROM_CHIP]] = 0xFF; + FCEU_printf("Flash sector #%d is erased (0x%08x - 0x%08x).\n", sector, offset, offset + FLASH_SECTOR_SIZE); + } + + // erase chip + if ((flash_state == 6) && + (flash_buffer_a[0] == 0x5555) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x2AAA) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x5555) && (flash_buffer_v[2] == 0x80) && + (flash_buffer_a[3] == 0x5555) && (flash_buffer_v[3] == 0xAA) && + (flash_buffer_a[4] == 0x2AAA) && (flash_buffer_v[4] == 0x55) && + (flash_buffer_a[4] == 0x5555) && (flash_buffer_v[4] == 0x10)) { + memset(flash_data, 0xFF, PRGsize[ROM_CHIP]); + FCEU_printf("Flash chip erased.\n"); + flash_state = 0; + } + + // write byte + if ((flash_state == 4) && + (flash_buffer_a[0] == 0x5555) && (flash_buffer_v[0] == 0xAA) && + (flash_buffer_a[1] == 0x2AAA) && (flash_buffer_v[1] == 0x55) && + (flash_buffer_a[2] == 0x5555) && (flash_buffer_v[2] == 0xA0)) { + int offset = &Page[A >> 11][A] - flash_data; + if (CartBR(A) != 0xFF) { + FCEU_PrintError("Error: can't write to 0x%08x, flash sector is not erased.\n", offset); + } + else { + CartBW(A, V); + } + flash_state = 0; + } + } + + // not a command + if (((A & 0xFFF) != 0x0AAA) && ((A & 0xFFF) != 0x0555)) { + flash_state = 0; + } + + // reset + if (V == 0xF0) { + flash_state = 0; + flash_id_mode = 0; + } + + UNROM512_Sync(); } static DECLFW(UNROM512HLatchWrite) { if (bus_conflict) - latche = (V == CartBR(A)) ? V : 0; + latche = V & CartBR(A); else latche = V; latcha = A; - WHSync(); -} - -static DECLFR(UNROM512LatchRead) -{ - uint8 flash_id[3]={0xB5,0xB6,0xB7}; - if(software_id) - { - if(A&1) - return flash_id[ROM_size>>4]; - else - return 0xBF; - } - if(flash_save) - { - if(A < 0xC000) - { - if(GetFlashWriteCount(flash_bank,A)) - return FlashPage[A >> 11][A]; - } - else - { - if(GetFlashWriteCount(ROM_size-1,A)) - return FlashPage[A >> 11][A]; - } - } - return Page[A >> 11][A]; + UNROM512_Sync(); } static void UNROM512LatchPower(void) { - latche = latcheinit; - WHSync(); - SetReadHandler(0x8000, 0xFFFF, UNROM512LatchRead); + latche = 0; + UNROM512_Sync(); + SetReadHandler(0x8000, 0xFFFF, CartBR); if(!flash_save) SetWriteHandler(0x8000, 0xFFFF, UNROM512HLatchWrite); else { - SetWriteHandler(0x8000,0xBFFF,UNROM512LLatchWrite); + SetWriteHandler(0x8000,0xBFFF,UNROM512FlashWrite); SetWriteHandler(0xC000,0xFFFF,UNROM512HLatchWrite); } } static void UNROM512LatchClose(void) { - if(flash_write_count) - FCEU_gfree(flash_write_count); - if(flashdata) - FCEU_gfree(flashdata); - flash_write_count = NULL; - flashdata = NULL; + if(flash_data) + FCEU_gfree(flash_data); + flash_data = NULL; } - -static void UNROM512LSync() { - int erase_a[5]={0x9555,0xAAAA,0x9555,0x9555,0xAAAA}; - int erase_d[5]={0xAA,0x55,0x80,0xAA,0x55}; - int erase_b[5]={1,0,1,1,0}; - - if(flash_mode==0) - { - if((latcha == erase_a[flash_state]) && (latche == erase_d[flash_state]) && (flash_bank == erase_b[flash_state])) - { - flash_state++; - if(flash_state == 5) - { - flash_mode=1; - } - } - else if ((flash_state==2)&&(latcha==0x9555)&&(latche==0xA0)&&(flash_bank==1)) - { - flash_state++; - flash_mode=2; - } - else if ((flash_state==2)&&(latcha==0x9555)&&(latche==0x90)&&(flash_bank==1)) - { - flash_state=0; - software_id=true; - } - else - { - if(latche==0xF0) - software_id=false; - flash_state=0; - } - } - else if(flash_mode==1) //Chip Erase or Sector Erase - { - if(latche==0x30) - { - inc_flash_write_count(flash_bank,latcha); - memset(&FlashPage[(latcha & 0xF000) >> 11][latcha & 0xF000],0xFF,0x1000); - } - else if (latche==0x10) - { - for(uint32 i=0;i<(ROM_size*4);i++) - inc_flash_write_count(i>>2,i<<12); - memset(flashdata,0xFF,ROM_size*0x4000); //Erasing the rom chip as instructed. Crash rate calulated to be 99.9% :) - } - flash_state=0; - flash_mode=0; - } - else if(flash_mode==2) //Byte Program - { - if(!GetFlashWriteCount(flash_bank,latcha)) - { - inc_flash_write_count(flash_bank,latcha); - memcpy(&FlashPage[(latcha & 0xF000) >> 11][latcha & 0xF000],&Page[(latcha & 0xF000)>>11][latcha & 0xF000],0x1000); - } - FlashPage[latcha>>11][latcha]&=latche; - flash_state=0; - flash_mode=0; - } -} - -static void UNROM512HSync() +static void UNROM512_FlashReset(void) { - flash_bank=latche&(ROM_size-1); - - setprg16(0x8000, flash_bank); - setprg16(0xc000, ~0); - setfprg16(0x8000, flash_bank); - setfprg16(0xC000, ~0); - setchr8r(0, (latche & chrram_mask) >> 5); - setmirror(MI_0+(latche>>7)); + if (flash_data) + { + size_t flash_size = PRGsize[ROM_CHIP]; + // Copy ROM to flash data + for (size_t i = 0; i < flash_size; i++) { + flash_data[i] = PRGptr[ROM_CHIP][i]; + } + } } void UNROM512_Init(CartInfo *info) { - flash_state=0; - flash_bank=0; - flash_save=info->battery; + info->Power = UNROM512LatchPower; + info->Close = UNROM512LatchClose; + GameStateRestore = StateRestore; - if(info->vram_size == 8192) - chrram_mask = 0; - else if (info->vram_size == 16384) - chrram_mask = 0x20; - else - chrram_mask = 0x60; + flash_state = 0; + flash_id_mode = 0; + flash_save = info->battery; + bus_conflict = !info->battery; // Is it required by any game? int mirror = (head.ROM_type & 1) | ((head.ROM_type & 8) >> 2); switch (mirror) @@ -258,28 +207,29 @@ void UNROM512_Init(CartInfo *info) { break; } - bus_conflict = !info->battery; - latcheinit = 0; - WLSync = UNROM512LSync; - WHSync = UNROM512HSync; - info->Power = UNROM512LatchPower; - info->Close = UNROM512LatchClose; - GameStateRestore = StateRestore; if(flash_save) { - flashdata = (uint8*)FCEU_gmalloc(ROM_size*0x4000); - flash_write_count = (uint32*)FCEU_gmalloc(ROM_size*4*sizeof(uint32)); - info->SaveGame[0] = (uint8*)flash_write_count; - info->SaveGame[1] = flashdata; - info->SaveGameLen[0] = ROM_size*4*sizeof(uint32); - info->SaveGameLen[1] = ROM_size*0x4000; - AddExState(flash_write_count,ROM_size*4*sizeof(uint32),0,"FLASH_WRITE_COUNT"); - AddExState(flashdata,ROM_size*0x4000,0,"FLASH_DATA"); - AddExState(&flash_state,1,0,"FLASH_STATE"); - AddExState(&flash_mode,1,0,"FLASH_MODE"); - AddExState(&flash_bank,1,0,"FLASH_BANK"); - AddExState(&latcha,2,0,"LATA"); + // Allocate memory for flash + size_t flash_size = PRGsize[ROM_CHIP]; + flash_data = (uint8*)FCEU_gmalloc(flash_size); + // Copy ROM to flash data + for (size_t i = 0; i < flash_size; i++) { + flash_data[i] = PRGptr[ROM_CHIP][i]; + } + SetupCartPRGMapping(FLASH_CHIP, flash_data, flash_size, 1); + info->addSaveGameBuf( flash_data, flash_size, UNROM512_FlashReset ); + + flash_id[0] = 0xBF; + flash_id[1] = 0xB5 + (ROM_size >> 4); + SetupCartPRGMapping(CFI_CHIP, flash_id, sizeof(flash_id), 0); + + AddExState(flash_data, flash_size, 0, "FLSH"); + AddExState(&flash_state, sizeof(flash_state), 0, "FLST"); + AddExState(&flash_id_mode, sizeof(flash_id_mode), 0, "FLMD"); + AddExState(flash_buffer_a, sizeof(flash_buffer_a), 0, "FLBA"); + AddExState(flash_buffer_v, sizeof(flash_buffer_v), 0, "FLBV"); } - AddExState(&latche, 1, 0, "LATC"); - AddExState(&bus_conflict, 1, 0, "BUSC"); + AddExState(&latcha, sizeof(latcha), 0, "LATA"); + AddExState(&latche, sizeof(latche), 0, "LATC"); + AddExState(&bus_conflict, sizeof(bus_conflict), 0, "BUSC"); } diff --git a/source/fceultra/boards/vrc2and4.cpp b/source/fceultra/boards/vrc2and4.cpp index 97e6e53..564c51b 100644 --- a/source/fceultra/boards/vrc2and4.cpp +++ b/source/fceultra/boards/vrc2and4.cpp @@ -23,7 +23,7 @@ static bool isPirate; static uint8 is22, reg1mask, reg2mask; static uint16 IRQCount; -static uint8 IRQLatch, IRQa; +static uint8 IRQLatch, IRQa, IRQMode; static uint8 prgreg[2], chrreg[8]; static uint16 chrhi[8]; static uint8 regcmd, irqcmd, mirr, big_bank; @@ -45,6 +45,7 @@ static SFORMAT StateRegs[] = { &IRQCount, 2, "IRQC" }, { &IRQLatch, 1, "IRQL" }, { &IRQa, 1, "IRQA" }, + { &IRQMode, 1, "IRQM" }, { 0 } }; @@ -115,7 +116,7 @@ static DECLFW(VRC24Write) { case 0x9003: regcmd = V; Sync(); break; case 0xF000: X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0xF0; IRQLatch |= V & 0xF; break; case 0xF001: X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0x0F; IRQLatch |= V << 4; break; - case 0xF002: X6502_IRQEnd(FCEU_IQEXT); acount = 0; IRQCount = IRQLatch; IRQa = V & 2; irqcmd = V & 1; break; + case 0xF002: X6502_IRQEnd(FCEU_IQEXT); acount = 0; IRQCount = IRQLatch; IRQMode = V & 4; IRQa = V & 2; irqcmd = V & 1; break; case 0xF003: X6502_IRQEnd(FCEU_IQEXT); IRQa = irqcmd; break; } } @@ -136,16 +137,28 @@ static void VRC24Power(void) { void VRC24IRQHook(int a) { #define LCYCS 341 if (IRQa) { - acount += a * 3; - if (acount >= LCYCS) { - while (acount >= LCYCS) { - acount -= LCYCS; + if (IRQMode) { + acount += a; + while (acount > 0) { + acount--; IRQCount++; if (IRQCount & 0x100) { X6502_IRQBegin(FCEU_IQEXT); IRQCount = IRQLatch; } } + } else { + acount += a * 3; + if (acount >= LCYCS) { + while (acount >= LCYCS) { + acount -= LCYCS; + IRQCount++; + if (IRQCount & 0x100) { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount = IRQLatch; + } + } + } } } } @@ -172,8 +185,7 @@ static void VRC24_Init(CartInfo *info) { AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if(info->battery) { - info->SaveGame[0]=WRAM; - info->SaveGameLen[0]=WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/vrc5.cpp b/source/fceultra/boards/vrc5.cpp index 07de801..0c9e362 100644 --- a/source/fceultra/boards/vrc5.cpp +++ b/source/fceultra/boards/vrc5.cpp @@ -254,11 +254,10 @@ void QTAi_Init(CartInfo *info) { AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; // note, only extrnal cart's SRAM is battery backed, the the part on the main cartridge is just // an additional work ram. so we may save only half here, but I forgot what part is saved lol, will // find out later. - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/vrc6.cpp b/source/fceultra/boards/vrc6.cpp index 43cd382..030f74c 100644 --- a/source/fceultra/boards/vrc6.cpp +++ b/source/fceultra/boards/vrc6.cpp @@ -25,10 +25,10 @@ static uint8 is26; static uint8 prg[2], chr[8], mirr; -static uint8 IRQLatch, IRQa, IRQd; +static uint8 IRQLatch, IRQa, IRQd, IRQMode; static int32 IRQCount, CycleCount; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; static SFORMAT StateRegs[] = { @@ -40,6 +40,7 @@ static SFORMAT StateRegs[] = { &IRQLatch, 1, "IRQL" }, { &IRQCount, 4, "IRQC" }, { &CycleCount, 4, "CYCC" }, + { &IRQMode, 1, "IRQM" }, { 0 } }; @@ -109,6 +110,7 @@ static DECLFW(VRC6Write) { case 0xE003: chr[7] = V; Sync(); break; case 0xF000: IRQLatch = V; X6502_IRQEnd(FCEU_IQEXT); break; case 0xF001: + IRQMode = V & 4; IRQa = V & 2; IRQd = V & 1; if (V & 2) @@ -132,13 +134,25 @@ static void VRC6Power(void) { static void VRC6IRQHook(int a) { if (IRQa) { - CycleCount += a * 3; - while(CycleCount >= 341) { - CycleCount -= 341; - IRQCount++; - if (IRQCount == 0x100) { - IRQCount = IRQLatch; - X6502_IRQBegin(FCEU_IQEXT); + if (IRQMode) { + CycleCount += a; + while (CycleCount > 0) { + CycleCount--; + IRQCount++; + if (IRQCount & 0x100) { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount = IRQLatch; + } + } + } else { + CycleCount += a * 3; + while(CycleCount >= 341) { + CycleCount -= 341; + IRQCount++; + if (IRQCount == 0x100) { + IRQCount = IRQLatch; + X6502_IRQBegin(FCEU_IQEXT); + } } } } @@ -365,8 +379,7 @@ void Mapper26_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } AddExState(&StateRegs, ~0, 0, 0); diff --git a/source/fceultra/boards/vrc7.cpp b/source/fceultra/boards/vrc7.cpp index d4df33a..a37d76f 100644 --- a/source/fceultra/boards/vrc7.cpp +++ b/source/fceultra/boards/vrc7.cpp @@ -21,10 +21,10 @@ #include "mapinc.h" static uint8 vrc7idx, preg[3], creg[8], mirr; -static uint8 IRQLatch, IRQa, IRQd; +static uint8 IRQLatch, IRQa, IRQd, IRQMode; static int32 IRQCount, CycleCount; static uint8 *WRAM = NULL; -static uint32 WRAMSIZE; +static uint32 WRAMSIZE=0; #include "emu2413.h" @@ -44,6 +44,7 @@ static SFORMAT StateRegs[] = { &IRQCount, 4, "IRQC" }, { &CycleCount, 4, "CYCC" }, { (void**)VRC7Sound_saveptr, sizeof(*VRC7Sound) | FCEUSTATE_INDIRECT, "VRC7" }, + { &IRQMode, 1, "IRQM" }, {0} }; @@ -134,6 +135,7 @@ static DECLFW(VRC7Write) { case 0xE000: mirr = V & 3; Sync(); break; case 0xE010: IRQLatch = V; X6502_IRQEnd(FCEU_IQEXT); break; case 0xF000: + IRQMode = V & 4; IRQa = V & 2; IRQd = V & 1; if (V & 2) @@ -165,13 +167,25 @@ static void VRC7Close(void) static void VRC7IRQHook(int a) { if (IRQa) { - CycleCount += a * 3; - while(CycleCount >= 341) { - CycleCount -= 341; - IRQCount++; - if (IRQCount == 0x100) { - IRQCount = IRQLatch; - X6502_IRQBegin(FCEU_IQEXT); + if (IRQMode) { + CycleCount += a; + while (CycleCount > 0) { + CycleCount--; + IRQCount++; + if (IRQCount & 0x100) { + X6502_IRQBegin(FCEU_IQEXT); + IRQCount = IRQLatch; + } + } + } else { + CycleCount += a * 3; + while(CycleCount >= 341) { + CycleCount -= 341; + IRQCount++; + if (IRQCount == 0x100) { + IRQCount = IRQLatch; + X6502_IRQBegin(FCEU_IQEXT); + } } } } @@ -190,8 +204,7 @@ void Mapper85_Init(CartInfo *info) { SetupCartPRGMapping(0x10, WRAM, WRAMSIZE, 1); AddExState(WRAM, WRAMSIZE, 0, "WRAM"); if (info->battery) { - info->SaveGame[0] = WRAM; - info->SaveGameLen[0] = WRAMSIZE; + info->addSaveGameBuf( WRAM, WRAMSIZE ); } GameStateRestore = StateRestore; VRC7_ESI(); diff --git a/source/fceultra/cart.cpp b/source/fceultra/cart.cpp index e050811..4fc62b3 100644 --- a/source/fceultra/cart.cpp +++ b/source/fceultra/cart.cpp @@ -540,19 +540,28 @@ void FCEU_GeniePower(void) { } -void FCEU_SaveGameSave(CartInfo *LocalHWInfo) { - if (LocalHWInfo->battery && LocalHWInfo->SaveGame[0]) { +void FCEU_SaveGameSave(CartInfo *LocalHWInfo) +{ + if (LocalHWInfo->battery && !LocalHWInfo->SaveGame.empty()) + { FILE *sp; std::string soot = FCEU_MakeFName(FCEUMKF_SAV, 0, "sav"); - if ((sp = FCEUD_UTF8fopen(soot, "wb")) == NULL) { + if ((sp = FCEUD_UTF8fopen(soot, "wb")) == NULL) + { FCEU_PrintError("WRAM file \"%s\" cannot be written to.\n", soot.c_str()); - } else { - for (int x = 0; x < 4; x++) - if (LocalHWInfo->SaveGame[x]) { - fwrite(LocalHWInfo->SaveGame[x], 1, - LocalHWInfo->SaveGameLen[x], sp); + } + else + { + for (size_t x = 0; x < LocalHWInfo->SaveGame.size(); x++) + { + if (LocalHWInfo->SaveGame[x].bufptr) + { + fwrite(LocalHWInfo->SaveGame[x].bufptr, 1, + LocalHWInfo->SaveGame[x].buflen, sp); } + } + fclose(sp); } } } @@ -560,25 +569,46 @@ void FCEU_SaveGameSave(CartInfo *LocalHWInfo) { // hack, movie.cpp has to communicate with this function somehow int disableBatteryLoading = 0; -void FCEU_LoadGameSave(CartInfo *LocalHWInfo) { - if (LocalHWInfo->battery && LocalHWInfo->SaveGame[0] && !disableBatteryLoading) { +void FCEU_LoadGameSave(CartInfo *LocalHWInfo) +{ + if (LocalHWInfo->battery && !LocalHWInfo->SaveGame.empty() && !disableBatteryLoading) + { FILE *sp; std::string soot = FCEU_MakeFName(FCEUMKF_SAV, 0, "sav"); sp = FCEUD_UTF8fopen(soot, "rb"); - if (sp != NULL) { - for (int x = 0; x < 4; x++) - if (LocalHWInfo->SaveGame[x]) - fread(LocalHWInfo->SaveGame[x], 1, LocalHWInfo->SaveGameLen[x], sp); + if (sp != NULL) + { + for (size_t x = 0; x < LocalHWInfo->SaveGame.size(); x++) + { + if (LocalHWInfo->SaveGame[x].bufptr) + { + if ( fread(LocalHWInfo->SaveGame[x].bufptr, 1, LocalHWInfo->SaveGame[x].buflen, sp) != LocalHWInfo->SaveGame[x].buflen ) + { + FCEU_printf("Warning save game data read came up short!\n"); + } + } + } + fclose(sp); } } } //clears all save memory. call this if you want to pretend the saveram has been reset (it doesnt touch what is on disk though) -void FCEU_ClearGameSave(CartInfo *LocalHWInfo) { - if (LocalHWInfo->battery && LocalHWInfo->SaveGame[0]) { - for (int x = 0; x < 4; x++) - if (LocalHWInfo->SaveGame[x]) - memset(LocalHWInfo->SaveGame[x], 0, LocalHWInfo->SaveGameLen[x]); +void FCEU_ClearGameSave(CartInfo *LocalHWInfo) +{ + if (LocalHWInfo->battery && !LocalHWInfo->SaveGame.empty()) + { + for (size_t x = 0; x < LocalHWInfo->SaveGame.size(); x++) + { + if (LocalHWInfo->SaveGame[x].bufptr) + { + memset(LocalHWInfo->SaveGame[x].bufptr, 0, LocalHWInfo->SaveGame[x].buflen); + } + if (LocalHWInfo->SaveGame[x].resetFunc) + { + LocalHWInfo->SaveGame[x].resetFunc(); + } + } } } diff --git a/source/fceultra/cart.h b/source/fceultra/cart.h index e78c87e..0b58133 100644 --- a/source/fceultra/cart.h +++ b/source/fceultra/cart.h @@ -1,20 +1,46 @@ #ifndef CART_H #define CART_H -typedef struct { +#include + +struct CartInfo +{ // Set by mapper/board code: void (*Power)(void); void (*Reset)(void); void (*Close)(void); - uint8 *SaveGame[4]; // Pointers to memory to save/load. - uint32 SaveGameLen[4]; // How much memory to save/load. + + struct SaveGame_t + { + uint8 *bufptr; // Pointer to memory to save/load. + uint32 buflen; // How much memory to save/load. + void (*resetFunc)(void); // Callback to reset save game memory + + SaveGame_t(void) + : bufptr(nullptr), buflen(0), resetFunc(nullptr) + { + } + }; + std::vector SaveGame; + + void addSaveGameBuf( uint8* bufptrIn, uint32 buflenIn, void (*resetFuncIn)(void) = nullptr ) + { + SaveGame_t tmp; + + tmp.bufptr = bufptrIn; + tmp.buflen = buflenIn; + tmp.resetFunc = resetFuncIn; + + SaveGame.push_back( tmp ); + } // Set by iNES/UNIF loading code. int mirror; // As set in the header or chunk. - // iNES/UNIF specific. Intended - // to help support games like "Karnov" - // that are not really MMC3 but are - // set to mapper 4. + // iNES/UNIF specific. Intended + // to help support games like "Karnov" + // that are not really MMC3 but are + // set to mapper 4. + int mirrorAs2Bits; int battery; // Presence of an actual battery. int ines2; int submapper; // Submappers as defined by NES 2.0 @@ -26,7 +52,33 @@ typedef struct { uint32 CRC32; // Should be set by the iNES/UNIF loading // code, used by mapper/board code, maybe // other code in the future. -} CartInfo; + + CartInfo(void) + { + clear(); + } + + void clear(void) + { + Power = nullptr; + Reset = nullptr; + Close = nullptr; + + SaveGame.clear(); + + mirror = 0; + mirrorAs2Bits = 0; + battery = 0; + ines2 = 0; + submapper = 0; + wram_size = 0; + battery_wram_size = 0; + vram_size = 0; + battery_vram_size = 0; + memset( MD5, 0, sizeof(MD5)); + CRC32 = 0; + }; +}; extern CartInfo *currCartInfo; diff --git a/source/fceultra/cheat.cpp b/source/fceultra/cheat.cpp index ddfa824..ef95b56 100644 --- a/source/fceultra/cheat.cpp +++ b/source/fceultra/cheat.cpp @@ -58,8 +58,17 @@ void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p) CheatRPtrs[AB+x]=p-A; } +// Cheat change event callback. Called whenever cheat map is changed or recalculated. +static void (*cheatsChangeEventCB)(void*) = nullptr; +static void* cheatsChangeEventUserData = nullptr; -CHEATF_SUBFAST SubCheats[256] = { 0 }; +void FCEU_SetCheatChangeEventCallback( void (*func)(void*), void* userData ) +{ + cheatsChangeEventCB = func; + cheatsChangeEventUserData = userData; +} + +CHEATF_SUBFAST SubCheats[256]; uint32 numsubcheats = 0; int globalCheatDisabled = 0; int disableAutoLSCheats = 0; @@ -132,6 +141,11 @@ void RebuildSubCheats(void) } FrozenAddressCount = numsubcheats; //Update the frozen address list + // Notify the system of a change + if (cheatsChangeEventCB != nullptr) + { + cheatsChangeEventCB( cheatsChangeEventUserData ); + } } void FCEU_PowerCheats() @@ -157,22 +171,17 @@ static void CheatMemErr(void) FCEUD_PrintError("Error allocating memory for cheat data."); } -int AddCheatEntry(const char *name, uint32 addr, uint8 val, int compare, int status, int type) +void AddCheatEntry(const char *name, uint32 addr, uint8 val, int compare, int status, int type) { - struct CHEATF *temp; - if(!(temp = (struct CHEATF *)FCEU_dmalloc(sizeof(struct CHEATF)))) - { - CheatMemErr(); - return(0); - } + CHEATF *temp = new CHEATF(); - temp->name = strcpy((char*) FCEU_dmalloc(strlen(name) + 1), name); + temp->name = name; temp->addr = addr; temp->val = val; temp->status = status; temp->compare = compare; temp->type = type; - temp->next = 0; + temp->next = nullptr; if(cheats) { @@ -181,8 +190,6 @@ int AddCheatEntry(const char *name, uint32 addr, uint8 val, int compare, int sta } else cheats = cheatsl = temp; - - return (1); } /* The "override_existing" parameter is used only in cheat dialog import. @@ -305,14 +312,13 @@ void FCEU_SaveGameCheats(FILE* fp, int release) fputc(':', fp); if (next->compare >= 0) - fprintf(fp, "%04x:%02x:%02x:%s\n", next->addr, next->val, next->compare, next->name); + fprintf(fp, "%04x:%02x:%02x:%s\n", next->addr, next->val, next->compare, next->name.c_str()); else - fprintf(fp, "%04x:%02x:%s\n", next->addr, next->val, next->name); + fprintf(fp, "%04x:%02x:%s\n", next->addr, next->val, next->name.c_str()); - if (release) free(next->name); struct CHEATF *t = next; next = next->next; - if (release) free(t); + if (release) delete t; } } @@ -332,8 +338,7 @@ void FCEU_FlushGameCheats(FILE *override, int nosave) { struct CHEATF *last=next; next=next->next; - free(last->name); - free(last); + delete last; if(!next) break; } cheats=cheatsl=0; @@ -376,14 +381,15 @@ void FCEU_FlushGameCheats(FILE *override, int nosave) } -int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type) +int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type, int status, bool rebuild) { - - if(!AddCheatEntry(name, addr, val, compare, 1, type)) - return 0; + AddCheatEntry(name, addr, val, compare, status, type); savecheats = 1; - RebuildSubCheats(); + if (rebuild) + { + RebuildSubCheats(); + } return 1; } @@ -414,8 +420,7 @@ int FCEUI_DelCheat(uint32 which) else cheats=cheatsl=0; // No (more) cheats. } - free(cur->name); // Now that all references to this cheat are removed, - free(cur); // free the memory. + delete cur; // free the memory. break; } // *END REMOVE THIS CHEAT* @@ -450,18 +455,18 @@ void FCEU_ApplyPeriodicCheats(void) } -void FCEUI_ListCheats(int (*callb)(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data), void *data) +void FCEUI_ListCheats(int (*callb)(const char *name, uint32 a, uint8 v, int compare, int s, int type, void *data), void *data) { struct CHEATF *next=cheats; while(next) { - if(!callb(next->name,next->addr,next->val,next->compare,next->status,next->type,data)) break; + if(!callb(next->name.c_str(),next->addr,next->val,next->compare,next->status,next->type,data)) break; next=next->next; } } -int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *compare, int *s, int *type) +int FCEUI_GetCheat(uint32 which, std::string *name, uint32 *a, uint8 *v, int *compare, int *s, int *type) { struct CHEATF *next=cheats; uint32 x=0; @@ -599,7 +604,7 @@ int FCEUI_DecodePAR(const char *str, int *a, int *v, int *c, int *type) /* name can be NULL if the name isn't going to be changed. */ /* same goes for a, v, and s(except the values of each one must be <0) */ -int FCEUI_SetCheat(uint32 which, const char *name, int32 a, int32 v, int c, int s, int type) +int FCEUI_SetCheat(uint32 which, const std::string *name, int32 a, int32 v, int c, int s, int type) { struct CHEATF *next = cheats; uint32 x = 0; @@ -609,13 +614,7 @@ int FCEUI_SetCheat(uint32 which, const char *name, int32 a, int32 v, int c, int if(x == which) { if(name) - { - char *t; - if((t = (char *)realloc(next->name, strlen(name) + 1))) - strcpy(next->name = t, name); - else - return 0; - } + next->name = *name; if(a >= 0) next->addr = a; if(v >= 0) @@ -661,7 +660,7 @@ int FCEUI_ToggleCheat(uint32 which) int FCEUI_GlobalToggleCheat(int global_enabled) { - int _numsubcheats = numsubcheats; + unsigned int _numsubcheats = numsubcheats; globalCheatDisabled = !global_enabled; RebuildSubCheats(); return _numsubcheats != numsubcheats; @@ -910,11 +909,7 @@ int FCEU_DeleteAllCheats(void) while (cur) { next = cur->next; - if ( cur->name ) - { - free(cur->name); - } - free(cur); + delete cur; cur = next; } cheats = cheatsl = 0; diff --git a/source/fceultra/cheat.h b/source/fceultra/cheat.h index 82037fc..f90fc50 100644 --- a/source/fceultra/cheat.h +++ b/source/fceultra/cheat.h @@ -33,16 +33,24 @@ extern int disableAutoLSCheats; int FCEU_DisableAllCheats(void); int FCEU_DeleteAllCheats(void); -typedef struct { +void FCEU_SetCheatChangeEventCallback( void (*func)(void*) = nullptr, void* userData = nullptr ); + +struct CHEATF_SUBFAST +{ uint16 addr; uint8 val; int compare; readfunc PrevRead; -} CHEATF_SUBFAST; + + CHEATF_SUBFAST(void) + { + addr = 0; val = 0; compare = 0; PrevRead = nullptr; + } +}; struct CHEATF { struct CHEATF *next; - char *name; + std::string name; uint16 addr; uint8 val; int compare; /* -1 for no compare. */ diff --git a/source/fceultra/conddebug.cpp b/source/fceultra/conddebug.cpp index 3775d2b..90b3076 100644 --- a/source/fceultra/conddebug.cpp +++ b/source/fceultra/conddebug.cpp @@ -41,6 +41,10 @@ * Value -> 'R' | 'W' */ +#ifdef GEKKO + +#else + #include "types.h" #include "conddebug.h" #include "utils/memory.h" @@ -52,17 +56,17 @@ #include uint16 debugLastAddress = 0; // used by 'T' and 'R' conditions -uint8 debugLastOpcode; // used to evaluate 'W' condition +uint8 debugLastOpcode = 0; // used to evaluate 'W' condition // Next non-whitespace character in string -char next; +static char next = 0; -int ishex(char c) +static int ishex(char c) { return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } -void scan(const char** str) +static void scan(const char** str) { do { @@ -71,40 +75,37 @@ void scan(const char** str) } while (isspace(next)); } -// Frees a condition and all of it's sub conditions -void freeTree(Condition* c) -{ - if (c->lhs) freeTree(c->lhs); - if (c->rhs) freeTree(c->rhs); - - free(c); -} - // Generic function to handle all infix operators but the last one in the precedence hierarchy. : '(' E ')' -Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), int(*operators)(const char**)) +static Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), int(*operators)(const char**)) { Condition* t = nextPart(str); Condition* t1; Condition* mid; int op; + if (t == nullptr) + { + return nullptr; + } while ((op = operators(str))) { scan(str); t1 = nextPart(str); - if (t1 == 0) + if (t1 == nullptr) { - if(t) - freeTree(t); + delete t; return 0; } - mid = (Condition*)FCEU_dmalloc(sizeof(Condition)); - if (!mid) - return NULL; - memset(mid, 0, sizeof(Condition)); + mid = new Condition(); + if (mid == nullptr) + { + delete t; + delete t1; + return nullptr; + } mid->lhs = t; mid->rhs = t1; @@ -117,7 +118,7 @@ Condition* InfixOperator(const char** str, Condition(*nextPart(const char**)), i } // Generic handler for two-character operators -int TwoCharOperator(const char** str, char c1, char c2, int op) +static int TwoCharOperator(const char** str, char c1, char c2, int op) { if (next == c1 && **str == c2) { @@ -131,43 +132,43 @@ int TwoCharOperator(const char** str, char c1, char c2, int op) } // Determines if a character is a flag -int isFlag(char c) +static int isFlag(char c) { return c == 'N' || c == 'I' || c == 'C' || c == 'V' || c == 'Z' || c == 'B' || c == 'U' || c == 'D'; } // Determines if a character is a register -int isRegister(char c) +static int isRegister(char c) { return c == 'A' || c == 'X' || c == 'Y' || c == 'P' || c == 'S'; } // Determines if a character is for PC bank -int isPCBank(char c) +static int isPCBank(char c) { return c == 'K'; } // Determines if a character is for Data bank -int isDataBank(char c) +static int isDataBank(char c) { return c == 'T'; } // Determines if a character is for value read -int isValueRead(char c) +static int isValueRead(char c) { return c == 'R'; } // Determines if a character is for value write -int isValueWrite(char c) +static int isValueWrite(char c) { return c == 'W'; } // Reads a hexadecimal number from str -int getNumber(unsigned int* number, const char** str) +static int getNumber(unsigned int* number, const char** str) { // char buffer[5]; @@ -185,10 +186,10 @@ int getNumber(unsigned int* number, const char** str) return 1; } -Condition* Connect(const char** str); +static Condition* Connect(const char** str); // Handles the following part of the grammar: '(' E ')' -Condition* Parentheses(const char** str, Condition* c, char openPar, char closePar) +static Condition* Parentheses(const char** str, Condition* c, char openPar, char closePar) { if (next == openPar) { @@ -216,7 +217,7 @@ Condition* Parentheses(const char** str, Condition* c, char openPar, char closeP * Check for primitives * Flags, Registers, Numbers, Addresses and parentheses */ -Condition* Primitive(const char** str, Condition* c) +static Condition* Primitive(const char** str, Condition* c) { if (isFlag(next)) /* Flags */ { @@ -394,24 +395,22 @@ Condition* Primitive(const char** str, Condition* c) } /* Handle * and / operators */ -Condition* Term(const char** str) +static Condition* Term(const char** str) { Condition* t; Condition* t1; Condition* mid; - t = (Condition*)FCEU_dmalloc(sizeof(Condition)); + t = new Condition(); - if (!t) + if (t == nullptr) { return NULL; } - memset(t, 0, sizeof(Condition)); - if (!Primitive(str, t)) { - freeTree(t); + delete t; return 0; } @@ -421,22 +420,25 @@ Condition* Term(const char** str) scan(str); - if (!(t1 = (Condition*)FCEU_dmalloc(sizeof(Condition)))) - return NULL; - - memset(t1, 0, sizeof(Condition)); + if ((t1 = new Condition()) == nullptr) + { + delete t; + return nullptr; + } if (!Primitive(str, t1)) { - freeTree(t); - freeTree(t1); + delete t; + delete t1; return 0; } - if (!(mid = (Condition*)FCEU_dmalloc(sizeof(Condition)))) - return NULL; - - memset(mid, 0, sizeof(Condition)); + if ((mid = new Condition()) == nullptr) + { + delete t; + delete t1; + return nullptr; + } mid->lhs = t; mid->rhs = t1; @@ -449,7 +451,7 @@ Condition* Term(const char** str) } /* Check for + and - operators */ -int SumOperators(const char** str) +static int SumOperators(const char** str) { switch (next) { @@ -460,13 +462,13 @@ int SumOperators(const char** str) } /* Handle + and - operators */ -Condition* Sum(const char** str) +static Condition* Sum(const char** str) { return InfixOperator(str, Term, SumOperators); } /* Check for <=, =>, ==, !=, > and < operators */ -int CompareOperators(const char** str) +static int CompareOperators(const char** str) { int val = TwoCharOperator(str, '=', '=', OP_EQ); if (val) return val; @@ -490,13 +492,13 @@ int CompareOperators(const char** str) } /* Handle <=, =>, ==, !=, > and < operators */ -Condition* Compare(const char** str) +static Condition* Compare(const char** str) { return InfixOperator(str, Sum, CompareOperators); } /* Check for || or && operators */ -int ConnectOperators(const char** str) +static int ConnectOperators(const char** str) { int val = TwoCharOperator(str, '|', '|', OP_OR); if(val) return val; @@ -508,7 +510,7 @@ int ConnectOperators(const char** str) } /* Handle || and && operators */ -Condition* Connect(const char** str) +static Condition* Connect(const char** str) { return InfixOperator(str, Compare, ConnectOperators); } @@ -521,6 +523,12 @@ Condition* generateCondition(const char* str) scan(&str); c = Connect(&str); - if (!c || next != 0) return 0; + if (!c || next != 0) + { + if (c) delete c; + return 0; + } else return c; } + +#endif \ No newline at end of file diff --git a/source/fceultra/conddebug.h b/source/fceultra/conddebug.h index 24af528..168eb4a 100644 --- a/source/fceultra/conddebug.h +++ b/source/fceultra/conddebug.h @@ -61,9 +61,28 @@ struct Condition unsigned int type2; unsigned int value2; + + Condition(void) + { + op = 0; + lhs = rhs = nullptr; + type1 = value1 = 0; + type2 = value2 = 0; + }; + + ~Condition(void) + { + if (lhs) + { + delete lhs; + } + if (rhs) + { + delete rhs; + } + } }; -void freeTree(Condition* c); Condition* generateCondition(const char* str); #endif diff --git a/source/fceultra/config.cpp b/source/fceultra/config.cpp index 6615763..4c372d1 100644 --- a/source/fceultra/config.cpp +++ b/source/fceultra/config.cpp @@ -11,12 +11,14 @@ #include #include -static char *aboutString = 0; +static std::string aboutString; #ifndef FCEUX_BUILD_TIMESTAMP #define FCEUX_BUILD_TIMESTAMP __TIME__ " " __DATE__ #endif +//#pragma message( "Compiling using C++ Std: " __FCEU_STRINGIZE(__cplusplus) ) + // returns a string suitable for use in an aboutbox const char *FCEUI_GetAboutString(void) { @@ -26,7 +28,7 @@ const char *FCEUI_GetAboutString(void) "zeromus, feos\n" "\n" "Current Contributors:\n" - "CaH4e3, rainwarrior, owomomo, punkrockguy318\n" + "CaH4e3, rainwarrior, owomomo, punkrockguy318, Cluster\n" "\n" "Past Contributors:\n" "xhainingx, gocha, AnS, mjbudd77\n" @@ -55,14 +57,17 @@ const char *FCEUI_GetAboutString(void) "\n" FCEUX_BUILD_TIMESTAMP "\n"; - if (aboutString) return aboutString; + if (aboutString.size() > 0) return aboutString.c_str(); const char *compilerString = FCEUD_GetCompilerString(); - //allocate the string and concatenate the template with the compiler string - if (!(aboutString = (char*)FCEU_dmalloc(strlen(aboutTemplate) + strlen(compilerString) + 1))) - return NULL; + char cppVersion[128]; - sprintf(aboutString,"%s%s",aboutTemplate,compilerString); - return aboutString; + snprintf( cppVersion, sizeof(cppVersion), "\nCompiled using C++ Language Standard: %li\n", __cplusplus); + + aboutString.assign( aboutTemplate ); + aboutString.append( compilerString ); + aboutString.append( cppVersion ); + + return aboutString.c_str(); } diff --git a/source/fceultra/debug.cpp b/source/fceultra/debug.cpp index b02a151..7d0b526 100644 --- a/source/fceultra/debug.cpp +++ b/source/fceultra/debug.cpp @@ -1,3 +1,9 @@ +#ifdef GEKKO +int debug_loggingCD = 0; +int StackAddrBackup; +void IncrementInstructionsCounters() {} +#else + /// \file /// \brief Implements core debugging facilities #include "types.h" @@ -6,6 +12,7 @@ #include "cart.h" #include "ines.h" #include "debug.h" +#include "debugsymboltable.h" #include "driver.h" #include "ppu.h" @@ -18,10 +25,23 @@ unsigned int debuggerPageSize = 14; int vblankScanLines = 0; //Used to calculate scanlines 240-261 (vblank) int vblankPixel = 0; //Used to calculate the pixels in vblank -int offsetStringToInt(unsigned int type, const char* offsetBuffer) + +struct TraceInstructionCallback +{ + void (*func)(uint8 *opcode, int size) = nullptr; + TraceInstructionCallback* next = nullptr; +}; +static TraceInstructionCallback* traceInstructionCB = nullptr; + +int offsetStringToInt(unsigned int type, const char* offsetBuffer, bool *conversionOk) { int offset = -1; + if (conversionOk) + { + *conversionOk = false; + } + if (sscanf(offsetBuffer,"%7X",(unsigned int *)&offset) == EOF) { return -1; @@ -29,18 +49,41 @@ int offsetStringToInt(unsigned int type, const char* offsetBuffer) if (type & BT_P) { + if (conversionOk) + { + *conversionOk = (offset >= 0) && (offset < 0x4000); + } return offset & 0x3FFF; } else if (type & BT_S) { + if (conversionOk) + { + *conversionOk = (offset >= 0) && (offset < 0x100); + } return offset & 0x00FF; } else if (type & BT_R) { + if (conversionOk) + { + *conversionOk = (offset >= 0); + } return offset; } else // BT_C { + auto sym = debugSymbolTable.getSymbolAtAnyBank(offsetBuffer); + + if (sym) + { + if (conversionOk) + { + *conversionOk = true; + } + return sym->offset() & 0xFFFF; + } + int type = GIT_CART; if (GameInfo) @@ -48,21 +91,26 @@ int offsetStringToInt(unsigned int type, const char* offsetBuffer) type = GameInfo->type; } if (type == GIT_NSF) { //NSF Breakpoint keywords - if (strcmp(offsetBuffer,"LOAD") == 0) return (NSFHeader.LoadAddressLow | (NSFHeader.LoadAddressHigh<<8)); - if (strcmp(offsetBuffer,"INIT") == 0) return (NSFHeader.InitAddressLow | (NSFHeader.InitAddressHigh<<8)); - if (strcmp(offsetBuffer,"PLAY") == 0) return (NSFHeader.PlayAddressLow | (NSFHeader.PlayAddressHigh<<8)); + if (strcmp(offsetBuffer,"LOAD") == 0) offset = (NSFHeader.LoadAddressLow | (NSFHeader.LoadAddressHigh<<8)); + else if (strcmp(offsetBuffer,"INIT") == 0) offset = (NSFHeader.InitAddressLow | (NSFHeader.InitAddressHigh<<8)); + else if (strcmp(offsetBuffer,"PLAY") == 0) offset = (NSFHeader.PlayAddressLow | (NSFHeader.PlayAddressHigh<<8)); } else if (type == GIT_FDS) { //FDS Breakpoint keywords - if (strcmp(offsetBuffer,"NMI1") == 0) return (GetMem(0xDFF6) | (GetMem(0xDFF7)<<8)); - if (strcmp(offsetBuffer,"NMI2") == 0) return (GetMem(0xDFF8) | (GetMem(0xDFF9)<<8)); - if (strcmp(offsetBuffer,"NMI3") == 0) return (GetMem(0xDFFA) | (GetMem(0xDFFB)<<8)); - if (strcmp(offsetBuffer,"RST") == 0) return (GetMem(0xDFFC) | (GetMem(0xDFFD)<<8)); - if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) return (GetMem(0xDFFE) | (GetMem(0xDFFF)<<8)); + if (strcmp(offsetBuffer,"NMI1") == 0) offset = (GetMem(0xDFF6) | (GetMem(0xDFF7)<<8)); + else if (strcmp(offsetBuffer,"NMI2") == 0) offset = (GetMem(0xDFF8) | (GetMem(0xDFF9)<<8)); + else if (strcmp(offsetBuffer,"NMI3") == 0) offset = (GetMem(0xDFFA) | (GetMem(0xDFFB)<<8)); + else if (strcmp(offsetBuffer,"RST") == 0) offset = (GetMem(0xDFFC) | (GetMem(0xDFFD)<<8)); + else if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) offset = (GetMem(0xDFFE) | (GetMem(0xDFFF)<<8)); } else { //NES Breakpoint keywords - if ((strcmp(offsetBuffer,"NMI") == 0) || (strcmp(offsetBuffer,"VBL") == 0)) return (GetMem(0xFFFA) | (GetMem(0xFFFB)<<8)); - if (strcmp(offsetBuffer,"RST") == 0) return (GetMem(0xFFFC) | (GetMem(0xFFFD)<<8)); - if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) return (GetMem(0xFFFE) | (GetMem(0xFFFF)<<8)); + if ((strcmp(offsetBuffer,"NMI") == 0) || (strcmp(offsetBuffer,"VBL") == 0)) offset = (GetMem(0xFFFA) | (GetMem(0xFFFB)<<8)); + else if (strcmp(offsetBuffer,"RST") == 0) offset = (GetMem(0xFFFC) | (GetMem(0xFFFD)<<8)); + else if ((strcmp(offsetBuffer,"IRQ") == 0) || (strcmp(offsetBuffer,"BRK") == 0)) offset = (GetMem(0xFFFE) | (GetMem(0xFFFF)<<8)); + } + + if (conversionOk) + { + *conversionOk = (offset >= 0) && (offset < 0x10000); } } @@ -131,7 +179,7 @@ int checkCondition(const char* condition, int num) // Remove the old breakpoint condition before adding a new condition. if (watchpoint[num].cond) { - freeTree(watchpoint[num].cond); + delete watchpoint[num].cond; free(watchpoint[num].condText); watchpoint[num].cond = 0; watchpoint[num].condText = 0; @@ -145,8 +193,8 @@ int checkCondition(const char* condition, int num) { watchpoint[num].cond = c; watchpoint[num].condText = (char*)malloc(strlen(condition) + 1); - if (!watchpoint[num].condText) - return 0; + if (!watchpoint[num].condText) + return 0; strcpy(watchpoint[num].condText, condition); } else @@ -161,7 +209,7 @@ int checkCondition(const char* condition, int num) // Remove the old breakpoint condition if (watchpoint[num].cond) { - freeTree(watchpoint[num].cond); + delete watchpoint[num].cond; free(watchpoint[num].condText); watchpoint[num].cond = 0; watchpoint[num].condText = 0; @@ -258,7 +306,7 @@ int getBank(int offs) //Anything over FFFFF will kill it. //GetNesFileAddress doesn't work well with Unif files - int addr = GetNesFileAddress(offs)-16; + int addr = GetNesFileAddress(offs)-NES_HEADER_SIZE; if (GameInfo && GameInfo->type==GIT_NSF) return addr != -1 ? addr / 0x1000 : -1; @@ -270,12 +318,12 @@ int GetNesFileAddress(int A){ if((A < 0x6000) || (A > 0xFFFF))return -1; result = &Page[A>>11][A]-PRGptr[0]; if((result > (int)(PRGsize[0])) || (result < 0))return -1; - else return result+16; //16 bytes for the header remember + else return result+NES_HEADER_SIZE; //16 bytes for the header remember } int GetRomAddress(int A){ int i; - uint8 *p = GetNesPRGPointer(A-=16); + uint8 *p = GetNesPRGPointer(A-=NES_HEADER_SIZE); for(i = 16;i < 32;i++){ if((&Page[i][i<<11] <= p) && (&Page[i][(i+1)<<11] > p))break; } @@ -512,7 +560,7 @@ void LogCDData(uint8 *opcode, uint16 A, int size) case 4: memop = 0x20; break; } - if ((j = GetPRGAddress(A)) != -1) + if (((j = GetPRGAddress(A)) != -1) && (opcode[0] != 0x4C) && (opcode[0] != 0x6C)) { if (opwrite[opcode[0]] == 0) { @@ -527,7 +575,12 @@ void LogCDData(uint8 *opcode, uint16 A, int size) newDataHit = true; } } - else + // Unclear why the write destination's access types gets reset for FDS... + // See: + // - https://github.com/TASEmulators/fceux/commit/a4fa6225a04b5ab8d3dfca3fc9abd7190bceec85 + // - https://github.com/TASEmulators/fceux/commit/b10b6254c3d5c9519a85cb4382cdb22846d2e394 + // - https://github.com/TASEmulators/fceux/commit/67942accc72149ae028d58f36419b64ea8651db9?diff=unified&w=1 + else if(GameInfo && GameInfo->type == GIT_FDS) { if (cdloggerdata[j] & 1) { @@ -642,7 +695,8 @@ uint16 StackNextIgnorePC = 0xFFFF; ///fires a breakpoint static void breakpoint(uint8 *opcode, uint16 A, int size) { - int i, j, romAddrPC; + int i, romAddrPC; + unsigned int j; uint8 brk_type; uint8 stackop=0; uint8 stackopstartaddr=0,stackopendaddr=0; @@ -783,7 +837,7 @@ static void breakpoint(uint8 *opcode, uint16 A, int size) { { if (watchpoint[i].flags & BT_R) { - if ( (watchpoint[i].flags & WP_X) && (watchpoint[i].address == romAddrPC) ) + if ( (watchpoint[i].flags & WP_X) && (watchpoint[i].address == static_cast(romAddrPC)) ) { BREAKHIT(i); } @@ -814,7 +868,7 @@ static void breakpoint(uint8 *opcode, uint16 A, int size) { // TXS and TSX only deal with the pointer. if (watchpoint[i].flags & stackop) { - for (j = (stackopstartaddr|0x0100); j <= (stackopendaddr|0x0100); j++) + for (j = (stackopstartaddr|0x0100); j <= (static_cast(stackopendaddr)|0x0100); j++) { if (watchpoint[i].endaddress) { @@ -840,7 +894,7 @@ static void breakpoint(uint8 *opcode, uint16 A, int size) { // Pushes to stack if (watchpoint[i].flags & WP_W) { - for (j = (X.S|0x0100); j < (StackAddrBackup|0x0100); j++) + for (j = (X.S|0x0100); j < (static_cast(StackAddrBackup)|0x0100); j++) { if (watchpoint[i].endaddress) { @@ -858,7 +912,7 @@ static void breakpoint(uint8 *opcode, uint16 A, int size) { // Pulls from stack if (watchpoint[i].flags & WP_R) { - for (j = (StackAddrBackup|0x0100); j < (X.S|0x0100); j++) + for (j = (StackAddrBackup|0x0100); j < (static_cast(X.S)|0x0100); j++) { if (watchpoint[i].endaddress) { @@ -954,5 +1008,83 @@ void DebugCycle() if(debug_loggingCD) LogCDData(opcode, A, size); +#ifdef __WIN_DRIVER__ FCEUD_TraceInstruction(opcode, size); +#else + // Use callback pointer that can be null checked, this saves on the overhead + // of calling a function for every instruction when we aren't tracing. + if (traceInstructionCB != nullptr) + { + auto* cb = traceInstructionCB; + while (cb != nullptr) + { + cb->func(opcode, size); + cb = cb->next; + } + } +#endif } + +void* FCEUI_TraceInstructionRegister( void (*func)(uint8*,int) ) +{ + TraceInstructionCallback* cb = nullptr; + + if (traceInstructionCB == nullptr) + { + cb = traceInstructionCB = new TraceInstructionCallback(); + cb->func = func; + } + else + { + cb = traceInstructionCB; + + while (cb != nullptr) + { + if (cb->func == func) + { + // This function has already been registered, don't double add. + return nullptr; + } + if (cb->next == nullptr) + { + auto* newCB = new TraceInstructionCallback(); + newCB->func = func; + cb->next = newCB; + return newCB; + } + cb = cb->next; + } + } + return cb; +} + +bool FCEUI_TraceInstructionUnregisterHandle( void* handle ) +{ + TraceInstructionCallback* cb, *cb_prev, *cb_handle; + + cb_handle = static_cast(handle); + cb_prev = nullptr; + cb = traceInstructionCB; + + while (cb != nullptr) + { + if (cb == cb_handle) + { // Match we are going to remove from list and delete + if (cb_prev != nullptr) + { + cb_prev = cb->next; + } + else + { + traceInstructionCB = cb->next; + } + delete cb; + return true; + } + cb_prev = cb; + cb = cb->next; + } + return false; +} + +#endif diff --git a/source/fceultra/debug.h b/source/fceultra/debug.h index e1d8b57..f8805a3 100644 --- a/source/fceultra/debug.h +++ b/source/fceultra/debug.h @@ -172,7 +172,10 @@ DebuggerState &FCEUI_Debugger(); //#define WRITE_BREAKPOINT 16 //#define EXECUTE_BREAKPOINT 32 -int offsetStringToInt(unsigned int type, const char* offsetBuffer); +int offsetStringToInt(unsigned int type, const char* offsetBuffer, bool *conversionOk = nullptr); unsigned int NewBreak(const char* name, int start, int end, unsigned int type, const char* condition, unsigned int num, bool enable); +void* FCEUI_TraceInstructionRegister( void (*func)(uint8*,int) ); +bool FCEUI_TraceInstructionUnregisterHandle( void* handle ); + #endif diff --git a/source/fceultra/drawing.cpp b/source/fceultra/drawing.cpp index 7597c06..1129b3c 100644 --- a/source/fceultra/drawing.cpp +++ b/source/fceultra/drawing.cpp @@ -346,7 +346,7 @@ void FCEU_DrawRecordingStatus(uint8* XBuf) hasPlayRecIcon = true; } - if(FCEUI_EmulationPaused()) + if( EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER) ) drawstatus(XBuf-ClipSidesOffset,3,28,hasPlayRecIcon?-16:0); } } diff --git a/source/fceultra/driver.h b/source/fceultra/driver.h index e82f744..39ea3be 100644 --- a/source/fceultra/driver.h +++ b/source/fceultra/driver.h @@ -23,7 +23,7 @@ ArchiveScanRecord FCEUD_ScanArchive(std::string fname); const char *FCEUD_GetCompilerString(); //This makes me feel dirty for some reason. -void FCEU_printf(const char *format, ...); +void FCEU_printf( __FCEU_PRINTF_FORMAT const char *format, ...) __FCEU_PRINTF_ATTRIBUTE( 1, 2 ); #define FCEUI_printf FCEU_printf //Video interface @@ -192,12 +192,12 @@ void TaseditorManualFunction(void); int32 FCEUI_GetDesiredFPS(void); void FCEUI_SaveSnapshot(void); void FCEUI_SaveSnapshotAs(void); -void FCEU_DispMessage(const char *format, int disppos, ...); +void FCEU_DispMessage( __FCEU_PRINTF_FORMAT const char *format, int disppos, ...) __FCEU_PRINTF_ATTRIBUTE( 1, 3 ); #define FCEUI_DispMessage FCEU_DispMessage int FCEUI_DecodePAR(const char *code, int *a, int *v, int *c, int *type); int FCEUI_DecodeGG(const char *str, int *a, int *v, int *c); -int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type); +int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type, int status = 1, bool rebuild = true); int FCEUI_DelCheat(uint32 which); int FCEUI_ToggleCheat(uint32 which); int FCEUI_GlobalToggleCheat(int global_enable); @@ -207,10 +207,10 @@ void FCEUI_CheatSearchGetRange(uint32 first, uint32 last, int (*callb)(uint32 a, void FCEUI_CheatSearchGet(int (*callb)(uint32 a, uint8 last, uint8 current, void *data), void *data); void FCEUI_CheatSearchBegin(void); void FCEUI_CheatSearchEnd(int type, uint8 v1, uint8 v2); -void FCEUI_ListCheats(int (*callb)(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data), void *data); +void FCEUI_ListCheats(int (*callb)(const char *name, uint32 a, uint8 v, int compare, int s, int type, void *data), void *data); -int FCEUI_GetCheat(uint32 which, char **name, uint32 *a, uint8 *v, int *compare, int *s, int *type); -int FCEUI_SetCheat(uint32 which, const char *name, int32 a, int32 v, int compare,int s, int type); +int FCEUI_GetCheat(uint32 which, std::string *name, uint32 *a, uint8 *v, int *compare, int *s, int *type); +int FCEUI_SetCheat(uint32 which, const std::string *name, int32 a, int32 v, int compare,int s, int type); void FCEUI_CheatSearchShowExcluded(void); void FCEUI_CheatSearchSetCurrentAsOriginal(void); @@ -254,6 +254,8 @@ void FCEUI_VSUniToggleDIP(int w); uint8 FCEUI_VSUniGetDIPs(void); void FCEUI_VSUniSetDIP(int w, int state); void FCEUI_VSUniCoin(void); +void FCEUI_VSUniCoin2(void); +void FCEUI_VSUniService(void); void FCEUI_FDSInsert(void); //mbg merge 7/17/06 changed to void fn(void) to make it an EMUCMDFN //int FCEUI_FDSEject(void); @@ -271,6 +273,10 @@ void FCEUI_ClearEmulationFrameStepped(); void FCEUI_SetEmulationPaused(int val); ///toggles the paused bit (bit0) for EmulationPaused. caused FCEUD_DebugUpdate() to fire if the emulation pauses void FCEUI_ToggleEmulationPause(); +void FCEUI_PauseForDuration(int secs); +int FCEUI_PauseFramesRemaining(); +void FCEUI_SetNetPlayPause(bool value); +bool FCEUI_GetNetPlayPause(); //indicates whether input aids should be drawn (such as crosshairs, etc; usually in fullscreen mode) bool FCEUD_ShouldDrawInputAids(); @@ -361,7 +367,11 @@ bool FCEU_IsValidUI(EFCEUI ui); #ifdef __cplusplus extern "C" +{ +#endif + FILE *FCEUI_UTF8fopen_C(const char *n, const char *m); +#ifdef __cplusplus +} // extern C #endif -FILE *FCEUI_UTF8fopen_C(const char *n, const char *m); #endif //__DRIVER_H_ diff --git a/source/fceultra/fceu.cpp b/source/fceultra/fceu.cpp index cf5fcdb..fce0fe3 100644 --- a/source/fceultra/fceu.cpp +++ b/source/fceultra/fceu.cpp @@ -116,26 +116,31 @@ bool movieSubtitles = true; //Toggle for displaying movie subtitles bool DebuggerWasUpdated = false; //To prevent the debugger from updating things without being updated. bool AutoResumePlay = false; char romNameWhenClosingEmulator[2048] = {0}; +static unsigned int pauseTimer = 0; FCEUGI::FCEUGI() - : filename(0), - archiveFilename(0) { - //printf("%08x",opsize); // WTF?! } FCEUGI::~FCEUGI() { + if (name) + { + free(name); + name = nullptr; + } + if (filename) { free(filename); - filename = NULL; + filename = nullptr; } + if (archiveFilename) { free(archiveFilename); - archiveFilename = NULL; + archiveFilename = nullptr; } } @@ -181,7 +186,6 @@ static void FCEU_CloseGame(void) } #ifdef __WIN_DRIVER__ - extern char LoadedRomFName[2048]; if (storePreferences(mass_replace(LoadedRomFName, "|", ".").c_str())) FCEUD_PrintError("Couldn't store debugging data"); CDLoggerROMClosed(); @@ -193,7 +197,7 @@ static void FCEU_CloseGame(void) if (GameInfo->name) { free(GameInfo->name); - GameInfo->name = NULL; + GameInfo->name = nullptr; } if (GameInfo->type != GIT_NSF) { @@ -209,6 +213,8 @@ static void FCEU_CloseGame(void) GameInterface(GI_CLOSE); + FCEU_StateRecorderStop(); + FCEUI_StopMovie(); ResetExState(0, 0); @@ -221,15 +227,15 @@ static void FCEU_CloseGame(void) FCEU_CloseGenie(); delete GameInfo; - GameInfo = NULL; + GameInfo = nullptr; currFrameCounter = 0; //Reset flags for Undo/Redo/Auto Savestating //adelikat: TODO: maybe this stuff would be cleaner as a struct or class - lastSavestateMade[0] = 0; + lastSavestateMade.clear(); undoSS = false; redoSS = false; - lastLoadstateMade[0] = 0; + lastLoadstateMade.clear(); undoLS = false; redoLS = false; AutoSS = false; @@ -240,7 +246,7 @@ static void FCEU_CloseGame(void) uint64 timestampbase; -FCEUGI *GameInfo = NULL; +FCEUGI *GameInfo = nullptr; void (*GameInterface)(GI h); void (*GameStateRestore)(int version); @@ -271,9 +277,13 @@ int AutosaveFrequency = 256; // Number of frames between autosaves int EnableAutosave = 0; ///a wrapper for unzip.c -extern "C" FILE *FCEUI_UTF8fopen_C(const char *n, const char *m) { - return ::FCEUD_UTF8fopen(n, m); -} +extern "C" +{ + FILE *FCEUI_UTF8fopen_C(const char *n, const char *m) + { + return ::FCEUD_UTF8fopen(n, m); + } +} // extern C static DECLFW(BNull) { } @@ -301,8 +311,8 @@ void FlushGenieRW(void) { } free(AReadG); free(BWriteG); - AReadG = NULL; - BWriteG = NULL; + AReadG = nullptr; + BWriteG = nullptr; RWWrap = 0; } } @@ -368,7 +378,7 @@ static void AllocBuffers() { static void FreeBuffers() { FCEU_free(RAM); - RAM = NULL; + RAM = nullptr; } //------ @@ -395,14 +405,14 @@ void ResetGameLoaded(void) { if (GameInfo) FCEU_CloseGame(); EmulationPaused = 0; //mbg 5/8/08 - loading games while paused was bad news. maybe this fixes it GameStateRestore = 0; - PPU_hook = NULL; - GameHBIRQHook = NULL; - FFCEUX_PPURead = NULL; - FFCEUX_PPUWrite = NULL; + PPU_hook = nullptr; + GameHBIRQHook = nullptr; + FFCEUX_PPURead = nullptr; + FFCEUX_PPUWrite = nullptr; if (GameExpSound.Kill) GameExpSound.Kill(); memset(&GameExpSound, 0, sizeof(GameExpSound)); - MapIRQHook = NULL; + MapIRQHook = nullptr; MMC5Hack = 0; PEC586Hack = 0; QTAIHack = 0; @@ -421,7 +431,7 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen //---------- //attempt to open the files FCEUFILE *fp; - char fullname[2048]; // this name contains both archive name and ROM file name + std::string fullname; // this name contains both archive name and ROM file name int lastpal = PAL; int lastdendy = dendy; @@ -431,7 +441,7 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen // currently there's only one situation: // the user clicked cancel form the open from archive dialog int userCancel = 0; - fp = FCEU_fopen(name, 0, "rb", 0, -1, romextensions, &userCancel); + fp = FCEU_fopen(name, LoadedRomFNamePatchToUse[0] ? LoadedRomFNamePatchToUse : nullptr, "rb", 0, -1, romextensions, &userCancel); if (!fp) { @@ -443,16 +453,19 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen } else if (fp->archiveFilename != "") { - strcpy(fullname, fp->archiveFilename.c_str()); - strcat(fullname, "|"); - strcat(fullname, fp->filename.c_str()); - } else - strcpy(fullname, name); + fullname.assign(fp->archiveFilename.c_str()); + fullname.append("|"); + fullname.append(fp->filename.c_str()); + } + else + { + fullname.assign(name); + } // reset loaded game BEFORE it's loading. ResetGameLoaded(); //file opened ok. start loading. - FCEU_printf("Loading %s...\n\n", fullname); + FCEU_printf("Loading %s...\n\n", fullname.c_str()); GetFileBase(fp->filename.c_str()); //reset parameters so they're cleared just in case a format's loader doesn't know to do the clearing MasterRomInfoParams = TMasterRomInfoParams(); @@ -464,12 +477,12 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen FCEU_CloseGame(); GameInfo = new FCEUGI(); - memset( (void*)GameInfo, 0, sizeof(FCEUGI)); GameInfo->filename = strdup(fp->filename.c_str()); if (fp->archiveFilename != "") GameInfo->archiveFilename = strdup(fp->archiveFilename.c_str()); GameInfo->archiveCount = fp->archiveCount; + GameInfo->archiveIndex = fp->archiveIndex; GameInfo->soundchan = 0; GameInfo->soundrate = 0; @@ -484,16 +497,16 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen bool FCEUXLoad(const char *name, FCEUFILE * fp); int load_result; - load_result = iNESLoad(fullname, fp, OverwriteVidMode); + load_result = iNESLoad(fullname.c_str(), fp, OverwriteVidMode); if (load_result == LOADER_INVALID_FORMAT) { - load_result = NSFLoad(fullname, fp); + load_result = NSFLoad(fullname.c_str(), fp); if (load_result == LOADER_INVALID_FORMAT) { - load_result = UNIFLoad(fullname, fp); + load_result = UNIFLoad(fullname.c_str(), fp); if (load_result == LOADER_INVALID_FORMAT) { - load_result = FDSLoad(fullname, fp); + load_result = FDSLoad(fullname.c_str(), fp); } } } @@ -502,7 +515,6 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen #ifdef __WIN_DRIVER__ // ################################## Start of SP CODE ########################### - extern char LoadedRomFName[2048]; extern int loadDebugDataFailed; if ((loadDebugDataFailed = loadPreferences(mass_replace(LoadedRomFName, "|", ".").c_str()))) @@ -591,6 +603,12 @@ FCEUGI *FCEUI_LoadGameVirtual(const char *name, int OverwriteVidMode, bool silen } FCEU_fclose(fp); + + if ( FCEU_StateRecorderIsEnabled() ) + { + FCEU_StateRecorderStart(); + } + return GameInfo; } @@ -723,8 +741,12 @@ extern unsigned int frameAdvHoldTimer; ///Skip may be passed in, if FRAMESKIP is #defined, to cause this to emulate more than one frame void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int skip) { +#ifndef GEKKO + FCEU_PROFILE_FUNC(prof, "Emulate Single Frame"); +#endif //skip initiates frame skip if 1, or frame skip and sound skip if 2 - int r, ssize; + FCEU_MAYBE_UNUSED int r; + int ssize; JustFrameAdvanced = false; @@ -741,7 +763,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski { EmulationPaused = EMULATIONPAUSED_FA; } - if (frameAdvance_Delay_count < frameAdvanceDelayScaled) + if ( static_cast(frameAdvance_Delay_count) < frameAdvanceDelayScaled) { frameAdvance_Delay_count++; } @@ -753,6 +775,22 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski #endif } + if (EmulationPaused & EMULATIONPAUSED_TIMER) + { + if (pauseTimer > 0) + { + pauseTimer--; + } + else + { + EmulationPaused &= ~EMULATIONPAUSED_TIMER; + } + if (EmulationPaused & EMULATIONPAUSED_PAUSED) + { + EmulationPaused &= ~EMULATIONPAUSED_TIMER; + } + } + if (EmulationPaused & EMULATIONPAUSED_FA) { // the user is holding Frame Advance key @@ -776,7 +814,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski RefreshThrottleFPS(); } #endif - if (EmulationPaused & EMULATIONPAUSED_PAUSED) + if (EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER | EMULATIONPAUSED_NETPLAY) ) { // emulator is paused memcpy(XBuf, XBackBuf, 256*256); @@ -790,6 +828,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski AutoFire(); UpdateAutosave(); + FCEU_StateRecorderUpdate(); #ifdef _S9XLUA_H FCEU_LuaFrameBoundary(); @@ -1253,8 +1292,52 @@ void FCEUI_FrameAdvanceEnd(void) { } void FCEUI_FrameAdvance(void) { - frameAdvanceRequested = true; frameAdvance_Delay_count = 0; + frameAdvanceRequested = true; +} + +void FCEUI_PauseForDuration(int secs) +{ + int framesPerSec; + + // If already paused, do nothing + if (EmulationPaused & EMULATIONPAUSED_PAUSED) + { + return; + } + + if (PAL || dendy) + { + framesPerSec = 50; + } + else + { + framesPerSec = 60; + } + pauseTimer = framesPerSec * secs; + EmulationPaused |= EMULATIONPAUSED_TIMER; +} + +int FCEUI_PauseFramesRemaining(void) +{ + return (EmulationPaused & EMULATIONPAUSED_TIMER) ? pauseTimer : 0; +} + +bool FCEUI_GetNetPlayPause() +{ + return (EmulationPaused & EMULATIONPAUSED_NETPLAY) ? true : false; +} + +void FCEUI_SetNetPlayPause(bool value) +{ + if (value) + { + EmulationPaused |= EMULATIONPAUSED_NETPLAY; + } + else + { + EmulationPaused &= ~EMULATIONPAUSED_NETPLAY; + } } static int AutosaveCounter = 0; @@ -1271,7 +1354,7 @@ void UpdateAutosave(void) { FCEUSS_Save(f, false); AutoSS = true; //Flag that an auto-savestate was made free(f); - f = NULL; + f = nullptr; AutosaveStatus[AutosaveIndex] = 1; } } @@ -1285,7 +1368,7 @@ void FCEUI_RewindToLastAutosave(void) { f = strdup(FCEU_MakeFName(FCEUMKF_AUTOSTATE, AutosaveIndex, 0).c_str()); FCEUSS_Load(f); free(f); - f = NULL; + f = nullptr; //Set pointer to previous available slot if (AutosaveStatus[(AutosaveIndex + AutosaveQty - 1) % AutosaveQty] == 1) { diff --git a/source/fceultra/fceu.h b/source/fceultra/fceu.h index 7fec58c..e74d03c 100644 --- a/source/fceultra/fceu.h +++ b/source/fceultra/fceu.h @@ -139,10 +139,10 @@ extern FCEUS FSettings; bool CheckFileExists(const char* filename); //Receives a filename (fullpath) and checks to see if that file exists -void FCEU_PrintError(const char *format, ...); -void FCEU_printf(const char *format, ...); -void FCEU_DispMessage(const char *format, int disppos, ...); -void FCEU_DispMessageOnMovie(const char *format, ...); +void FCEU_PrintError( __FCEU_PRINTF_FORMAT const char *format, ...) __FCEU_PRINTF_ATTRIBUTE( 1, 2 ); +void FCEU_printf( __FCEU_PRINTF_FORMAT const char *format, ...) __FCEU_PRINTF_ATTRIBUTE( 1, 2 ); +void FCEU_DispMessage( __FCEU_PRINTF_FORMAT const char *format, int disppos, ...) __FCEU_PRINTF_ATTRIBUTE( 1, 3 ); +void FCEU_DispMessageOnMovie( __FCEU_PRINTF_FORMAT const char *format, ...) __FCEU_PRINTF_ATTRIBUTE( 1, 2 ); void FCEU_TogglePPU(); void SetNESDeemph_OldHacky(uint8 d, int force); @@ -181,8 +181,11 @@ extern uint8 vsdip; #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) -#define EMULATIONPAUSED_PAUSED 1 -#define EMULATIONPAUSED_FA 2 +#define EMULATIONPAUSED_PAUSED 0x01 +#define EMULATIONPAUSED_TIMER 0x02 +#define EMULATIONPAUSED_FA 0x04 +#define EMULATIONPAUSED_NETPLAY 0x08 #define FRAMEADVANCE_DELAY_DEFAULT 10 +#define NES_HEADER_SIZE 16 diff --git a/source/fceultra/fds.cpp b/source/fceultra/fds.cpp index c5d0a45..637a05d 100644 --- a/source/fceultra/fds.cpp +++ b/source/fceultra/fds.cpp @@ -29,6 +29,7 @@ #include "state.h" #include "file.h" #include "cart.h" +#include "ines.h" #include "netplay.h" #include "driver.h" #include "movie.h" @@ -578,21 +579,30 @@ void FDSSoundReset(void) { static DECLFW(FDSWrite) { switch (A) { case 0x4020: - X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0xFF00; IRQLatch |= V; break; case 0x4021: - X6502_IRQEnd(FCEU_IQEXT); IRQLatch &= 0xFF; IRQLatch |= V << 8; break; case 0x4022: - X6502_IRQEnd(FCEU_IQEXT); - IRQCount = IRQLatch; - IRQa = V & 3; + if (FDSRegs[3] & 1) { + IRQa = V & 0x03; + if (IRQa & IRQ_Enabled) { + IRQCount = IRQLatch; + } else { + X6502_IRQEnd(FCEU_IQEXT); + } + } + break; + case 0x4023: + if (!(V & 0x01)) { + IRQa &= ~IRQ_Enabled; + X6502_IRQEnd(FCEU_IQEXT); + X6502_IRQEnd(FCEU_IQEXT2); + } break; - case 0x4023: break; case 0x4024: if (mapperFDS_diskinsert && ~mapperFDS_control & 0x04) { @@ -859,8 +869,8 @@ int FDSLoad(const char *name, FCEUFILE *fp) { FDSSoundStateAdd(); for (x = 0; x < TotalSides; x++) { - char temp[5]; - sprintf(temp, "DDT%d", x); + char temp[8]; + snprintf(temp, sizeof(temp), "DDT%d", x); AddExState(diskdata[x], 65500, 0, temp); } diff --git a/source/fceultra/file.cpp b/source/fceultra/file.cpp index 34872fd..d3e882f 100644 --- a/source/fceultra/file.cpp +++ b/source/fceultra/file.cpp @@ -65,7 +65,7 @@ void ApplyIPS(FILE *ips, FCEUFILE* fp) if(!ips) return; - char* buf = (char*)FCEU_dmalloc(fp->size); + char* buf = (char*)FCEU_malloc(fp->size); memcpy(buf,fp->EnsureMemorystream()->buf(),fp->size); @@ -104,13 +104,7 @@ void ApplyIPS(FILE *ips, FCEUFILE* fp) if((offset+size)>(uint32)fp->size) { // Probably a little slow. - char *newbuf=(char *)realloc(buf,offset+size); - if(!newbuf) - { - free(buf); buf=NULL; - FCEU_printf(" Oops. IPS patch %d(type RLE) goes beyond end of file. Could not allocate memory.\n",count); - goto end; - } + char *newbuf=(char *)FCEU_realloc(buf,offset+size); buf=newbuf; memset(buf+fp->size,0,offset+size-fp->size); fp->size=offset+size; @@ -129,17 +123,15 @@ void ApplyIPS(FILE *ips, FCEUFILE* fp) if((offset+size)>(uint32)fp->size) { // Probably a little slow. - char *newbuf=(char *)realloc(buf,offset+size); - if(!newbuf) - { - free(buf); buf=NULL; - FCEU_printf(" Oops. IPS patch %d(type normal) goes beyond end of file. Could not allocate memory.\n",count); - goto end; - } + char *newbuf=(char *)FCEU_realloc(buf,offset+size); buf=newbuf; memset(buf+fp->size,0,offset+size-fp->size); + fp->size=offset+size; + } + if ( fread(buf+offset,1,size,ips) != static_cast(size) ) + { + FCEU_printf(" Warn IPS data read came up short!\n"); } - fread(buf+offset,1,size,ips); } count++; } @@ -152,7 +144,7 @@ end: std::string FCEU_MakeIpsFilename(FileBaseInfo fbi) { char ret[FILENAME_MAX] = ""; - sprintf(ret,"%s" PSS "%s%s.ips",fbi.filebasedirectory.c_str(),fbi.filebase.c_str(),fbi.ext.c_str()); + snprintf(ret, sizeof(ret), "%s" PSS "%s%s.ips",fbi.filebasedirectory.c_str(),fbi.filebase.c_str(),fbi.ext.c_str()); return ret; } @@ -322,9 +314,9 @@ FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, const char *mode, cha { uint32 magic; - magic = fp->fgetc(); - magic|=fp->fgetc()<<8; - magic|=fp->fgetc()<<16; + magic = (fp->fgetc() & 0x00ff); + magic|= (fp->fgetc() & 0x00ff) << 8; + magic|= (fp->fgetc() & 0x00ff) << 16; fp->fseek(0,SEEK_SET); if(magic==0x088b1f) { @@ -334,7 +326,7 @@ FCEUFILE * FCEU_fopen(const char *path, const char *ipsfn, const char *mode, cha if(gzfile) { delete fp; - int size; + size_t size; for(size=0; gzgetc(gzfile) != EOF; size++) {} EMUFILE_MEMORY* ms = new EMUFILE_MEMORY(size); gzseek(gzfile,0,SEEK_SET); @@ -453,13 +445,12 @@ int FCEU_fisarchive(FCEUFILE *fp) std::string GetMfn() //Retrieves the movie filename from curMovieFilename (for adding to savestate and auto-save files) { std::string movieFilenamePart; - extern char curMovieFilename[512]; - if(*curMovieFilename) - { + if (!curMovieFilename.empty()) + { char drv[PATH_MAX], dir[PATH_MAX], name[PATH_MAX], ext[PATH_MAX]; - splitpath(curMovieFilename,drv,dir,name,ext); + splitpath(curMovieFilename.c_str(),drv,dir,name,ext); movieFilenamePart = std::string(".") + name; - } + } return movieFilenamePart; } @@ -491,7 +482,7 @@ void FCEUI_SetDirOverride(int which, char *n) va_list ap; int ret; - if(!(*strp=(char*)FCEU_dmalloc(2048))) //mbg merge 7/17/06 cast to char* + if(!(*strp=(char*)FCEU_malloc(2048))) //mbg merge 7/17/06 cast to char* return(0); va_start(ap,fmt); ret=vsnprintf(*strp,2048,fmt,ap); @@ -623,9 +614,9 @@ std::string FCEU_MakeFName(int type, int id1, const char *cd1) struct stat fileInfo; do { if(odirs[FCEUIOD_MOVIES]) - sprintf(ret,"%s" PSS "%s-%d.fm2",odirs[FCEUIOD_MOVIES],FileBase, id1); + snprintf(ret, sizeof(ret), "%s" PSS "%s-%d.fm2",odirs[FCEUIOD_MOVIES],FileBase, id1); else - sprintf(ret,"%s" PSS "movies" PSS "%s-%d.fm2",BaseDirectory.c_str(),FileBase, id1); + snprintf(ret, sizeof(ret), "%s" PSS "movies" PSS "%s-%d.fm2",BaseDirectory.c_str(),FileBase, id1); id1++; } while (stat(ret, &fileInfo) == 0); break; @@ -649,19 +640,19 @@ std::string FCEU_MakeFName(int type, int id1, const char *cd1) if(odirs[FCEUIOD_STATES]) { - sprintf(ret,"%s" PSS "%s%s.fc%d",odirs[FCEUIOD_STATES],FileBase,mfn,id1); + snprintf(ret, sizeof(ret), "%s" PSS "%s%s.fc%d",odirs[FCEUIOD_STATES],FileBase,mfn,id1); } else { - sprintf(ret,"%s" PSS "fcs" PSS "%s%s.fc%d",BaseDirectory.c_str(),FileBase,mfn,id1); + snprintf(ret, sizeof(ret), "%s" PSS "fcs" PSS "%s%s.fc%d",BaseDirectory.c_str(),FileBase,mfn,id1); } if(stat(ret,&tmpstat)==-1) { if(odirs[FCEUIOD_STATES]) { - sprintf(ret,"%s" PSS "%s%s.fc%d",odirs[FCEUIOD_STATES],FileBase,mfn,id1); + snprintf(ret, sizeof(ret), "%s" PSS "%s%s.fc%d",odirs[FCEUIOD_STATES],FileBase,mfn,id1); } else { - sprintf(ret,"%s" PSS "fcs" PSS "%s%s.fc%d",BaseDirectory.c_str(),FileBase,mfn,id1); + snprintf(ret, sizeof(ret), "%s" PSS "fcs" PSS "%s%s.fc%d",BaseDirectory.c_str(),FileBase,mfn,id1); } } } @@ -670,46 +661,46 @@ std::string FCEU_MakeFName(int type, int id1, const char *cd1) { if(odirs[FCEUIOD_STATES]) { - sprintf(ret,"%s" PSS "%s-resume.fcs",odirs[FCEUIOD_STATES],FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "%s-resume.fcs",odirs[FCEUIOD_STATES],FileBase); } else { - sprintf(ret,"%s" PSS "fcs" PSS "%s-resume.fcs",BaseDirectory.c_str(),FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "fcs" PSS "%s-resume.fcs",BaseDirectory.c_str(),FileBase); } if(stat(ret,&tmpstat)==-1) { if(odirs[FCEUIOD_STATES]) { - sprintf(ret,"%s" PSS "%s-resume.fcs",odirs[FCEUIOD_STATES],FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "%s-resume.fcs",odirs[FCEUIOD_STATES],FileBase); } else { - sprintf(ret,"%s" PSS "fcs" PSS "%s-resume.fcs",BaseDirectory.c_str(),FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "fcs" PSS "%s-resume.fcs",BaseDirectory.c_str(),FileBase); } } } break; case FCEUMKF_SNAP: if(odirs[FCEUIOD_SNAPS]) - sprintf(ret,"%s" PSS "%s-%d.%s",odirs[FCEUIOD_SNAPS],FileBase,id1,cd1); + snprintf(ret, sizeof(ret), "%s" PSS "%s-%d.%s",odirs[FCEUIOD_SNAPS],FileBase,id1,cd1); else - sprintf(ret,"%s" PSS "snaps" PSS "%s-%d.%s",BaseDirectory.c_str(),FileBase,id1,cd1); + snprintf(ret, sizeof(ret), "%s" PSS "snaps" PSS "%s-%d.%s",BaseDirectory.c_str(),FileBase,id1,cd1); break; case FCEUMKF_FDS: if(odirs[FCEUIOD_NV]) - sprintf(ret,"%s" PSS "%s.fds",odirs[FCEUIOD_NV],FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "%s.fds",odirs[FCEUIOD_NV],FileBase); else - sprintf(ret,"%s" PSS "sav" PSS "%s.fds",BaseDirectory.c_str(),FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "sav" PSS "%s.fds",BaseDirectory.c_str(),FileBase); break; case FCEUMKF_SAV: if(odirs[FCEUIOD_NV]) - sprintf(ret,"%s" PSS "%s.%s",odirs[FCEUIOD_NV],FileBase,cd1); + snprintf(ret, sizeof(ret), "%s" PSS "%s.%s",odirs[FCEUIOD_NV],FileBase,cd1); else - sprintf(ret,"%s" PSS "sav" PSS "%s.%s",BaseDirectory.c_str(),FileBase,cd1); + snprintf(ret, sizeof(ret), "%s" PSS "sav" PSS "%s.%s",BaseDirectory.c_str(),FileBase,cd1); if(stat(ret,&tmpstat)==-1) { if(odirs[FCEUIOD_NV]) - sprintf(ret,"%s" PSS "%s.%s",odirs[FCEUIOD_NV],FileBase,cd1); + snprintf(ret, sizeof(ret), "%s" PSS "%s.%s",odirs[FCEUIOD_NV],FileBase,cd1); else - sprintf(ret,"%s" PSS "sav" PSS "%s.%s",BaseDirectory.c_str(),FileBase,cd1); + snprintf(ret, sizeof(ret), "%s" PSS "sav" PSS "%s.%s",BaseDirectory.c_str(),FileBase,cd1); } break; case FCEUMKF_AUTOSTATE: @@ -727,52 +718,52 @@ std::string FCEU_MakeFName(int type, int id1, const char *cd1) if(odirs[FCEUIOD_STATES]) { - sprintf(ret,"%s" PSS "%s%s-autosave%d.fcs",odirs[FCEUIOD_STATES],FileBase,mfn,id1); + snprintf(ret, sizeof(ret), "%s" PSS "%s%s-autosave%d.fcs",odirs[FCEUIOD_STATES],FileBase,mfn,id1); } else { - sprintf(ret,"%s" PSS "fcs" PSS "%s%s-autosave%d.fcs",BaseDirectory.c_str(),FileBase,mfn,id1); + snprintf(ret, sizeof(ret), "%s" PSS "fcs" PSS "%s%s-autosave%d.fcs",BaseDirectory.c_str(),FileBase,mfn,id1); } if(stat(ret,&tmpstat)==-1) { if(odirs[FCEUIOD_STATES]) { - sprintf(ret,"%s" PSS "%s%s-autosave%d.fcs",odirs[FCEUIOD_STATES],FileBase,mfn,id1); + snprintf(ret, sizeof(ret), "%s" PSS "%s%s-autosave%d.fcs",odirs[FCEUIOD_STATES],FileBase,mfn,id1); } else { - sprintf(ret,"%s" PSS "fcs" PSS "%s%s-autosave%d.fcs",BaseDirectory.c_str(),FileBase,mfn,id1); + snprintf(ret, sizeof(ret), "%s" PSS "fcs" PSS "%s%s-autosave%d.fcs",BaseDirectory.c_str(),FileBase,mfn,id1); } } break; case FCEUMKF_CHEAT: if(odirs[FCEUIOD_CHEATS]) - sprintf(ret,"%s" PSS "%s.cht",odirs[FCEUIOD_CHEATS],FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "%s.cht",odirs[FCEUIOD_CHEATS],FileBase); else - sprintf(ret,"%s" PSS "cheats" PSS "%s.cht",BaseDirectory.c_str(),FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "cheats" PSS "%s.cht",BaseDirectory.c_str(),FileBase); break; case FCEUMKF_IPS: strcpy(ret,FCEU_MakeIpsFilename(CurrentFileBase()).c_str()); break; - case FCEUMKF_GGROM:sprintf(ret,"%s" PSS "gg.rom",BaseDirectory.c_str());break; + case FCEUMKF_GGROM:snprintf(ret, sizeof(ret), "%s" PSS "gg.rom",BaseDirectory.c_str());break; case FCEUMKF_FDSROM: if(odirs[FCEUIOD_FDSROM]) - sprintf(ret,"%s" PSS "disksys.rom",odirs[FCEUIOD_FDSROM]); + snprintf(ret, sizeof(ret), "%s" PSS "disksys.rom",odirs[FCEUIOD_FDSROM]); else - sprintf(ret,"%s" PSS "disksys.rom",BaseDirectory.c_str()); + snprintf(ret, sizeof(ret), "%s" PSS "disksys.rom",BaseDirectory.c_str()); break; - case FCEUMKF_PALETTE:sprintf(ret,"%s" PSS "%s.pal",BaseDirectory.c_str(),FileBase);break; + case FCEUMKF_PALETTE:snprintf(ret, sizeof(ret), "%s" PSS "%s.pal",BaseDirectory.c_str(),FileBase);break; case FCEUMKF_MOVIEGLOB: //these globs use ??? because we can load multiple formats if(odirs[FCEUIOD_MOVIES]) - sprintf(ret,"%s" PSS "*.???",odirs[FCEUIOD_MOVIES]); + snprintf(ret, sizeof(ret), "%s" PSS "*.???",odirs[FCEUIOD_MOVIES]); else - sprintf(ret,"%s" PSS "movies" PSS "*.???",BaseDirectory.c_str()); + snprintf(ret, sizeof(ret), "%s" PSS "movies" PSS "*.???",BaseDirectory.c_str()); break; - case FCEUMKF_MOVIEGLOB2:sprintf(ret,"%s" PSS "*.???",BaseDirectory.c_str());break; + case FCEUMKF_MOVIEGLOB2:snprintf(ret, sizeof(ret), "%s" PSS "*.???",BaseDirectory.c_str());break; case FCEUMKF_STATEGLOB: if(odirs[FCEUIOD_STATES]) - sprintf(ret,"%s" PSS "%s*.fc?",odirs[FCEUIOD_STATES],FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "%s*.fc?",odirs[FCEUIOD_STATES],FileBase); else - sprintf(ret,"%s" PSS "fcs" PSS "%s*.fc?",BaseDirectory.c_str(),FileBase); + snprintf(ret, sizeof(ret), "%s" PSS "fcs" PSS "%s*.fc?",BaseDirectory.c_str(),FileBase); break; } diff --git a/source/fceultra/file.h b/source/fceultra/file.h index 84235f7..05ee2ea 100644 --- a/source/fceultra/file.h +++ b/source/fceultra/file.h @@ -35,7 +35,7 @@ struct FCEUFILE { int archiveIndex; //the size of the file - int size; + size_t size; //whether the file is contained in an archive bool isArchive() { return archiveCount > 0; } diff --git a/source/fceultra/filter.cpp b/source/fceultra/filter.cpp index d1e13ac..d0e7a62 100644 --- a/source/fceultra/filter.cpp +++ b/source/fceultra/filter.cpp @@ -37,7 +37,7 @@ void SexyFilter2(int32 *in, int32 count) while(count--) { int64 dropcurrent; - dropcurrent=((*in<<16)-acc)>>3; + dropcurrent=( int32(uint32(*in)<<16)-acc)>>3; acc+=dropcurrent; *in=acc>>16; @@ -167,10 +167,10 @@ int32 NeoFilterSound(int32 *in, int32 *out, uint32 inlen, int32 *leftover) void MakeFilters(int32 rate) { - const int32 *tabs[6]={C44100NTSC,C44100PAL,C48000NTSC,C48000PAL,C96000NTSC, - C96000PAL}; - const int32 *sq2tabs[6]={SQ2C44100NTSC,SQ2C44100PAL,SQ2C48000NTSC,SQ2C48000PAL, - SQ2C96000NTSC,SQ2C96000PAL}; + const int32 *tabs[8]={C44100NTSC,C44100PAL,C48000NTSC,C48000PAL,C96000NTSC, + C96000PAL, nullptr, nullptr}; + const int32 *sq2tabs[8]={SQ2C44100NTSC,SQ2C44100PAL,SQ2C48000NTSC,SQ2C48000PAL, + SQ2C96000NTSC,SQ2C96000PAL, nullptr, nullptr}; const int32 *tmp; int32 x; diff --git a/source/fceultra/git.h b/source/fceultra/git.h index 6eca186..c245eb6 100644 --- a/source/fceultra/git.h +++ b/source/fceultra/git.h @@ -16,12 +16,34 @@ enum EGIV GIV_USER = 2, //What was set by FCEUI_SetVidSys(). }; +enum EGIPPU +{ + GIPPU_USER = 0, + GIPPU_RP2C04_0001 = 1, + GIPPU_RP2C04_0002 = 2, + GIPPU_RP2C04_0003 = 3, + GIPPU_RP2C04_0004 = 4, + GIPPU_RC2C03B = 5, + GIPPU_RC2C05_01 = 6, + GIPPU_RC2C05_02 = 7, + GIPPU_RC2C05_03 = 8, + GIPPU_RC2C05_04 = 9, +}; + +enum EGIVS +{ + EGIVS_NORMAL = 0, + EGIVS_RBI = 1, // RBI Baseball protection + EGIVS_TKO = 2, // TKO Boxing protection + EGIVS_XEVIOUS = 3, // Super Xevious protection +}; + enum ESIS { SIS_NONE = 0, SIS_DATACH = 1, SIS_NWC = 2, - SIS_VSUNISYSTEM = 3, + SIS_VSUNISYSTEM = 3, // Is it used? SIS_NSF = 4, }; @@ -44,6 +66,8 @@ enum ESI SI_COUNT = SI_LCDCOMP_ZAPPER }; + + inline const char* ESI_Name(ESI esi) { static const char * const names[] = @@ -129,24 +153,29 @@ struct FCEUGI FCEUGI(); ~FCEUGI(); - uint8 *name; //Game name, UTF8 encoding - int mappernum; + uint8 *name = nullptr; //Game name, UTF8 encoding + int mappernum = 0; - EGIT type; - EGIV vidsys; //Current emulated video system; - ESI input[2]; //Desired input for emulated input ports 1 and 2; -1 for unknown desired input. - ESIFC inputfc; //Desired Famicom expansion port device. -1 for unknown desired input. - ESIS cspecial; //Special cart expansion: DIP switches, barcode reader, etc. + EGIT type = GIT_CART; + EGIV vidsys = GIV_USER; //Current emulated video system; + ESI input[2] = { SI_UNSET, SI_UNSET }; //Desired input for emulated input ports 1 and 2; -1 for unknown desired input. + ESIFC inputfc = SIFC_UNSET; //Desired Famicom expansion port device. -1 for unknown desired input. + ESIS cspecial = SIS_NONE; //Special cart expansion: DIP switches, barcode reader, etc. + EGIPPU vs_ppu = GIPPU_USER; //PPU type for Vs. System + EGIVS vs_type = EGIVS_NORMAL; //Vs. System type + uint8 vs_cswitch = SIS_NONE; // Switch first and second controllers for Vs. System MD5DATA MD5; //mbg 6/8/08 - ??? - int soundrate; //For Ogg Vorbis expansion sound wacky support. 0 for default. - int soundchan; //Number of sound channels. + int soundrate = 0; //For Ogg Vorbis expansion sound wacky support. 0 for default. + int soundchan = 0; //Number of sound channels. - char* filename; - char* archiveFilename; - int archiveCount; + char* filename = nullptr; + char* archiveFilename = nullptr; + int archiveCount = 0; // the number of files that were in the archive + int archiveIndex = -1; // the index of the file within the archive + bool loadedFromTmpFile = false; // Was loaded from temporary file, file most likely no longer exists }; #endif diff --git a/source/fceultra/ines-correct.h b/source/fceultra/ines-correct.h index 37dc700..a284445 100644 --- a/source/fceultra/ines-correct.h +++ b/source/fceultra/ines-correct.h @@ -129,9 +129,9 @@ {0xd4a76b07, 79, 0}, /* F-15 City Wars*/ {0x1eb4a920, 79, 1}, /* Double Strike */ {0x3e1271d5, 79, 1}, /* Tiles of Fate */ + {0x0da5e32e, 87, 1}, /* Urusei Yatsura */ {0xd2699893, 88, 0}, /* Dragon Spirit */ {0xbb7c5f7a, 89, 8}, /* Mito Koumon or something similar */ - {0x0da5e32e, 101, -1}, /* new Uruusey Yatsura */ {0x8eab381c, 113, 1}, /* Death Bots */ {0x6a03d3f3, 114, -1}, {0x0d98db53, 114, -1}, /* Pocahontas */ diff --git a/source/fceultra/ines.cpp b/source/fceultra/ines.cpp index 1577aad..361ee1c 100644 --- a/source/fceultra/ines.cpp +++ b/source/fceultra/ines.cpp @@ -37,6 +37,7 @@ #include "cheat.h" #include "vsuni.h" #include "driver.h" +#include "input.h" #include #include @@ -48,6 +49,7 @@ extern SFORMAT FCEUVSUNI_STATEINFO[]; uint8 *trainerpoo = NULL; uint8 *ROM = NULL; uint8 *VROM = NULL; +uint8 *MiscROM = NULL; uint8 *ExtraNTARAM = NULL; iNES_HEADER head; @@ -58,9 +60,12 @@ static CartInfo iNESCart; #endif uint8 Mirroring = 0; +uint8 MirroringAs2bits = 0; uint32 ROM_size = 0; uint32 VROM_size = 0; -char LoadedRomFName[2048]; //mbg merge 7/17/06 added +uint32 MiscROM_size = 0; +char LoadedRomFName[4096]; //mbg merge 7/17/06 added +char LoadedRomFNamePatchToUse[4096]; static int CHRRAMSize = -1; static int iNES_Init(int num); @@ -111,11 +116,11 @@ void iNESGI(GI h) { //bbit edited: removed static keyword if (iNESCart.Close) iNESCart.Close(); if (ROM) { - free(ROM); + FCEU_free(ROM); ROM = NULL; } if (VROM) { - free(VROM); + FCEU_free(VROM); VROM = NULL; } if (trainerpoo) { @@ -145,6 +150,9 @@ struct INPSEL { ESIFC inputfc; }; +/* +* Function to set input controllers based on CRC +*/ static void SetInput(void) { static struct INPSEL moo[] = { @@ -157,9 +165,9 @@ static void SetInput(void) { {0x48ca0ee1, SI_GAMEPAD, SI_GAMEPAD, SIFC_BWORLD }, // Barcode World {0x4318a2f8, SI_UNSET, SI_ZAPPER, SIFC_NONE }, // Barker Bill's Trick Shooting {0x6cca1c1f, SI_GAMEPAD, SI_GAMEPAD, SIFC_FTRAINERB }, // Dai Undoukai - {0x24598791, SI_UNSET, SI_ZAPPER, SIFC_NONE }, // Duck Hunt + {0x24598791, SI_GAMEPAD, SI_ZAPPER, SIFC_NONE }, // Duck Hunt {0xd5d6eac4, SI_UNSET, SI_UNSET, SIFC_SUBORKB }, // Edu (As) - {0xe9a7fe9e, SI_UNSET, SI_MOUSE, SIFC_NONE }, // Educational Computer 2000 + {0xe9a7fe9e, SI_UNSET, SI_MOUSE, SIFC_SUBORKB }, // Educational Computer 2000 {0x8f7b1669, SI_UNSET, SI_UNSET, SIFC_SUBORKB }, // FP BASIC 3.3 by maxzhou88 {0xf7606810, SI_UNSET, SI_UNSET, SIFC_FKB }, // Family BASIC 2.0A {0x895037bc, SI_UNSET, SI_UNSET, SIFC_FKB }, // Family BASIC 2.1a @@ -219,6 +227,7 @@ static void SetInput(void) { {0x67b126b9, SI_GAMEPAD, SI_GAMEPAD, SIFC_FAMINETSYS }, // Famicom Network System {0x00000000, SI_UNSET, SI_UNSET, SIFC_UNSET } }; + int x = 0; while (moo[x].input1 >= 0 || moo[x].input2 >= 0 || moo[x].inputfc >= 0) { @@ -232,6 +241,64 @@ static void SetInput(void) { } } +struct INPSEL_NES20 { + uint8 expansion_id; + ESI input1; + ESI input2; + ESIFC inputfc; +}; + +/* +* Function to set input controllers based on NES 2.0 header +*/ +extern int eoptions; +static void SetInputNes20(uint8 expansion) { + static struct INPSEL_NES20 moo[] = + { + {0x01, SI_GAMEPAD, SI_GAMEPAD, SIFC_UNSET }, // Standard NES/Famicom controllers + {0x02, SI_GAMEPAD, SI_GAMEPAD, SIFC_NONE }, // NES Four Score/Satellite with two additional standard controllers + {0x03, SI_GAMEPAD, SI_GAMEPAD, SIFC_4PLAYER }, // Famicom Four Players Adapter with two additional standard controllers using the "simple" protocol + {0x04, SI_GAMEPAD, SI_GAMEPAD, SIFC_NONE }, // Vs. System (1P via $4016) + {0x05, SI_GAMEPAD, SI_GAMEPAD, SIFC_NONE }, // Vs. System (1P via $4017) + {0x07, SI_ZAPPER, SI_NONE, SIFC_NONE }, // Vs. Zapper + {0x08, SI_UNSET, SI_ZAPPER, SIFC_NONE }, // Zapper ($4017) + {0x0A, SI_UNSET, SI_UNSET, SIFC_SHADOW }, // Bandai Hyper Shot Lightgun + {0x0B, SI_UNSET, SI_POWERPADA, SIFC_UNSET }, // Power Pad Side A + {0x0C, SI_UNSET, SI_POWERPADB, SIFC_UNSET }, // Power Pad Side B + {0x0D, SI_UNSET, SI_UNSET, SIFC_FTRAINERA }, // Family Trainer Side A + {0x0E, SI_UNSET, SI_UNSET, SIFC_FTRAINERB }, // Family Trainer Side B + {0x0F, SI_UNSET, SI_ARKANOID, SIFC_UNSET }, // Arkanoid Vaus Controller (NES) + {0x10, SI_UNSET, SI_UNSET, SIFC_ARKANOID }, // Arkanoid Vaus Controller (Famicom) + {0x12, SI_UNSET, SI_UNSET, SIFC_HYPERSHOT }, // Konami Hyper Shot Controller + {0x15, SI_UNSET, SI_UNSET, SIFC_MAHJONG }, // Jissen Mahjong Controller + {0x17, SI_UNSET, SI_UNSET, SIFC_OEKAKIDS }, // Oeka Kids Tablet + {0x18, SI_UNSET, SI_UNSET, SIFC_BWORLD }, // Sunsoft Barcode Battler + {0x1B, SI_UNSET, SI_UNSET, SIFC_TOPRIDER }, // Top Rider (Inflatable Bicycle) + {0x23, SI_UNSET, SI_UNSET, SIFC_FKB }, // Family BASIC Keyboard plus Famicom Data Recorder + {0x24, SI_UNSET, SI_UNSET, SIFC_PEC586KB }, // Dongda PEC-586 Keyboard + {0x26, SI_UNSET, SI_UNSET, SIFC_SUBORKB }, // Subor Keyboard + //{0x27, SI_UNSET, SI_MOUSE, SIFC_SUBORKB }, // Subor Keyboard plus mouse (3x8-bit protocol) + {0x28, SI_UNSET, SI_MOUSE, SIFC_SUBORKB }, // Subor Keyboard plus mouse (24-bit protocol) + {0x29, SI_UNSET, SI_SNES_MOUSE, SIFC_UNSET }, // SNES Mouse + {0, SI_UNSET, SI_UNSET, SIFC_UNSET } + }; + + int x = 0; + + if (expansion == 0x02) + eoptions |= 32768; // dirty hack to enable Four-Score + GameInfo->vs_cswitch = expansion == 0x05; + + while (moo[x].expansion_id) { + if (moo[x].expansion_id == expansion) { + GameInfo->input[0] = moo[x].input1; + GameInfo->input[1] = moo[x].input2; + GameInfo->inputfc = moo[x].inputfc; + break; } + x++; + } +} + #define INESB_INCOMPLETE 1 #define INESB_CORRUPT 2 #define INESB_HACKED 4 @@ -280,7 +347,7 @@ static const TMasterRomInfo sMasterRomInfo[] = { const TMasterRomInfo* MasterRomInfo; TMasterRomInfoParams MasterRomInfoParams; -static void CheckHInfo(void) { +static void CheckHInfo(uint64 partialmd5) { /* ROM images that have the battery-backed bit set in the header that really don't have battery-backed RAM is not that big of a problem, so I'll treat this differently by only listing games that should have battery-backed RAM. @@ -331,14 +398,9 @@ static void CheckHInfo(void) { #include "ines-correct.h" }; int32 tofix = 0, x, mask; - uint64 partialmd5 = 0; - - for (x = 0; x < 8; x++) - partialmd5 |= (uint64)iNESCart.MD5[15 - x] << (x * 8); - CheckBad(partialmd5); MasterRomInfo = NULL; - for (int i = 0; i < ARRAY_SIZE(sMasterRomInfo); i++) { + for (size_t i = 0; i < ARRAY_SIZE(sMasterRomInfo); i++) { const TMasterRomInfo& info = sMasterRomInfo[i]; if (info.md5lower != partialmd5) continue; @@ -347,7 +409,7 @@ static void CheckHInfo(void) { if (!info.params) break; std::vector toks = tokenize_str(info.params, ","); - for (int j = 0; j < (int)toks.size(); j++) { + for (size_t j = 0; j < toks.size(); j++) { std::vector parts = tokenize_str(toks[j], "="); MasterRomInfoParams[parts[0]] = parts[1]; } @@ -417,21 +479,33 @@ static void CheckHInfo(void) { if (MapperNo == 99) Mirroring = 2; - if (tofix) { - char gigastr[768]; - strcpy(gigastr, "The iNES header contains incorrect information. For now, the information will be corrected in RAM. "); + if (tofix) + { + char tmpStr[128]; + std::string gigastr; + gigastr.reserve(768); + gigastr.assign("The iNES header contains incorrect information. For now, the information will be corrected in RAM. "); if (tofix & 1) - sprintf(gigastr + strlen(gigastr), "The mapper number should be set to %d. ", MapperNo); - if (tofix & 2) { + { + snprintf(tmpStr, sizeof(tmpStr), "The mapper number should be set to %d. ", MapperNo); + gigastr.append(tmpStr); + } + if (tofix & 2) + { const char *mstr[3] = { "Horizontal", "Vertical", "Four-screen" }; - sprintf(gigastr + strlen(gigastr), "Mirroring should be set to \"%s\". ", mstr[Mirroring & 3]); + snprintf(tmpStr, sizeof(tmpStr), "Mirroring should be set to \"%s\". ", mstr[Mirroring & 3]); + gigastr.append(tmpStr); } if (tofix & 4) - strcat(gigastr, "The battery-backed bit should be set. "); + { + gigastr.append("The battery-backed bit should be set. "); + } if (tofix & 8) - strcat(gigastr, "This game should not have any CHR ROM. "); - strcat(gigastr, "\n"); - FCEU_printf("%s", gigastr); + { + gigastr.append("This game should not have any CHR ROM. "); + } + gigastr.append("\n"); + FCEU_printf("%s", gigastr.c_str()); } } @@ -477,7 +551,7 @@ BMAPPINGLocal bmap[] = { {"100-in-1", 15, Mapper15_Init}, {"BANDAI 24C02", 16, Mapper16_Init}, {"FFE Rev. B", 17, Mapper17_Init}, - {"JALECO SS880006", 18, Mapper18_Init}, // JF-NNX (EB89018-30007) boards + {"JALECO SS88006", 18, Mapper18_Init}, // JF-NNX (EB89018-30007) boards {"Namcot 106", 19, Mapper19_Init}, // {"", 20, Mapper20_Init}, {"Konami VRC2/VRC4 A", 21, Mapper21_Init}, @@ -487,7 +561,7 @@ BMAPPINGLocal bmap[] = { {"Konami VRC2/VRC4 D", 25, Mapper25_Init}, {"Konami VRC6 Rev. B", 26, Mapper26_Init}, {"CC-21 MI HUN CHE", 27, UNLCC21_Init}, // Former dupe for VRC2/VRC4 mapper, redefined with crc to mihunche boards - {"", 28, Mapper28_Init}, + {"ACTION 53", 28, Mapper28_Init}, {"RET-CUFROM", 29, Mapper29_Init}, {"UNROM 512", 30, UNROM512_Init}, {"infiniteneslives-NSF", 31, Mapper31_Init}, @@ -560,7 +634,7 @@ BMAPPINGLocal bmap[] = { // {"", 98, Mapper98_Init}, {"VS Uni/Dual- system", 99, Mapper99_Init}, // {"", 100, Mapper100_Init}, - {"", 101, Mapper101_Init}, +// {"", 101, Mapper101_Init}, // Deprecated, dupe // {"", 102, Mapper102_Init}, {"FDS DOKIDOKI FULL", 103, Mapper103_Init}, // {"", 104, Mapper104_Init}, @@ -577,7 +651,7 @@ BMAPPINGLocal bmap[] = { {"MMC3 PIRATE A", 115, Mapper115_Init}, {"MMC1/MMC3/VRC PIRATE",116, UNLSL12_Init}, {"FUTURE MEDIA BOARD", 117, Mapper117_Init}, - {"TSKROM", 118, TKSROM_Init}, + {"TKSROM", 118, TKSROM_Init}, {"NES-TQROM", 119, Mapper119_Init}, {"FDS TOBIDASE", 120, Mapper120_Init}, {"MMC3 PIRATE PROT. A", 121, Mapper121_Init}, @@ -585,7 +659,7 @@ BMAPPINGLocal bmap[] = { {"MMC3 PIRATE H2288", 123, UNLH2288_Init}, // {"", 124, Mapper124_Init}, {"FDS LH32", 125, LH32_Init}, - {"PowerJoy 84-in-1", 126, Mapper126_Init}, +// {"", 126, Mapper126_Init}, // {"", 127, Mapper127_Init}, // {"", 128, Mapper128_Init}, // {"", 129, Mapper129_Init}, @@ -610,11 +684,11 @@ BMAPPINGLocal bmap[] = { {"SA0037", 148, SA0037_Init}, {"SA0036", 149, SA0036_Init}, {"S74LS374N", 150, S74LS374N_Init}, - {"", 151, Mapper151_Init}, - {"", 152, Mapper152_Init}, +// {"", 151, Mapper151_Init}, // Deprecated, dupe + {"BA SARA DISCRETE", 152, Mapper152_Init}, {"BANDAI SRAM", 153, Mapper153_Init}, // Bandai board 16 with SRAM instead of EEPROM {"", 154, Mapper154_Init}, - {"", 155, Mapper155_Init}, + {"MMC1A", 155, Mapper155_Init}, // No WRAM disable {"", 156, Mapper156_Init}, {"BANDAI BARCODE", 157, Mapper157_Init}, // {"", 158, Mapper158_Init}, @@ -633,21 +707,21 @@ BMAPPINGLocal bmap[] = { {"", 171, Mapper171_Init}, {"", 172, Mapper172_Init}, {"", 173, Mapper173_Init}, -// {"", 174, Mapper174_Init}, + {"NTDec 5-in-1", 174, Mapper174_Init}, {"", 175, Mapper175_Init}, {"BMCFK23C", 176, BMCFK23C_Init}, // zero 26-may-2012 - well, i have some WXN junk games that use 176 for instance ????. i dont know what game uses this BMCFK23C as mapper 176. we'll have to make a note when we find it. {"", 177, Mapper177_Init}, {"", 178, Mapper178_Init}, // {"", 179, Mapper179_Init}, - {"", 180, Mapper180_Init}, + {"UNROM+74HC08", 180, Mapper180_Init}, // Crazy Climber {"", 181, Mapper181_Init}, // {"", 182, Mapper182_Init}, // Deprecated, dupe {"", 183, Mapper183_Init}, - {"", 184, Mapper184_Init}, - {"", 185, Mapper185_Init}, - {"", 186, Mapper186_Init}, + {"SUNSOFT-K", 184, Mapper184_Init}, // Sunsoft-1 mapper + {"CNROM+SECURITY", 185, Mapper185_Init}, + {"STUDY BOX", 186, Mapper186_Init}, {"", 187, Mapper187_Init}, - {"", 188, Mapper188_Init}, + {"KARAOKE STUDIO", 188, Mapper188_Init}, {"", 189, Mapper189_Init}, {"", 190, Mapper190_Init}, {"", 191, Mapper191_Init}, @@ -664,7 +738,7 @@ BMAPPINGLocal bmap[] = { {"", 202, Mapper202_Init}, {"", 203, Mapper203_Init}, {"", 204, Mapper204_Init}, - {"", 205, Mapper205_Init}, + {"JC-016-2", 205, Mapper205_Init}, {"NAMCOT 108 Rev. C", 206, Mapper206_Init}, // Deprecated, Used to be "DEIROM" whatever it means, but actually simple version of MMC3 {"TAITO X1-005 Rev. B", 207, Mapper207_Init}, {"", 208, Mapper208_Init}, @@ -677,7 +751,7 @@ BMAPPINGLocal bmap[] = { {"", 215, UNL8237_Init}, {"", 216, Mapper216_Init}, {"", 217, Mapper217_Init}, // Redefined to a new Discrete BMC mapper - {"Magic Floor", 218, Mapper218_Init}, + {"", 218, Mapper218_Init}, {"UNLA9746", 219, UNLA9746_Init}, {"Debug Mapper", 220, QTAi_Init}, {"UNLN625092", 221, UNLN625092_Init}, @@ -687,8 +761,8 @@ BMAPPINGLocal bmap[] = { {"", 225, Mapper225_Init}, {"BMC 22+20-in-1", 226, Mapper226_Init}, {"", 227, Mapper227_Init}, - {"", 228, Mapper228_Init}, - {"", 229, Mapper229_Init}, + {"ACTIVE ENTERPRISES", 228, Mapper228_Init}, + {"BMC 31-in-1", 229, Mapper229_Init}, {"BMC Contra+22-in-1", 230, Mapper230_Init}, {"", 231, Mapper231_Init}, {"BMC QUATTRO", 232, Mapper232_Init}, @@ -714,7 +788,7 @@ BMAPPINGLocal bmap[] = { {"SAN GUO ZHI PIRATE", 252, Mapper252_Init}, {"DRAGON BALL PIRATE", 253, Mapper253_Init}, {"", 254, Mapper254_Init}, -// {"", 255, Mapper255_Init}, // No good dumps for this mapper + {"", 255, Mapper255_Init}, // dupe of 225 //-------- Mappers 256-511 is the Supplementary Multilingual Plane ---------- //-------- Mappers 512-767 is the Supplementary Ideographic Plane ----------- @@ -731,9 +805,17 @@ BMAPPINGLocal bmap[] = { {"F-15 MMC3 Based", 259, BMCF15_Init}, {"HP10xx/H20xx Boards", 260, BMCHPxx_Init}, {"810544-CA-1", 261, BMC810544CA1_Init}, - {"SMD132/SMD133", 268, SMD132_SMD133_Init}, + {"AA6023/AA6023B", 268, AA6023_Init}, + {"OK-411", 361, GN45_Init}, + {"GN-45", 366, GN45_Init}, + {"COOLGIRL", 342, COOLGIRL_Init }, + {"FAM250/81-01-39-C/SCHI-24", 354, Mapper354_Init }, {"Impact Soft MMC3 Flash Board", 406, Mapper406_Init }, + {"Super Russian Roulette", 413, Mapper413_Init }, + {"INX_007T_V01", 470, INX_007T_Init }, + {"Haratyler HP/MP", 451, Mapper451_Init }, + {"Impact Soft IM1", 471, Mapper471_Init }, {"KONAMI QTAi Board", 547, QTAi_Init }, @@ -741,14 +823,20 @@ BMAPPINGLocal bmap[] = { }; int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { + int result; struct md5_context md5; + uint64 partialmd5 = 0; + const char* mappername = "Not Listed"; + size_t filesize = FCEU_fgetsize(fp); if (FCEU_fread(&head, 1, 16, fp) != 16 || memcmp(&head, "NES\x1A", 4)) return LOADER_INVALID_FORMAT; - + // Remove header size from filesize + filesize -= 16; + head.cleanup(); - memset(&iNESCart, 0, sizeof(iNESCart)); + iNESCart.clear(); iNES2 = ((head.ROM_type2 & 0x0C) == 0x08); if(iNES2) @@ -770,34 +858,39 @@ int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { } else Mirroring = (head.ROM_type & 1); - int not_round_size; + MirroringAs2bits = head.ROM_type & 1; + if (head.ROM_type & 8) MirroringAs2bits |= 2; + + int not_round_size = 0; + int rom_size_bytes = 0; + int vrom_size_bytes = 0; + if (!iNES2) { - not_round_size = head.ROM_size; + not_round_size = head.ROM_size << 14; } else { if ((head.Upper_ROM_VROM_size & 0x0F) != 0x0F) // simple notation - not_round_size = head.ROM_size | ((head.Upper_ROM_VROM_size & 0x0F) << 8); + not_round_size = (head.ROM_size | ((head.Upper_ROM_VROM_size & 0x0F) << 8)) << 14; else // exponent-multiplier notation - not_round_size = ((1 << (head.ROM_size >> 2)) * ((head.ROM_size & 0b11) * 2 + 1)) >> 14; + not_round_size = ((1 << (head.ROM_size >> 2)) * ((head.ROM_size & 0b11) * 2 + 1)); } - + if (!head.ROM_size && !iNES2) - ROM_size = 256; + rom_size_bytes = 256 << 14; else - ROM_size = uppow2(not_round_size); + rom_size_bytes = uppow2(not_round_size); - VROM_size = uppow2(head.VROM_size | (iNES2?((head.Upper_ROM_VROM_size & 0xF0)<<4):0)); if (!iNES2) { - VROM_size = uppow2(head.VROM_size); + vrom_size_bytes = uppow2(head.VROM_size << 13); } else { if ((head.Upper_ROM_VROM_size & 0xF0) != 0xF0) // simple notation - VROM_size = uppow2(head.VROM_size | ((head.Upper_ROM_VROM_size & 0xF0) << 4)); + vrom_size_bytes = uppow2((head.VROM_size | ((head.Upper_ROM_VROM_size & 0xF0) << 4)) << 13); else - VROM_size = ((1 << (head.VROM_size >> 2)) * ((head.VROM_size & 0b11) * 2 + 1)) >> 13; + vrom_size_bytes = ((1 << (head.VROM_size >> 2)) * ((head.VROM_size & 0b11) * 2 + 1)); } int round = true; @@ -813,52 +906,111 @@ int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { } } - if ((ROM = (uint8*)FCEU_malloc(ROM_size << 14)) == NULL) - return 0; - memset(ROM, 0xFF, ROM_size << 14); + ROM_size = rom_size_bytes >> 14; + VROM_size = vrom_size_bytes >> 13; - if (VROM_size) { - if ((VROM = (uint8*)FCEU_malloc(VROM_size << 13)) == NULL) { - free(ROM); - ROM = NULL; - FCEU_PrintError("Unable to allocate memory."); - return LOADER_HANDLED_ERROR; + ROM = (uint8*)FCEU_malloc(rom_size_bytes); + memset(ROM, 0xFF, rom_size_bytes); + + if (vrom_size_bytes) { + VROM = (uint8*)FCEU_malloc(vrom_size_bytes); + memset(VROM, 0xFF, vrom_size_bytes); + } + + // Set Vs. System flag if need + if (!iNES2) { + GameInfo->type = !(head.ROM_type2 & 1) ? GIT_CART : GIT_VSUNI; + } + else { + switch (!(head.ROM_type2 & 2) ? (head.ROM_type2 & 3) : (head.VS_hardware & 0xF)) { + case 0: + GameInfo->type = GIT_CART; + break; + case 1: + GameInfo->type = GIT_VSUNI; + break; + default: + FCEU_PrintError("Game type is not supported at all."); + goto init_error; + } + } + + // Set Vs. System PPU type if need + if (GameInfo->type == GIT_VSUNI && !(head.ROM_type2 & 2)) { + switch (head.VS_hardware & 0xF) { + case 0x0: GameInfo->vs_ppu = GIPPU_RC2C03B; break; + //case 0x1: GameInfo->vs_ppu = GIPPU_RPC2C03C; break; + case 0x2: GameInfo->vs_ppu = GIPPU_RP2C04_0001; break; + case 0x3: GameInfo->vs_ppu = GIPPU_RP2C04_0002; break; + case 0x4: GameInfo->vs_ppu = GIPPU_RP2C04_0003; break; + case 0x5: GameInfo->vs_ppu = GIPPU_RP2C04_0004; break; + case 0x6: GameInfo->vs_ppu = GIPPU_RC2C03B; break; + //case 0x7: GameInfo->ppu = GIPPU_RPC2C03C; break; + case 0x8: GameInfo->vs_ppu = GIPPU_RC2C05_01; break; + case 0x9: GameInfo->vs_ppu = GIPPU_RC2C05_02; break; + case 0xA: GameInfo->vs_ppu = GIPPU_RC2C05_03; break; + case 0xB: GameInfo->vs_ppu = GIPPU_RC2C05_04; break; + //case 0xC: GameInfo->ppu = GIPPU_RPC2C05_05; break; + default: + FCEU_PrintError("Vs. System PPU type is not supported at all."); + goto init_error; + } + + switch (head.VS_hardware >> 4) { + case 0x0: GameInfo->vs_type = EGIVS_NORMAL; break; + case 0x1: GameInfo->vs_type = EGIVS_RBI; break; + case 0x2: GameInfo->vs_type = EGIVS_TKO; break; + case 0x3: GameInfo->vs_type = EGIVS_XEVIOUS; break; + default: + FCEU_PrintError("Vs. System type is not supported at all."); + goto init_error; } - memset(VROM, 0xFF, VROM_size << 13); } if (head.ROM_type & 4) { /* Trainer */ trainerpoo = (uint8*)FCEU_gmalloc(512); FCEU_fread(trainerpoo, 512, 1, fp); + filesize -= 512; } ResetCartMapping(); ResetExState(0, 0); - SetupCartPRGMapping(0, ROM, ROM_size << 14, 0); + SetupCartPRGMapping(0, ROM, rom_size_bytes, 0); - FCEU_fread(ROM, 0x4000, (round) ? ROM_size : not_round_size, fp); + FCEU_fread(ROM, 1, (round) ? rom_size_bytes : not_round_size, fp); - if (VROM_size) - FCEU_fread(VROM, 0x2000, VROM_size, fp); + if (vrom_size_bytes) + FCEU_fread(VROM, 1, vrom_size_bytes, fp); + + // Misc ROMS + if ((head.misc_roms & 0x03) && !(head.ROM_type & 4)) { + MiscROM_size = filesize - rom_size_bytes - vrom_size_bytes; + MiscROM = (uint8 *)FCEU_malloc(MiscROM_size); + memset(MiscROM, 0xFF, MiscROM_size); + FCEU_fread(MiscROM, 1, MiscROM_size, fp); + FCEU_printf(" Misc ROM size : %d\n", MiscROM_size); + } - md5_starts(&md5); - md5_update(&md5, ROM, ROM_size << 14); + md5_starts(&md5); + md5_update(&md5, ROM, rom_size_bytes); - iNESGameCRC32 = CalcCRC32(0, ROM, ROM_size << 14); + iNESGameCRC32 = CalcCRC32(0, ROM, rom_size_bytes); - if (VROM_size) { - iNESGameCRC32 = CalcCRC32(iNESGameCRC32, VROM, VROM_size << 13); - md5_update(&md5, VROM, VROM_size << 13); + if (vrom_size_bytes) { + iNESGameCRC32 = CalcCRC32(iNESGameCRC32, VROM, vrom_size_bytes); + md5_update(&md5, VROM, vrom_size_bytes); } md5_finish(&md5, iNESCart.MD5); memcpy(&GameInfo->MD5, &iNESCart.MD5, sizeof(iNESCart.MD5)); + for (int x = 0; x < 8; x++) + partialmd5 |= (uint64)iNESCart.MD5[7 - x] << (x * 8); iNESCart.CRC32 = iNESGameCRC32; - FCEU_printf(" PRG ROM: %d x 16KiB = %d KiB\n", round ? ROM_size : not_round_size, (round ? ROM_size : not_round_size) * 16); - FCEU_printf(" CHR ROM: %d x 8KiB = %d KiB\n", VROM_size, VROM_size * 8); - FCEU_printf(" ROM CRC32: 0x%08lx\n", iNESGameCRC32); + FCEU_printf(" PRG ROM: %d x 16KiB = %d KiB\n", (round ? rom_size_bytes : not_round_size) >> 14, ((round ? rom_size_bytes : not_round_size) >> 14) * 16); + FCEU_printf(" CHR ROM: %d x 8KiB = %d KiB\n", (vrom_size_bytes >> 13), (vrom_size_bytes >> 13) * 8); + FCEU_printf(" ROM CRC32: 0x%08x\n", iNESGameCRC32); { int x; FCEU_printf(" ROM MD5: 0x"); @@ -867,9 +1019,7 @@ int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { FCEU_printf("\n"); } - const char* mappername = "Not Listed"; - - for (int mappertest = 0; mappertest < (sizeof bmap / sizeof bmap[0]) - 1; mappertest++) { + for (size_t mappertest = 0; mappertest < (sizeof bmap / sizeof bmap[0]) - 1; mappertest++) { if (bmap[mappertest].number == MapperNo) { mappername = bmap[mappertest].name; break; @@ -892,25 +1042,21 @@ int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { FCEU_printf(" WRAM backed by battery: %d KiB\n", iNESCart.battery_wram_size / 1024); FCEU_printf(" VRAM backed by battery: %d KiB\n", iNESCart.battery_vram_size / 1024); } + if (head.misc_roms & 0x03) FCEU_printf(" Misc ROM: %d KiB\n", MiscROM_size / 1024); } SetInput(); - CheckHInfo(); - { - int x; - uint64 partialmd5 = 0; + // Input can be overriden by NES 2.0 header + if (iNES2) SetInputNes20(head.expansion); + CheckHInfo(partialmd5); + FCEU_VSUniCheck(partialmd5, &MapperNo, &Mirroring); + CheckBad(partialmd5); - for (x = 0; x < 8; x++) { - partialmd5 |= (uint64)iNESCart.MD5[7 - x] << (x * 8); - } - - FCEU_VSUniCheck(partialmd5, &MapperNo, &Mirroring); - } /* Must remain here because above functions might change value of VROM_size and free(VROM). */ - if (VROM_size) - SetupCartCHRMapping(0, VROM, VROM_size * 0x2000, 0); + if (vrom_size_bytes) + SetupCartCHRMapping(0, VROM, vrom_size_bytes, 0); if (Mirroring == 2) { ExtraNTARAM = (uint8*)FCEU_gmalloc(2048); @@ -922,8 +1068,9 @@ int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { iNESCart.battery = (head.ROM_type & 2) ? 1 : 0; iNESCart.mirror = Mirroring; + iNESCart.mirrorAs2Bits = MirroringAs2bits; - int result = iNES_Init(MapperNo); + result = iNES_Init(MapperNo); switch(result) { case 0: @@ -935,6 +1082,8 @@ int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { FCEU_PrintError("Unable to allocate CHR-RAM."); break; } + +init_error: if (ROM) free(ROM); if (VROM) free(VROM); if (trainerpoo) free(trainerpoo); @@ -973,13 +1122,7 @@ init_ok: || strstr(name, "(Europe)") || strstr(name, "(PAL)") || strstr(name, "(F)") || strstr(name, "(f)") || strstr(name, "(G)") || strstr(name, "(g)") - || strstr(name, "(I)") || strstr(name, "(i)") - || strstr(name, "(S)") || strstr(name, "(s)") - || strstr(name, "(France)") || strstr(name, "(Germany)") - || strstr(name, "(Italy)") || strstr(name, "(Spain)") - || strstr(name, "(Sweden)") || strstr(name, "(Sw)") - || strstr(name, "(Australia)") || strstr(name, "(A)") - || strstr(name, "(a)")) + || strstr(name, "(I)") || strstr(name, "(i)")) FCEUI_SetVidSystem(1); else FCEUI_SetVidSystem(0); @@ -1005,7 +1148,7 @@ int iNesSaveAs(const char* name) //caitsith2: done. iNesSave() now gets filename and calls iNesSaveAs with that filename. FILE *fp; - if (GameInfo->type != GIT_CART) return 0; + if ((GameInfo->type != GIT_CART) && (GameInfo->type != GIT_VSUNI)) return 0; if (GameInterface != iNESGI) return 0; fp = fopen(name, "wb"); diff --git a/source/fceultra/ines.h b/source/fceultra/ines.h index 5ffcd2b..0db0aaf 100644 --- a/source/fceultra/ines.h +++ b/source/fceultra/ines.h @@ -25,6 +25,8 @@ #include #include +#include "cart.h" + struct TMasterRomInfo { uint64 md5lower; @@ -40,30 +42,35 @@ public: //mbg merge 6/29/06 extern uint8 *ROM; extern uint8 *VROM; +extern uint8 *MiscROM; extern uint32 VROM_size; extern uint32 ROM_size; +extern uint32 MiscROM_size; extern uint8 *ExtraNTARAM; +extern uint8 **VPageR; extern int iNesSave(void); //bbit Edited: line added extern int iNesSaveAs(const char* name); -extern char LoadedRomFName[2048]; //bbit Edited: line added +extern char LoadedRomFName[4096]; //bbit Edited: line added +extern char LoadedRomFNamePatchToUse[4096]; extern char *iNesShortFName(void); extern const TMasterRomInfo* MasterRomInfo; extern TMasterRomInfoParams MasterRomInfoParams; //mbg merge 7/19/06 changed to c++ decl format struct iNES_HEADER { - char ID[4]; /*NES^Z*/ // 0-3 - uint8 ROM_size; // 4 - uint8 VROM_size; // 5 - uint8 ROM_type; // 6 - uint8 ROM_type2; // 7 - uint8 ROM_type3; // 8 - uint8 Upper_ROM_VROM_size; // 9 - uint8 RAM_size; // 10 - uint8 VRAM_size; // 11 - uint8 TV_system; // 12 - uint8 VS_hardware; // 13 - uint8 reserved[2]; // 14, 15 + char ID[4]; /*NES^Z*/ // 0-3 + uint8 ROM_size; // 4 + uint8 VROM_size; // 5 + uint8 ROM_type; // 6 + uint8 ROM_type2; // 7 + uint8 ROM_type3; // 8 + uint8 Upper_ROM_VROM_size; // 9 + uint8 RAM_size; // 10 + uint8 VRAM_size; // 11 + uint8 TV_system; // 12 + uint8 VS_hardware; // 13 + uint8 misc_roms; // 14 + uint8 expansion; // 15 void cleanup() { @@ -204,6 +211,7 @@ void Mapper170_Init(CartInfo *); void Mapper171_Init(CartInfo *); void Mapper172_Init(CartInfo *); void Mapper173_Init(CartInfo *); +void Mapper174_Init(CartInfo *); void Mapper175_Init(CartInfo *); void Mapper177_Init(CartInfo *); void Mapper178_Init(CartInfo *); @@ -270,7 +278,15 @@ void Mapper250_Init(CartInfo *); void Mapper252_Init(CartInfo *); void Mapper253_Init(CartInfo *); void Mapper254_Init(CartInfo *); +void Mapper255_Init(CartInfo *); +void Mapper354_Init(CartInfo *); void Mapper406_Init(CartInfo *); +void Mapper413_Init(CartInfo *); +void Mapper451_Init(CartInfo *); +void Mapper471_Init(CartInfo *); + +void INX_007T_Init(CartInfo* info); +void GN45_Init(CartInfo *info); /* previously mapper 205 */ typedef struct { const char *name; diff --git a/source/fceultra/input.cpp b/source/fceultra/input.cpp index 7445c87..81bad25 100644 --- a/source/fceultra/input.cpp +++ b/source/fceultra/input.cpp @@ -106,6 +106,8 @@ uint8 FCEU_GetJoyJoy(void) } extern uint8 coinon; +extern uint8 coinon2; +extern uint8 service; //set to true if the fourscore is attached static bool FSAttached = false; @@ -232,6 +234,10 @@ static uint8 ReadGPVS(int w) return ret; } +#ifdef __FCEU_QSCRIPT_ENABLE__ +extern uint8_t FCEU_JSReadJoypad(int which, uint8_t phyState); +#endif + static void UpdateGP(int w, void *data, int arg) { if(w==0) //adelikat, 3/14/09: Changing the joypads to inclusive OR the user's joypad + the Lua joypad, this way lua only takes over the buttons it explicity says to @@ -245,6 +251,11 @@ static void UpdateGP(int w, void *data, int arg) joy[0] = *(uint32 *)joyports[0].ptr;; joy[2] = *(uint32 *)joyports[0].ptr >> 16; #endif + + #ifdef __FCEU_QSCRIPT_ENABLE__ + joy[0]= FCEU_JSReadJoypad(0,joy[0]); + joy[2]= FCEU_JSReadJoypad(2,joy[2]); + #endif } else { @@ -257,8 +268,12 @@ static void UpdateGP(int w, void *data, int arg) joy[1] = *(uint32 *)joyports[1].ptr >> 8; joy[3] = *(uint32 *)joyports[1].ptr >> 24; #endif - } + #ifdef __FCEU_QSCRIPT_ENABLE__ + joy[1]= FCEU_JSReadJoypad(1,joy[1]); + joy[3]= FCEU_JSReadJoypad(3,joy[3]); + #endif + } } static void LogGP(int w, MovieRecord* mr) @@ -419,6 +434,10 @@ void FCEU_DrawInput(uint8 *buf) portFC.driver->Draw(buf,portFC.attrib); } +#ifdef __FCEU_QNETWORK_ENABLE__ +extern bool NetPlayActive(void); +void NetPlayReadInputFrame(uint8_t* joy); +#endif void FCEU_UpdateInput(void) { @@ -431,8 +450,17 @@ void FCEU_UpdateInput(void) portFC.driver->Update(portFC.ptr,portFC.attrib); } - if(GameInfo->type==GIT_VSUNI) - if(coinon) coinon--; + if (GameInfo->type == GIT_VSUNI) { + if (coinon) coinon--; + if (coinon2) coinon2--; + if (service) service--; + } + #ifdef __FCEU_QNETWORK_ENABLE__ + if (NetPlayActive()) + { + NetPlayReadInputFrame(joy); + } + #endif if(FCEUnetplay) NetplayUpdate(joy); @@ -454,7 +482,11 @@ static DECLFR(VSUNIRead0) ret|=(vsdip&3)<<3; if(coinon) - ret|=0x4; + ret |= 0x20; + if (coinon2) + ret |= 0x40; + if (service) + ret |= 0x04; return ret; } @@ -668,7 +700,9 @@ void FCEU_DoSimpleCommand(int cmd) { case FCEUNPCMD_FDSINSERT: FCEU_FDSInsert();break; case FCEUNPCMD_FDSSELECT: FCEU_FDSSelect();break; - case FCEUNPCMD_VSUNICOIN: FCEU_VSUniCoin(); break; + case FCEUNPCMD_VSUNICOIN: FCEU_VSUniCoin(0); break; + case FCEUNPCMD_VSUNICOIN2: FCEU_VSUniCoin(1); break; + case FCEUNPCMD_VSUNISERVICE: FCEU_VSUniService(); break; case FCEUNPCMD_VSUNIDIP0: case FCEUNPCMD_VSUNIDIP0+1: case FCEUNPCMD_VSUNIDIP0+2: @@ -726,6 +760,22 @@ void FCEUI_VSUniCoin(void) FCEU_QSimpleCommand(FCEUNPCMD_VSUNICOIN); } +void FCEUI_VSUniCoin2(void) +{ + if (!FCEU_IsValidUI(FCEUI_INSERT_COIN)) + return; + + FCEU_QSimpleCommand(FCEUNPCMD_VSUNICOIN2); +} + +void FCEUI_VSUniService(void) +{ + if (!FCEU_IsValidUI(FCEUI_INSERT_COIN)) + return; + + FCEU_QSimpleCommand(FCEUNPCMD_VSUNISERVICE); +} + //Resets the frame counter if movie inactive and rom is reset or power-cycle void ResetFrameCounter() { @@ -798,6 +848,7 @@ static void RamSearchOpLTE(void); static void RamSearchOpGTE(void); static void RamSearchOpEQ(void); static void RamSearchOpNE(void); +static void ToggleCheats(void); static void DebuggerStepInto(void); static void FA_SkipLag(void); static void OpenRom(void); @@ -900,17 +951,17 @@ struct EMUCMDTABLE FCEUI_CommandTable[]= { EMUCMD_FDS_EJECT_INSERT, EMUCMDTYPE_FDS, FCEUI_FDSInsert, 0, 0, "Eject or Insert FDS Disk", EMUCMDFLAG_TASEDITOR }, { EMUCMD_FDS_SIDE_SELECT, EMUCMDTYPE_FDS, FCEUI_FDSSelect, 0, 0, "Switch FDS Disk Side", EMUCMDFLAG_TASEDITOR }, - { EMUCMD_VSUNI_COIN, EMUCMDTYPE_VSUNI, FCEUI_VSUniCoin, 0, 0, "Insert Coin", EMUCMDFLAG_TASEDITOR }, - { EMUCMD_VSUNI_TOGGLE_DIP_0, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 0", 0 }, - { EMUCMD_VSUNI_TOGGLE_DIP_1, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 1", 0 }, - { EMUCMD_VSUNI_TOGGLE_DIP_2, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 2", 0 }, - { EMUCMD_VSUNI_TOGGLE_DIP_3, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 3", 0 }, - { EMUCMD_VSUNI_TOGGLE_DIP_4, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 4", 0 }, - { EMUCMD_VSUNI_TOGGLE_DIP_5, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 5", 0 }, - { EMUCMD_VSUNI_TOGGLE_DIP_6, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 6", 0 }, - { EMUCMD_VSUNI_TOGGLE_DIP_7, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 7", 0 }, - { EMUCMD_VSUNI_TOGGLE_DIP_8, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 8", 0 }, - { EMUCMD_VSUNI_TOGGLE_DIP_9, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dipswitch 9", 0 }, + { EMUCMD_VSUNI_COIN, EMUCMDTYPE_VSUNI, FCEUI_VSUniCoin, 0, 0, "Insert Coin #1", EMUCMDFLAG_TASEDITOR }, + { EMUCMD_VSUNI_COIN_2, EMUCMDTYPE_VSUNI, FCEUI_VSUniCoin2, 0, 0, "Insert Coin #2", EMUCMDFLAG_TASEDITOR }, + { EMUCMD_VSUNI_SERVICE_BUTTON, EMUCMDTYPE_VSUNI, FCEUI_VSUniService, 0, 0, "Service Button", EMUCMDFLAG_TASEDITOR }, + { EMUCMD_VSUNI_TOGGLE_DIP_0, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dip Switch 0", 0 }, + { EMUCMD_VSUNI_TOGGLE_DIP_1, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dip Switch 1", 0 }, + { EMUCMD_VSUNI_TOGGLE_DIP_2, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dip Switch 2", 0 }, + { EMUCMD_VSUNI_TOGGLE_DIP_3, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dip Switch 3", 0 }, + { EMUCMD_VSUNI_TOGGLE_DIP_4, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dip Switch 4", 0 }, + { EMUCMD_VSUNI_TOGGLE_DIP_5, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dip Switch 5", 0 }, + { EMUCMD_VSUNI_TOGGLE_DIP_6, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dip Switch 6", 0 }, + { EMUCMD_VSUNI_TOGGLE_DIP_7, EMUCMDTYPE_VSUNI, CommandToggleDip, 0, 0, "Toggle Dip Switch 7", 0 }, { EMUCMD_MISC_AUTOSAVE, EMUCMDTYPE_MISC, FCEUI_RewindToLastAutosave, 0, 0, "Load Last Auto-save", 0}, { EMUCMD_MISC_SHOWSTATES, EMUCMDTYPE_MISC, ViewSlots, 0, 0, "View save slots", 0 }, @@ -945,6 +996,7 @@ struct EMUCMDTABLE FCEUI_CommandTable[]= { EMUCMD_TOOL_RAMSEARCHGTE, EMUCMDTYPE_TOOL, RamSearchOpGTE, 0, 0, "Ram Search - Greater Than or Equal", 0}, { EMUCMD_TOOL_RAMSEARCHEQ, EMUCMDTYPE_TOOL, RamSearchOpEQ, 0, 0, "Ram Search - Equal", 0}, { EMUCMD_TOOL_RAMSEARCHNE, EMUCMDTYPE_TOOL, RamSearchOpNE, 0, 0, "Ram Search - Not Equal", 0}, + { EMUCMD_TOOL_TOGGLECHEATS, EMUCMDTYPE_TOOL, ToggleCheats, 0, 0, "Toggle Cheats", 0}, { EMUCMD_RERECORD_DISPLAY_TOGGLE, EMUCMDTYPE_MISC, FCEUI_MovieToggleRerecordDisplay,0, 0, "Toggle Rerecord Display", EMUCMDFLAG_TASEDITOR }, { EMUCMD_TASEDITOR_REWIND, EMUCMDTYPE_TASEDITOR, TaseditorRewindOn, TaseditorRewindOff, 0, "Frame Rewind", EMUCMDFLAG_TASEDITOR }, @@ -960,12 +1012,12 @@ struct EMUCMDTABLE FCEUI_CommandTable[]= #define NUM_EMU_CMDS (sizeof(FCEUI_CommandTable)/sizeof(FCEUI_CommandTable[0])) -static int execcmd, i; +static int execcmd; void FCEUI_HandleEmuCommands(TestCommandState* testfn) { bool taseditor = FCEUMOV_Mode(MOVIEMODE_TASEDITOR); - for(i=0; i #include "share.h" - -typedef struct { - uint32 mzx,mzy,mzb; - int zap_readbit; - int bogo; - int zappo; - uint64 zaphit; -} ZAPPER; +#include "zapper.h" static ZAPPER ZD; diff --git a/source/fceultra/input/zapper.h b/source/fceultra/input/zapper.h index a106294..b2faaf7 100644 --- a/source/fceultra/input/zapper.h +++ b/source/fceultra/input/zapper.h @@ -10,7 +10,7 @@ struct ZAPPER uint8 bogo; int zappo; uint64 zaphit; - uint32 lastInput; + uint32 lastInput; }; #endif diff --git a/source/fceultra/movie.cpp b/source/fceultra/movie.cpp index 131c89d..a68e1b8 100644 --- a/source/fceultra/movie.cpp +++ b/source/fceultra/movie.cpp @@ -110,7 +110,7 @@ SFORMAT FCEUMOV_STATEINFO[]={ { 0 } }; -char curMovieFilename[512] = {0}; +std::string curMovieFilename; MovieData currMovieData; MovieData defaultMovieData; int currRerecordCount; // Keep the global value @@ -306,7 +306,7 @@ void MovieRecord::dumpJoy(EMUFILE* os, uint8 joystate) void MovieRecord::parseJoy(EMUFILE* is, uint8& joystate) { - char buf[8]; + char buf[8] = {0}; is->fread(buf,8); joystate = 0; for(int i=0;i<8;i++) @@ -468,6 +468,7 @@ MovieData::MovieData() , rerecordCount(0) , binaryFlag(false) , loadFrameCount(-1) + , fourscore(false) , microphone(false) , RAMInitOption(0) , RAMInitSeed(0) @@ -751,7 +752,7 @@ bool LoadFM2(MovieData& movieData, EMUFILE* fp, int size, bool stopAfterHeader) { LoadFM2_binarychunk(movieData, fp, size); return true; - } else if (isnewline && movieData.loadFrameCount == movieData.records.size()) + } else if (isnewline && static_cast(movieData.loadFrameCount) == movieData.records.size()) // exit prematurely if loaded the specified amound of records return true; switch(state) @@ -865,7 +866,10 @@ static EMUFILE *openRecordingMovie(const char* fname) FCEU_PrintError("Error opening movie output file: %s", fname); return NULL; } - strcpy(curMovieFilename, fname); + if ( fname != curMovieFilename.c_str() ) + { + curMovieFilename.assign(fname); + } return osRecordingMovie; } @@ -885,7 +889,7 @@ static void RedumpWholeMovieFile(bool justToggledRecording = false) bool recording = (movieMode == MOVIEMODE_RECORD); assert((NULL != osRecordingMovie) == (recording != justToggledRecording) && "osRecordingMovie should be consistent with movie mode!"); - if (NULL == openRecordingMovie(curMovieFilename)) + if (NULL == openRecordingMovie(curMovieFilename.c_str())) return; currMovieData.dump(osRecordingMovie, false/*currMovieData.binaryFlag*/, recording); @@ -933,7 +937,7 @@ static void OnMovieClosed() { assert(movieMode == MOVIEMODE_INACTIVE); - curMovieFilename[0] = 0; //No longer a current movie filename + curMovieFilename.clear(); //No longer a current movie filename freshMovie = false; //No longer a fresh movie loaded if (bindSavestate) AutoSS = false; //If bind movies to savestates is true, then there is no longer a valid auto-save to load @@ -1039,23 +1043,23 @@ bool MovieData::loadSaveramFrom(std::vector* buf) return false; } - for(int i=0;i<4;i++) + for (size_t i=0;iSaveGame.size();i++) { int len = ms.read32le(); - if(!currCartInfo->SaveGame[i] && len!=0) + if( (currCartInfo->SaveGame[i].bufptr == nullptr) && (len!=0) ) { FCEU_PrintError("movie battery load mismatch 2"); return false; } - if(currCartInfo->SaveGameLen[i] != len) + if(currCartInfo->SaveGame[i].buflen != static_cast(len)) { FCEU_PrintError("movie battery load mismatch 3"); return false; } - ms.fread(currCartInfo->SaveGame[i], len); + ms.fread(currCartInfo->SaveGame[i].bufptr, len); } return true; @@ -1066,16 +1070,15 @@ void MovieData::dumpSaveramTo(std::vector* buf, int compressionLevel) EMUFILE_MEMORY ms(buf); ms.write32le(currCartInfo->battery?1:0); - for(int i=0;i<4;i++) + for(size_t i=0;iSaveGame.size();i++) { - if(!currCartInfo->SaveGame[i]) + if (!currCartInfo->SaveGame[i].bufptr) { ms.write32le((u32)0); continue; } - - ms.write32le(currCartInfo->SaveGameLen[i]); - ms.fwrite(currCartInfo->SaveGame[i], currCartInfo->SaveGameLen[i]); + ms.write32le( static_cast(currCartInfo->SaveGame[i].buflen) ); + ms.fwrite(currCartInfo->SaveGame[i].bufptr, currCartInfo->SaveGame[i].buflen); } } @@ -1098,7 +1101,7 @@ bool FCEUI_LoadMovie(const char *fname, bool _read_only, int _pauseframe) currMovieData = MovieData(); - strcpy(curMovieFilename, fname); + curMovieFilename.assign(fname); FCEUFILE *fp = FCEU_fopen(fname,0,"rb",0); if (!fp) return false; if(fp->isArchive() && !_read_only) { @@ -1279,7 +1282,11 @@ void FCEUMOV_AddInputState() if (mr->command_fds_select()) FCEU_FDSSelect(); if (mr->command_vs_insertcoin()) - FCEU_VSUniCoin(); + FCEU_VSUniCoin(0); + if (mr->command_vs_insertcoin2()) + FCEU_VSUniCoin(1); + if (mr->command_vs_service()) + FCEU_VSUniService(); _currCommand = 0; } else #endif @@ -1307,14 +1314,18 @@ void FCEUMOV_AddInputState() if(mr->command_fds_select()) FCEU_FDSSelect(); if (mr->command_vs_insertcoin()) - FCEU_VSUniCoin(); + FCEU_VSUniCoin(0); + if (mr->command_vs_insertcoin2()) + FCEU_VSUniCoin(1); + if (mr->command_vs_service()) + FCEU_VSUniService(); joyports[0].load(mr); joyports[1].load(mr); } //if we are on the last frame, then pause the emulator if the player requested it - if (currFrameCounter == currMovieData.records.size()-1) + if ( static_cast(currFrameCounter) == currMovieData.records.size()-1) { if(FCEUD_PauseAfterPlayback()) { @@ -1385,6 +1396,8 @@ void FCEUMOV_AddCommand(int cmd) case FCEUNPCMD_FDSINSERT: cmd = MOVIECMD_FDS_INSERT; break; case FCEUNPCMD_FDSSELECT: cmd = MOVIECMD_FDS_SELECT; break; case FCEUNPCMD_VSUNICOIN: cmd = MOVIECMD_VS_INSERTCOIN; break; + case FCEUNPCMD_VSUNICOIN2: cmd = MOVIECMD_VS_INSERTCOIN2; break; + case FCEUNPCMD_VSUNISERVICE: cmd = MOVIECMD_VS_SERVICE; break; // all other netplay commands (e.g. FCEUNPCMD_VSUNIDIP0) are not supported by movie recorder for now default: return; } @@ -1404,22 +1417,22 @@ void FCEU_DrawMovies(uint8 *XBuf) if (movieMode == MOVIEMODE_PLAY) { - sprintf(counterbuf, "%d/%d%s%s", currFrameCounter, (int)currMovieData.records.size(), GetMovieRecordModeStr(), GetMovieReadOnlyStr()); + snprintf(counterbuf, sizeof(counterbuf), "%d/%d%s%s", currFrameCounter, (int)currMovieData.records.size(), GetMovieRecordModeStr(), GetMovieReadOnlyStr()); } else if (movieMode == MOVIEMODE_RECORD) { if (movieRecordMode == MOVIE_RECORD_MODE_TRUNCATE) - sprintf(counterbuf, "%d%s%s (record)", currFrameCounter, GetMovieRecordModeStr(), GetMovieReadOnlyStr()); // nearly classic + snprintf(counterbuf, sizeof(counterbuf), "%d%s%s (record)", currFrameCounter, GetMovieRecordModeStr(), GetMovieReadOnlyStr()); // nearly classic else - sprintf(counterbuf, "%d/%d%s%s (record)", currFrameCounter, (int)currMovieData.records.size(), GetMovieRecordModeStr(), GetMovieReadOnlyStr()); + snprintf(counterbuf, sizeof(counterbuf), "%d/%d%s%s (record)", currFrameCounter, (int)currMovieData.records.size(), GetMovieRecordModeStr(), GetMovieReadOnlyStr()); } else if (movieMode == MOVIEMODE_FINISHED) { - sprintf(counterbuf,"%d/%d%s%s (finished)",currFrameCounter,(int)currMovieData.records.size(), GetMovieRecordModeStr(), GetMovieReadOnlyStr()); + snprintf(counterbuf, sizeof(counterbuf), "%d/%d%s%s (finished)",currFrameCounter,(int)currMovieData.records.size(), GetMovieRecordModeStr(), GetMovieReadOnlyStr()); color = 0x17; //Show red to get attention } else if (movieMode == MOVIEMODE_TASEDITOR) { - sprintf(counterbuf,"%d",currFrameCounter); + snprintf(counterbuf, sizeof(counterbuf),"%d",currFrameCounter); } else - sprintf(counterbuf,"%d (no movie)",currFrameCounter); + snprintf(counterbuf, sizeof(counterbuf),"%d (no movie)",currFrameCounter); if (counterbuf[0]) DrawTextTrans(ClipSidesOffset+XBuf+FCEU_TextScanlineOffsetFromBottom(30)+1, 256, (uint8*)counterbuf, color+0x80); @@ -1427,7 +1440,7 @@ void FCEU_DrawMovies(uint8 *XBuf) if (rerecord_display && movieMode != MOVIEMODE_INACTIVE) { char counterbuf[32] = {0}; - sprintf(counterbuf, "%d", currMovieData.rerecordCount); + snprintf(counterbuf, sizeof(counterbuf), "%d", currMovieData.rerecordCount); if (counterbuf[0]) DrawTextTrans(ClipSidesOffset+XBuf+FCEU_TextScanlineOffsetFromBottom(50)+1, 256, (uint8*)counterbuf, 0x28+0x80); @@ -1440,7 +1453,7 @@ void FCEU_DrawLagCounter(uint8 *XBuf) { // If currently lagging - display red, else display green uint8 color = (lagFlag) ? (0x16+0x80) : (0x2A+0x80); - sprintf(lagcounterbuf, "%d", lagCounter); + snprintf(lagcounterbuf, sizeof(lagcounterbuf), "%d", lagCounter); if(lagcounterbuf[0]) DrawTextTrans(ClipSidesOffset + XBuf + FCEU_TextScanlineOffsetFromBottom(40) + 1, 256, (uint8*)lagcounterbuf, color); } @@ -1474,7 +1487,7 @@ int CheckTimelines(MovieData& stateMovie, MovieData& currMovie) } -static bool load_successful; +static bool load_successful = false; bool FCEUMOV_ReadState(EMUFILE* is, uint32 size) { @@ -1493,7 +1506,7 @@ bool FCEUMOV_ReadState(EMUFILE* is, uint32 size) #endif movie_readonly = true; } - if (FCEU_isFileInArchive(curMovieFilename)) + if (FCEU_isFileInArchive(curMovieFilename.c_str())) { //a little rule: cant load states in read+write mode with a movie from an archive. //so we are going to switch it to readonly mode in that case @@ -1628,10 +1641,10 @@ bool FCEUMOV_ReadState(EMUFILE* is, uint32 size) //TODO: turn frame counter to red to get attention if (!backupSavestates) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user { - FCEU_PrintError("Error: Savestate taken from a frame (%d) after the final frame in the savestated movie (%d) cannot be verified against current movie (%d). This is not permitted.\nUnable to restore backup, movie playback stopped.", currFrameCounter, tempMovieData.records.size() - 1, currMovieData.records.size() - 1); + FCEU_PrintError("Error: Savestate taken from a frame (%d) after the final frame in the savestated movie (%zi) cannot be verified against current movie (%zi). This is not permitted.\nUnable to restore backup, movie playback stopped.", currFrameCounter, tempMovieData.records.size() - 1, currMovieData.records.size() - 1); FCEUI_StopMovie(); } else - FCEU_PrintError("Savestate taken from a frame (%d) after the final frame in the savestated movie (%d) cannot be verified against current movie (%d). This is not permitted.", currFrameCounter, tempMovieData.records.size() - 1, currMovieData.records.size() - 1); + FCEU_PrintError("Savestate taken from a frame (%d) after the final frame in the savestated movie (%zi) cannot be verified against current movie (%zi). This is not permitted.", currFrameCounter, tempMovieData.records.size() - 1, currMovieData.records.size() - 1); return false; } } @@ -1691,18 +1704,20 @@ bool FCEUMOV_PostLoad(void) void FCEUMOV_IncrementRerecordCount() { + bool skip = false; #ifdef _S9XLUA_H - if(!FCEU_LuaRerecordCountSkip()) + skip = skip || FCEU_LuaRerecordCountSkip(); +#endif +#ifdef __FCEU_QSCRIPT_ENABLE__ + extern bool FCEU_JSRerecordCountSkip(); + skip = skip || FCEU_JSRerecordCountSkip(); +#endif + + if(!skip) if (movieMode != MOVIEMODE_TASEDITOR) currRerecordCount++; else currMovieData.rerecordCount++; -#else - if (movieMode != MOVIEMODE_TASEDITOR) - currRerecordCount++; - else - currMovieData.rerecordCount++; -#endif if (movieMode != MOVIEMODE_TASEDITOR) currMovieData.rerecordCount = currRerecordCount; } @@ -1787,7 +1802,7 @@ void FCEUI_MovieToggleReadOnly() strcpy(message, "Movie is now Read+Write"); strcat(message, GetMovieModeStr()); - FCEU_DispMessage(message,0); + FCEU_DispMessage("%s",0,message); } void FCEUI_MovieToggleRecording() @@ -1831,7 +1846,7 @@ void FCEUI_MovieToggleRecording() strcat(message, GetMovieModeStr()); - FCEU_DispMessage(message, 0); + FCEU_DispMessage("%s",0,message); } void FCEUI_MovieInsertFrame() @@ -1858,7 +1873,7 @@ void FCEUI_MovieInsertFrame() strcat(message, GetMovieModeStr()); } - FCEU_DispMessage(message, 0); + FCEU_DispMessage("%s",0,message); } void FCEUI_MovieDeleteFrame() @@ -1896,7 +1911,7 @@ void FCEUI_MovieDeleteFrame() strcat(message, GetMovieModeStr()); } - FCEU_DispMessage(message, 0); + FCEU_DispMessage("%s",0,message); } void FCEUI_MovieTruncate() @@ -1934,7 +1949,7 @@ void FCEUI_MovieTruncate() strcat(message, GetMovieModeStr()); } - FCEU_DispMessage(message, 0); + FCEU_DispMessage("%s",0,message); } void FCEUI_MovieNextRecordMode() @@ -2004,7 +2019,7 @@ void FCEUI_MoviePlayFromBeginning(void) #endif } -string FCEUI_GetMovieName(void) +std::string FCEUI_GetMovieName(void) { return curMovieFilename; } @@ -2098,9 +2113,9 @@ void FCEUI_CreateMovieFile(std::string fn) void FCEUI_MakeBackupMovie(bool dispMessage) { //This function generates backup movie files - string currentFn; //Current movie fillename - string backupFn; //Target backup filename - string tempFn; //temp used in back filename creation + std::string currentFn; //Current movie fillename + std::string backupFn; //Target backup filename + std::string tempFn; //temp used in back filename creation stringstream stream; int x; //Temp variable for string manip bool exist = false; //Used to test if filename exists diff --git a/source/fceultra/movie.h b/source/fceultra/movie.h index 45398a0..bcbe49a 100644 --- a/source/fceultra/movie.h +++ b/source/fceultra/movie.h @@ -81,7 +81,9 @@ enum EMOVIECMD MOVIECMD_POWER = 2, MOVIECMD_FDS_INSERT = 4, MOVIECMD_FDS_SELECT = 8, - MOVIECMD_VS_INSERTCOIN = 16 + MOVIECMD_VS_INSERTCOIN = 16, + MOVIECMD_VS_INSERTCOIN2 = 32, + MOVIECMD_VS_SERVICE = 64 }; EMOVIEMODE FCEUMOV_Mode(); @@ -131,6 +133,8 @@ public: bool command_fds_insert() { return (commands & MOVIECMD_FDS_INSERT) != 0; } bool command_fds_select() { return (commands & MOVIECMD_FDS_SELECT) != 0; } bool command_vs_insertcoin() { return (commands & MOVIECMD_VS_INSERTCOIN) != 0; } + bool command_vs_insertcoin2() { return (commands & MOVIECMD_VS_INSERTCOIN2) != 0; } + bool command_vs_service() { return (commands & MOVIECMD_VS_SERVICE) != 0; } void toggleBit(int joy, int bit) { @@ -210,7 +214,7 @@ public: //whether microphone is enabled bool microphone; - int getNumRecords() { return (int)records.size(); } + int getNumRecords() { return static_cast( records.size() ); } int RAMInitOption, RAMInitSeed; @@ -271,7 +275,7 @@ private: extern MovieData currMovieData; extern int currFrameCounter; -extern char curMovieFilename[512]; +extern std::string curMovieFilename; extern bool subtitlesOnAVI; extern bool freshMovie; extern bool movie_readonly; diff --git a/source/fceultra/netplay.cpp b/source/fceultra/netplay.cpp index 4d2003a..bbec736 100644 --- a/source/fceultra/netplay.cpp +++ b/source/fceultra/netplay.cpp @@ -122,7 +122,10 @@ int FCEUNET_SendFile(uint8 cmd, char *fn) FCEUX_fstat(fileno(fp),&sb); len = sb.st_size; buf = (char*)FCEU_dmalloc(len); //mbg merge 7/17/06 added cast - fread(buf, 1, len, fp); + if ( fread(buf, 1, len, fp) != static_cast(len) ) + { + FCEU_printf("Warning: FCEUNET_SendFile failed to load complete file.\n"); + } fclose(fp); cbuf = (char*)FCEU_dmalloc(4 + len + len / 1000 + 12); //mbg merge 7/17/06 added cast diff --git a/source/fceultra/netplay.h b/source/fceultra/netplay.h index 8385255..7e5ead0 100644 --- a/source/fceultra/netplay.h +++ b/source/fceultra/netplay.h @@ -7,7 +7,10 @@ extern int FCEUnetplay; #define FCEUNPCMD_POWER 0x02 #define FCEUNPCMD_VSUNICOIN 0x07 -#define FCEUNPCMD_VSUNIDIP0 0x08 +#define FCEUNPCMD_VSUNIDIP0 0x08 +#define FCEUNPCMD_VSUNICOIN2 0x20 +#define FCEUNPCMD_VSUNISERVICE 0x21 + #define FCEUNPCMD_FDSINSERTx 0x10 #define FCEUNPCMD_FDSINSERT 0x18 //#define FCEUNPCMD_FDSEJECT 0x19 diff --git a/source/fceultra/nsf.cpp b/source/fceultra/nsf.cpp index d7aee06..e1b30a8 100644 --- a/source/fceultra/nsf.cpp +++ b/source/fceultra/nsf.cpp @@ -32,6 +32,7 @@ #include "file.h" #include "fds.h" #include "cart.h" +#include "ines.h" #include "input.h" #include "state.h" #include "driver.h" @@ -123,8 +124,6 @@ static uint16 PlayAddr; //configuration static uint16 InitAddr; //configuration static uint16 LoadAddr; //configuration -extern char LoadedRomFName[2048]; - NSF_HEADER NSFHeader; //mbg merge 6/29/06 - needs to be global void NSFMMC5_Close(void); @@ -582,7 +581,7 @@ void DrawNSF(uint8 *XBuf) DrawTextTrans(ClipSidesOffset+XBuf+42*256+4+(((31-strlen((char*)NSFHeader.Copyright))<<2)), 256,NSFHeader.Copyright, kFgColor); DrawTextTrans(ClipSidesOffset+XBuf+70*256+4+(((31-strlen("Song:"))<<2)), 256, (uint8*)"Song:", kFgColor); - sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs); + snprintf(snbuf, sizeof(snbuf), "<%d/%d>",CurrentSong,NSFHeader.TotalSongs); DrawTextTrans(XBuf+82*256+4+(((31-strlen(snbuf))<<2)), 256, (uint8*)snbuf, kFgColor); { diff --git a/source/fceultra/palette.cpp b/source/fceultra/palette.cpp index 2d888d4..baf0a89 100644 --- a/source/fceultra/palette.cpp +++ b/source/fceultra/palette.cpp @@ -88,6 +88,7 @@ pal *palo = NULL; (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ ) +FCEU_MAYBE_UNUSED static void ApplyDeemphasisNTSC(int entry, u8& r, u8& g, u8& b) { static float const to_float = 1.0f / 0xFF; @@ -253,6 +254,7 @@ static void ApplyDeemphasisBisqwit(int entry, u8& r, u8& g, u8& b) } //classic algorithm +FCEU_MAYBE_UNUSED static void ApplyDeemphasisClassic(int entry, u8& r, u8& g, u8& b) { //DEEMPH BITS MAY BE ORDERED WRONG. PLEASE CHECK diff --git a/source/fceultra/palettes/conv.c b/source/fceultra/palettes/conv.c deleted file mode 100644 index 13e4d8f..0000000 --- a/source/fceultra/palettes/conv.c +++ /dev/null @@ -1,74 +0,0 @@ -/* Quick conversion stuff for MAME->FCE Ultra */ - -#include -#include "../types.h" -#include "../palette.h" -#include "palettes.h" - -/* check 0x08 */ -static uint8 rp2c04001_colortable[] = -{ - 0x35, 0x23, 0x16, 0x22, 0x1c, 0x09, 0xff, 0x15, /* 0x00 - 0x07 */ - 0x20, 0x00, 0x27, 0x05, 0x04, 0x27, 0x08, 0x30, /* 0x08 - 0x0f */ - 0x21, 0xff, 0xff, 0x29, 0x3c, 0x32, 0x36, 0x12, /* 0x10 - 0x17 */ - 0xff, 0x2b, 0x0f, 0xff, 0x20, 0x10, 0x24, 0x01, /* 0x18 - 0x1f */ - 0xff, 0x31, 0xff, 0x2a, 0x2c, 0x0c, 0xff, 0xff, /* 0x20 - 0x27 */ - 0xff, 0x07, 0x34, 0x06, 0x13, 0x02, 0x26, 0x0f, /* 0x28 - 0x2f */ - 0xff, 0x19, 0x10, 0x0a, 0x39, 0xff, 0x37, 0x17, /* 0x30 - 0x37 */ - 0xff, 0x11, 0x09, 0xff, 0x39, 0x25, 0x18, 0xff /* 0x38 - 0x3f */ -}; - -/* RP2C04-002 */ -static uint8 rp2c04002_colortable[] = -{ - 0x0f, 0x27, 0x18, 0xff, 0x3a, 0x25, 0xff, 0x31, /* 0x00 - 0x07 */ - 0x16, 0x13, 0x38, 0x34, 0x20, 0x23, 0xff, 0x0b, /* 0x08 - 0x0f */ - 0xff, 0x21, 0x06, 0xff, 0x1b, 0x29, 0xff, 0x22, /* 0x10 - 0x17 */ - 0xff, 0x24, 0xff, 0x2b, 0xff, 0x08, 0xff, 0x03, /* 0x18 - 0x1f */ - 0xff, 0x36, 0x26, 0x33, 0x11, 0xff, 0x10, 0x02, /* 0x20 - 0x27 */ - 0x14, 0xff, 0x00, 0x09, 0x12, 0x0f, 0x37, 0x30, /* 0x28 - 0x2f */ - 0xff, 0xff, 0x2a, 0x17, 0x0c, 0x01, 0x15, 0x19, /* 0x30 - 0x37 */ - 0xff, 0x2c, 0x07, 0x37, 0xff, 0x05, 0x0a, 0x00 /* 0x38 - 0x3f */ -}; - -/* RP2C04-003 */ -/* Check 0x00. Used in Dr Mario. */ -static uint8 rp2c04003_colortable[] = -{ - 0x03, 0xff, 0xff, 0x00, 0x1a, 0x30, 0x31, 0x09, /* 0x00 - 0x07 */ - 0x01, 0x0f, 0x36, 0x08, 0x15, 0xff, 0xff, 0x30, /* 0x08 - 0x0f */ - 0x22, 0x1c, 0xff, 0x12, 0x19, 0x18, 0x17, 0x1b, /* 0x10 - 0x17 */ - 0x00, 0xff, 0xff, 0x02, 0x16, 0x06, 0xff, 0x35, /* 0x18 - 0x1f */ - 0x23, 0xff, 0x0f, 0x37, 0xff, 0x27, 0x26, 0x30, /* 0x20 - 0x27 */ - 0x29, 0xff, 0x21, 0x24, 0x11, 0xff, 0x0f, 0xff, /* 0x28 - 0x2f */ - 0x2c, 0xff, 0xff, 0xff, 0x07, 0x2a, 0x28, 0xff, /* 0x30 - 0x37 */ - 0x0a, 0xff, 0x32, 0x37, 0x13, 0xff, 0xff, 0x0c /* 0x38 - 0x3f */ -}; - -/* RP2C05-004 */ -/* check 0x1d, 0x38 */ -static uint8 rp2c05004_colortable[] = -{ - 0x18, 0xff, 0x1c, 0x28, 0xff, 0xff, 0x01, 0x17, /* 0x00 - 0x07 */ - 0x10, 0x0f, 0x2a, 0x0f, 0x36, 0x37, 0x1a, 0xff, /* 0x08 - 0x0f */ - 0x25, 0xff, 0x12, 0xff, 0x0f, 0xff, 0xff, 0x26, /* 0x10 - 0x17 */ - 0xff, 0xff, 0x22, 0x19, 0xff, 0x0f, 0x3a, 0x21, /* 0x18 - 0x1f */ - 0x05, 0x0a, 0x07, 0x01, 0x13, 0xff, 0x00, 0x15, /* 0x20 - 0x27 */ - 0x0c, 0xff, 0x11, 0xff, 0xff, 0x38, 0xff, 0xff, /* 0x28 - 0x2f */ - 0xff, 0xff, 0x08, 0x16, 0xff, 0xff, 0x30, 0x3c, /* 0x30 - 0x37 */ - 0x0f, 0x27, 0xff, 0x31, 0x29, 0xff, 0x30, 0x09 /* 0x38 - 0x3f */ -}; - - -main() -{ - int x; - for(x=0;x<64;x++) - { - // if(x <= 0x20) -// if(rp2c04002_colortable[x] == 0xFF) rp2c04002_colortable[x]= 0x30; - printf("{0x%02x, 0x%02x, 0x%02x},\n",palette[rp2c04001_colortable[x]&0x3F].r, - palette[rp2c04001_colortable[x]&0x3F].g, - palette[rp2c04001_colortable[x]&0x3F].b); - } -} diff --git a/source/fceultra/palettes/palettes.h b/source/fceultra/palettes/palettes.h index b9a43da..3d97453 100644 --- a/source/fceultra/palettes/palettes.h +++ b/source/fceultra/palettes/palettes.h @@ -56,73 +56,76 @@ pal palette_unvarying[] = { }; +#define P64(x) (((x)<<2)|((x>>4)&3)) + // Default palette pal palette[512] = { + { P64(0x1D), P64(0x1D), P64(0x1D)}, /* Value 0 */ + { P64(0x09), P64(0x06), P64(0x23)}, /* Value 1 */ + { P64(0x00), P64(0x00), P64(0x2A)}, /* Value 2 */ + { P64(0x11), P64(0x00), P64(0x27)}, /* Value 3 */ + { P64(0x23), P64(0x00), P64(0x1D)}, /* Value 4 */ + { P64(0x2A), P64(0x00), P64(0x04)}, /* Value 5 */ + { P64(0x29), P64(0x00), P64(0x00)}, /* Value 6 */ + { P64(0x1F), P64(0x02), P64(0x00)}, /* Value 7 */ + { P64(0x10), P64(0x0B), P64(0x00)}, /* Value 8 */ + { P64(0x00), P64(0x11), P64(0x00)}, /* Value 9 */ + { P64(0x00), P64(0x14), P64(0x00)}, /* Value 10 */ + { P64(0x00), P64(0x0F), P64(0x05)}, /* Value 11 */ + { P64(0x06), P64(0x0F), P64(0x17)}, /* Value 12 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 13 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 14 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 15 */ + { P64(0x2F), P64(0x2F), P64(0x2F)}, /* Value 16 */ + { P64(0x00), P64(0x1C), P64(0x3B)}, /* Value 17 */ + { P64(0x08), P64(0x0E), P64(0x3B)}, /* Value 18 */ + { P64(0x20), P64(0x00), P64(0x3C)}, /* Value 19 */ + { P64(0x2F), P64(0x00), P64(0x2F)}, /* Value 20 */ + { P64(0x39), P64(0x00), P64(0x16)}, /* Value 21 */ + { P64(0x36), P64(0x0A), P64(0x00)}, /* Value 22 */ + { P64(0x32), P64(0x13), P64(0x03)}, /* Value 23 */ + { P64(0x22), P64(0x1C), P64(0x00)}, /* Value 24 */ + { P64(0x00), P64(0x25), P64(0x00)}, /* Value 25 */ + { P64(0x00), P64(0x2A), P64(0x00)}, /* Value 26 */ + { P64(0x00), P64(0x24), P64(0x0E)}, /* Value 27 */ + { P64(0x00), P64(0x20), P64(0x22)}, /* Value 28 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 29 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 30 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 31 */ + { P64(0x3F), P64(0x3F), P64(0x3F)}, /* Value 32 */ + { P64(0x0F), P64(0x2F), P64(0x3F)}, /* Value 33 */ + { P64(0x17), P64(0x25), P64(0x3F)}, /* Value 34 */ + { P64(0x33), P64(0x22), P64(0x3F)}, /* Value 35 */ + { P64(0x3D), P64(0x1E), P64(0x3F)}, /* Value 36 */ + { P64(0x3F), P64(0x1D), P64(0x2D)}, /* Value 37 */ + { P64(0x3F), P64(0x1D), P64(0x18)}, /* Value 38 */ + { P64(0x3F), P64(0x26), P64(0x0E)}, /* Value 39 */ + { P64(0x3C), P64(0x2F), P64(0x0F)}, /* Value 40 */ + { P64(0x20), P64(0x34), P64(0x04)}, /* Value 41 */ + { P64(0x13), P64(0x37), P64(0x12)}, /* Value 42 */ + { P64(0x16), P64(0x3E), P64(0x26)}, /* Value 43 */ + { P64(0x00), P64(0x3A), P64(0x36)}, /* Value 44 */ + { P64(0x1E), P64(0x1E), P64(0x1E)}, /* Value 45 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 46 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 47 */ + { P64(0x3F), P64(0x3F), P64(0x3F)}, /* Value 48 */ + { P64(0x2A), P64(0x39), P64(0x3F)}, /* Value 49 */ + { P64(0x31), P64(0x35), P64(0x3F)}, /* Value 50 */ + { P64(0x35), P64(0x32), P64(0x3F)}, /* Value 51 */ + { P64(0x3F), P64(0x31), P64(0x3F)}, /* Value 52 */ + { P64(0x3F), P64(0x31), P64(0x36)}, /* Value 53 */ + { P64(0x3F), P64(0x2F), P64(0x2C)}, /* Value 54 */ + { P64(0x3F), P64(0x36), P64(0x2A)}, /* Value 55 */ + { P64(0x3F), P64(0x39), P64(0x28)}, /* Value 56 */ + { P64(0x38), P64(0x3F), P64(0x28)}, /* Value 57 */ + { P64(0x2A), P64(0x3C), P64(0x2F)}, /* Value 58 */ + { P64(0x2C), P64(0x3F), P64(0x33)}, /* Value 59 */ + { P64(0x27), P64(0x3F), P64(0x3C)}, /* Value 60 */ + { P64(0x31), P64(0x31), P64(0x31)}, /* Value 61 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 62 */ + { P64(0x00), P64(0x00), P64(0x00)}, /* Value 63 */ - { 0x1D<<2, 0x1D<<2, 0x1D<<2 }, /* Value 0 */ - { 0x09<<2, 0x06<<2, 0x23<<2 }, /* Value 1 */ - { 0x00<<2, 0x00<<2, 0x2A<<2 }, /* Value 2 */ - { 0x11<<2, 0x00<<2, 0x27<<2 }, /* Value 3 */ - { 0x23<<2, 0x00<<2, 0x1D<<2 }, /* Value 4 */ - { 0x2A<<2, 0x00<<2, 0x04<<2 }, /* Value 5 */ - { 0x29<<2, 0x00<<2, 0x00<<2 }, /* Value 6 */ - { 0x1F<<2, 0x02<<2, 0x00<<2 }, /* Value 7 */ - { 0x10<<2, 0x0B<<2, 0x00<<2 }, /* Value 8 */ - { 0x00<<2, 0x11<<2, 0x00<<2 }, /* Value 9 */ - { 0x00<<2, 0x14<<2, 0x00<<2 }, /* Value 10 */ - { 0x00<<2, 0x0F<<2, 0x05<<2 }, /* Value 11 */ - { 0x06<<2, 0x0F<<2, 0x17<<2 }, /* Value 12 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 13 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 14 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 15 */ - { 0x2F<<2, 0x2F<<2, 0x2F<<2 }, /* Value 16 */ - { 0x00<<2, 0x1C<<2, 0x3B<<2 }, /* Value 17 */ - { 0x08<<2, 0x0E<<2, 0x3B<<2 }, /* Value 18 */ - { 0x20<<2, 0x00<<2, 0x3C<<2 }, /* Value 19 */ - { 0x2F<<2, 0x00<<2, 0x2F<<2 }, /* Value 20 */ - { 0x39<<2, 0x00<<2, 0x16<<2 }, /* Value 21 */ - { 0x36<<2, 0x0A<<2, 0x00<<2 }, /* Value 22 */ - { 0x32<<2, 0x13<<2, 0x03<<2 }, /* Value 23 */ - { 0x22<<2, 0x1C<<2, 0x00<<2 }, /* Value 24 */ - { 0x00<<2, 0x25<<2, 0x00<<2 }, /* Value 25 */ - { 0x00<<2, 0x2A<<2, 0x00<<2 }, /* Value 26 */ - { 0x00<<2, 0x24<<2, 0x0E<<2 }, /* Value 27 */ - { 0x00<<2, 0x20<<2, 0x22<<2 }, /* Value 28 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 29 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 30 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 31 */ - { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 32 */ - { 0x0F<<2, 0x2F<<2, 0x3F<<2 }, /* Value 33 */ - { 0x17<<2, 0x25<<2, 0x3F<<2 }, /* Value 34 */ - { 0x33<<2, 0x22<<2, 0x3F<<2 }, /* Value 35 */ - { 0x3D<<2, 0x1E<<2, 0x3F<<2 }, /* Value 36 */ - { 0x3F<<2, 0x1D<<2, 0x2D<<2 }, /* Value 37 */ - { 0x3F<<2, 0x1D<<2, 0x18<<2 }, /* Value 38 */ - { 0x3F<<2, 0x26<<2, 0x0E<<2 }, /* Value 39 */ - { 0x3C<<2, 0x2F<<2, 0x0F<<2 }, /* Value 40 */ - { 0x20<<2, 0x34<<2, 0x04<<2 }, /* Value 41 */ - { 0x13<<2, 0x37<<2, 0x12<<2 }, /* Value 42 */ - { 0x16<<2, 0x3E<<2, 0x26<<2 }, /* Value 43 */ - { 0x00<<2, 0x3A<<2, 0x36<<2 }, /* Value 44 */ - { 0x1E<<2, 0x1E<<2, 0x1E<<2 }, /* Value 45 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 46 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 47 */ - { 0x3F<<2, 0x3F<<2, 0x3F<<2 }, /* Value 48 */ - { 0x2A<<2, 0x39<<2, 0x3F<<2 }, /* Value 49 */ - { 0x31<<2, 0x35<<2, 0x3F<<2 }, /* Value 50 */ - { 0x35<<2, 0x32<<2, 0x3F<<2 }, /* Value 51 */ - { 0x3F<<2, 0x31<<2, 0x3F<<2 }, /* Value 52 */ - { 0x3F<<2, 0x31<<2, 0x36<<2 }, /* Value 53 */ - { 0x3F<<2, 0x2F<<2, 0x2C<<2 }, /* Value 54 */ - { 0x3F<<2, 0x36<<2, 0x2A<<2 }, /* Value 55 */ - { 0x3F<<2, 0x39<<2, 0x28<<2 }, /* Value 56 */ - { 0x38<<2, 0x3F<<2, 0x28<<2 }, /* Value 57 */ - { 0x2A<<2, 0x3C<<2, 0x2F<<2 }, /* Value 58 */ - { 0x2C<<2, 0x3F<<2, 0x33<<2 }, /* Value 59 */ - { 0x27<<2, 0x3F<<2, 0x3C<<2 }, /* Value 60 */ - { 0x31<<2, 0x31<<2, 0x31<<2 }, /* Value 61 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 62 */ - { 0x00<<2, 0x00<<2, 0x00<<2 }, /* Value 63 */ + #undef P64 //luke's .16+ palette //{0x60, 0x60, 0x60}, /* Value 0 */ diff --git a/source/fceultra/ppu.cpp b/source/fceultra/ppu.cpp index 01a03a7..ce84a9d 100644 --- a/source/fceultra/ppu.cpp +++ b/source/fceultra/ppu.cpp @@ -57,6 +57,7 @@ #define PPU_status (PPU[2]) +#define READPALNOGS(ofs) (PALRAM[(ofs)]) #define READPAL(ofs) (PALRAM[(ofs)] & (GRAYSCALE ? 0x30 : 0xFF)) #define READUPAL(ofs) (UPALRAM[(ofs)] & (GRAYSCALE ? 0x30 : 0xFF)) @@ -455,13 +456,24 @@ volatile int rendercount, vromreadcount, undefinedvromcount, LogAddress = -1; unsigned char *cdloggervdata = NULL; unsigned int cdloggerVideoDataSize = 0; -int GetCHRAddress(int A) { - if (cdloggerVideoDataSize) { - int result = &VPage[A >> 10][A] - CHRptr[0]; +int GetCHRAddress(int A) +{ + if (cdloggerVideoDataSize) + { + int result = -1; + if ( (A >= 0) && (A < 0x2000) ) + { + result = &VPage[A >> 10][A] - CHRptr[0]; + } if ((result >= 0) && (result < (int)cdloggerVideoDataSize)) + { return result; - } else - if(A < 0x2000) return A; + } + } + else + { + if ( (A >= 0) && (A < 0x2000) ) return A; + } return -1; } @@ -1722,10 +1734,17 @@ void FCEUPPU_Reset(void) { void FCEUPPU_Power(void) { int x; - memset(NTARAM, 0x00, 0x800); - memset(PALRAM, 0x00, 0x20); - memset(UPALRAM, 0x00, 0x03); - memset(SPRAM, 0x00, 0x100); + // initialize PPU memory regions according to settings + FCEU_MemoryRand(NTARAM, 0x800, true); + FCEU_MemoryRand(PALRAM, 0x20, true); + FCEU_MemoryRand(SPRAM, 0x100, true); + // palettes can only store values up to $3F, and PALRAM X4/X8/XC are mirrors of X0 for rendering purposes (UPALRAM is used for $2007 readback) + for (x = 0; x < 0x20; ++x) PALRAM[x] &= 0x3F; + UPALRAM[0] = PALRAM[0x04]; + UPALRAM[1] = PALRAM[0x08]; + UPALRAM[2] = PALRAM[0x0C]; + PALRAM[0x0C] = PALRAM[0x08] = PALRAM[0x04] = PALRAM[0x00]; + PALRAM[0x1C] = PALRAM[0x18] = PALRAM[0x14] = PALRAM[0x10]; FCEUPPU_Reset(); for (x = 0x2000; x < 0x4000; x += 8) { @@ -1987,6 +2006,7 @@ void runppu(int x) { struct BGData { struct Record { uint8 nt, pecnt, at, pt[2], qtnt; + uint8 ppu1[8]; INLINE void Read() { NTRefreshAddr = RefreshAddr = ppur.get_ntread(); @@ -1998,7 +2018,12 @@ struct BGData { } pecnt = (RefreshAddr & 1) << 3; nt = CALL_PPUREAD(RefreshAddr); - runppu(kFetchTime); + ppu1[0] = PPU[1]; + runppu(1); + ppu1[1] = PPU[1]; + runppu(1); + + RefreshAddr = ppur.get_atread(); at = CALL_PPUREAD(RefreshAddr); @@ -2010,37 +2035,57 @@ struct BGData { at <<= 2; //horizontal scroll clocked at cycle 3 and then //vertical scroll at 251 + ppu1[2] = PPU[1]; runppu(1); if (PPUON) { ppur.increment_hsc(); if (ppur.status.cycle == 251) ppur.increment_vs(); } + ppu1[3] = PPU[1]; runppu(1); ppur.par = nt; RefreshAddr = ppur.get_ptread(); if (PEC586Hack) { pt[0] = CALL_PPUREAD(RefreshAddr | pecnt); - runppu(kFetchTime); + ppu1[4] = PPU[1]; + runppu(1); + ppu1[5] = PPU[1]; + runppu(1); pt[1] = CALL_PPUREAD(RefreshAddr | pecnt); - runppu(kFetchTime); + ppu1[6] = PPU[1]; + runppu(1); + ppu1[7] = PPU[1]; + runppu(1); } else if (QTAIHack && (qtnt & 0x40)) { pt[0] = *(CHRptr[0] + RefreshAddr); - runppu(kFetchTime); + ppu1[4] = PPU[1]; + runppu(1); + ppu1[5] = PPU[1]; + runppu(1); RefreshAddr |= 8; pt[1] = *(CHRptr[0] + RefreshAddr); - runppu(kFetchTime); + ppu1[6] = PPU[1]; + runppu(1); + ppu1[7] = PPU[1]; + runppu(1); } else { if (ScreenON) RENDER_LOG(RefreshAddr); pt[0] = CALL_PPUREAD(RefreshAddr); - runppu(kFetchTime); + ppu1[4] = PPU[1]; + runppu(1); + ppu1[5] = PPU[1]; + runppu(1); RefreshAddr |= 8; if (ScreenON) RENDER_LOG(RefreshAddr); pt[1] = CALL_PPUREAD(RefreshAddr); - runppu(kFetchTime); + ppu1[6] = PPU[1]; + runppu(1); + ppu1[7] = PPU[1]; + runppu(1); } } }; @@ -2216,7 +2261,7 @@ int FCEUX_PPU_Loop(int skip) { pixel = ((pt[0] >> (7 - bgpx)) & 1) | (((pt[1] >> (7 - bgpx)) & 1) << 1) | bgdata.main[bgtile].at; } if (renderbg) - pixelcolor = READPAL(pixel); + pixelcolor = READPALNOGS(pixel); //look for a sprite to be drawn bool havepixel = false; @@ -2261,12 +2306,25 @@ int FCEUX_PPU_Loop(int skip) { spixel |= (oam[2] & 3) << 2; if (rendersprites) - pixelcolor = READPAL(0x10 + spixel); + pixelcolor = READPALNOGS(0x10 + spixel); } } - *ptr++ = PaletteAdjustPixel(pixelcolor); - *dptr++= PPU[1]>>5; //grab deemph + //apply grayscale.. kind of clunky + //really we need to read the entire palette instead of just ppu1 + //this will be needed for special color effects probably (very fine rainbows and whatnot?) + //are you allowed to chang the palette mid-line anyway? well you can definitely change the grayscale flag as we know from the FF1 "polygon" effect + if(bgdata.main[xt+2].ppu1[xp]&1) + pixelcolor &= 0x30; + + //this does deemph stuff inside it.. which is probably wrong... + *ptr = PaletteAdjustPixel(pixelcolor); + + ptr++; + + //grab deemph.. + //I guess this works the same way as the grayscale, ideally? + *dptr++ = bgdata.main[xt+2].ppu1[xp]>>5; } } } diff --git a/source/fceultra/pputile.inc b/source/fceultra/pputile.inc index ce4a6d0..af4299b 100644 --- a/source/fceultra/pputile.inc +++ b/source/fceultra/pputile.inc @@ -6,7 +6,7 @@ uint8 tmpd; #endif #ifndef PPUT_MMC5SP - uint8 zz; + FCEU_MAYBE_UNUSED uint8 zz; #else uint8 xs, ys; xs = X1; @@ -87,9 +87,14 @@ pshift[1] <<= 8; #else #ifdef PPU_VRC5FETCH - if(tmpd & 0x40) + if(tmpd & 0x40) { + if (CHRsize[0] == (128 * 1024)) { + // NOTE: address 128K CHR-ROM using offsets into 256K CHR-ROM data + // https://www.nesdev.org/wiki/NES_2.0_Mapper_547#Kanji_ROM_layout + vadr = ((vadr & 0x00007) << 1) | ((vadr & 0x00010) >> 4) | ((vadr & 0x3FFE0) >> 1); + } C = CHRptr[0] + vadr; - else + } else C = VRAMADR(vadr); #else C = VRAMADR(vadr); diff --git a/source/fceultra/sound.cpp b/source/fceultra/sound.cpp index b484129..64523e5 100644 --- a/source/fceultra/sound.cpp +++ b/source/fceultra/sound.cpp @@ -534,9 +534,10 @@ static INLINE void DMCDMA(void) PrepDPCM(); else { - SIRQStat|=0x80; - if(DMCFormat&0x80) + if(DMCFormat&0x80) { + SIRQStat|=0x80; X6502_IRQBegin(FCEU_IQDPCM); + } } } } diff --git a/source/fceultra/state.cpp b/source/fceultra/state.cpp index fa572ed..b79a945 100644 --- a/source/fceultra/state.cpp +++ b/source/fceultra/state.cpp @@ -63,6 +63,7 @@ using namespace std; static void (*SPreSave)(void) = NULL; static void (*SPostSave)(void) = NULL; +static void (*SPostLoad)(bool) = NULL; static int SaveStateStatus[10]; static int StateShow; @@ -84,11 +85,11 @@ bool backupSavestates = true; bool compressSavestates = true; //By default FCEUX compresses savestates when a movie is inactive. // a temp memory stream. We'll be dumping some data here and then compress -EMUFILE_MEMORY memory_savestate; +static EMUFILE_MEMORY memory_savestate; // temporary buffer for compressed data of a savestate -std::vector compressed_buf; +static std::vector compressed_buf; -#define SFMDATA_SIZE (64) +#define SFMDATA_SIZE (128) static SFORMAT SFMDATA[SFMDATA_SIZE]; static int SFEXINDEX; @@ -110,7 +111,7 @@ SFORMAT SFCPU[]={ { &X.Y, 1, "Y\0\0"}, { &X.S, 1, "S\0\0"}, { &X.P, 1, "P\0\0"}, - { &X.DB, 1, "DB"}, + { &X.DB, 1, "DB\0"}, { &RAM, 0x800 | FCEUSTATE_INDIRECT, "RAM", }, { 0 } }; @@ -133,7 +134,7 @@ static int SubWrite(EMUFILE* os, SFORMAT *sf) while(sf->v) { - if(sf->s==~0) //Link to another struct + if(sf->s==~0u) //Link to another struct { uint32 tmp; @@ -152,7 +153,7 @@ static int SubWrite(EMUFILE* os, SFORMAT *sf) os->fwrite(sf->desc,4); write32le(sf->s&(~FCEUSTATE_FLAGS),os); -#ifndef LSB_FIRST +#ifdef FCEU_BIG_ENDIAN if(sf->s&RLSB) FlipByteOrder((uint8*)sf->v,sf->s&(~FCEUSTATE_FLAGS)); #endif @@ -163,7 +164,7 @@ static int SubWrite(EMUFILE* os, SFORMAT *sf) os->fwrite((char*)sf->v,sf->s&(~FCEUSTATE_FLAGS)); //Now restore the original byte order. -#ifndef LSB_FIRST +#ifdef FCEU_BIG_ENDIAN if(sf->s&RLSB) FlipByteOrder((uint8*)sf->v,sf->s&(~FCEUSTATE_FLAGS)); #endif @@ -191,7 +192,7 @@ static SFORMAT *CheckS(SFORMAT *sf, uint32 tsize, char *desc) { while(sf->v) { - if(sf->s==~0) // Link to another SFORMAT structure. + if(sf->s==~0u) // Link to another SFORMAT structure. { SFORMAT *tmp; if((tmp= CheckS((SFORMAT *)sf->v, tsize, desc) )) @@ -231,7 +232,7 @@ static bool ReadStateChunk(EMUFILE* is, SFORMAT *sf, int size) else is->fread((char *)tmp->v,tmp->s&(~FCEUSTATE_FLAGS)); -#ifndef LSB_FIRST +#ifdef FCEU_BIG_ENDIAN if(tmp->s&RLSB) FlipByteOrder((uint8*)tmp->v,tmp->s&(~FCEUSTATE_FLAGS)); #endif @@ -307,13 +308,24 @@ static bool ReadStateChunks(EMUFILE* is, int32 totalsize) // load back buffer { extern uint8 *XBackBuf; - if(is->fread((char*)XBackBuf,size) != size) - ret = false; + //ignore 8 garbage bytes, whose idea was it to write these or even have them there in the first place + if(size == 256*256+8) + { + if(is->fread((char*)XBackBuf,256*256) != 256*256) + ret = false; + is->fseek(8,SEEK_CUR); + } + else + { + if(is->fread((char*)XBackBuf,size) != size) + ret = false; + } + //MBG TODO - can this be moved to a better place? //does it even make sense, displaying XBuf when its XBackBuf we just loaded? #ifdef __WIN_DRIVER__ - else + if(ret) { FCEUD_BlitScreen(XBuf); UpdateFCEUWindow(); @@ -334,7 +346,7 @@ static bool ReadStateChunks(EMUFILE* is, int32 totalsize) if(!warned) { char str [256]; - sprintf(str, "Warning: Found unknown save chunk of type %d.\nThis could indicate the save state is corrupted\nor made with a different (incompatible) emulator version.", t); + snprintf(str, sizeof(str), "Warning: Found unknown save chunk of type %d.\nThis could indicate the save state is corrupted\nor made with a different (incompatible) emulator version.", t); FCEUD_PrintError(str); warned=true; } @@ -404,7 +416,7 @@ bool FCEUSS_SaveMS(EMUFILE* outstream, int compressionLevel) // save back buffer { extern uint8 *XBackBuf; - uint32 size = 256 * 256 + 8; + uint32 size = 256 * 256; os->fputc(8); write32le(size, os); os->fwrite((char*)XBackBuf,size); @@ -416,7 +428,7 @@ bool FCEUSS_SaveMS(EMUFILE* outstream, int compressionLevel) if(SPostSave) SPostSave(); //save the length of the file - int len = memory_savestate.size(); + size_t len = memory_savestate.size(); //sanity check: len and totalsize should be the same if(len != totalsize) @@ -427,7 +439,7 @@ bool FCEUSS_SaveMS(EMUFILE* outstream, int compressionLevel) int error = Z_OK; uint8* cbuf = (uint8*)memory_savestate.buf(); - uLongf comprlen = -1; + uLongf comprlen = ~0lu; if(compressionLevel != Z_NO_COMPRESSION && (compressSavestates || FCEUMOV_Mode(MOVIEMODE_TASEDITOR))) { // worst case compression: zlib says "0.1% larger than sourceLen plus 12 bytes" @@ -446,7 +458,7 @@ bool FCEUSS_SaveMS(EMUFILE* outstream, int compressionLevel) //dump it to the destination file outstream->fwrite((char*)header,16); - outstream->fwrite((char*)cbuf,comprlen==-1?totalsize:comprlen); + outstream->fwrite((char*)cbuf,comprlen==~0lu?totalsize:comprlen); return error == Z_OK; } @@ -500,13 +512,12 @@ void FCEUSS_Save(const char *fname, bool display_message) LuaSaveData saveData; CallRegisteredLuaSaveFunctions(CurrentState, saveData); - char luaSaveFilename [512]; - strncpy(luaSaveFilename, fn, 512); - luaSaveFilename[512-(1+7/*strlen(".luasav")*/)] = '\0'; - strcat(luaSaveFilename, ".luasav"); + std::string luaSaveFilename; + luaSaveFilename.assign(fn.c_str()); + luaSaveFilename.append(".luasav"); if(saveData.recordList) { - FILE* luaSaveFile = fopen(luaSaveFilename, "wb"); + FILE* luaSaveFile = fopen(luaSaveFilename.c_str(), "wb"); if(luaSaveFile) { saveData.ExportRecords(luaSaveFile); @@ -515,7 +526,7 @@ void FCEUSS_Save(const char *fname, bool display_message) } else { - unlink(luaSaveFilename); + unlink(luaSaveFilename.c_str()); } } #endif @@ -629,6 +640,11 @@ int FCEUSS_LoadFP_old(EMUFILE* is, ENUM_SSLOADPARAMS params) return(x); } +#ifdef __QT_DRIVER__ +// Qt Driver NetPlay state load handler. This is to control state loading +// during netplay, only hosts can load states and clients can request loads. +bool NetPlayStateLoadReq(EMUFILE* is); +#endif bool FCEUSS_LoadFP(EMUFILE* is, ENUM_SSLOADPARAMS params) { @@ -655,29 +671,37 @@ bool FCEUSS_LoadFP(EMUFILE* is, ENUM_SSLOADPARAMS params) return ret; } - int totalsize = FCEU_de32lsb(header + 4); - int stateversion = FCEU_de32lsb(header + 8); - int comprlen = FCEU_de32lsb(header + 12); +#ifdef __QT_DRIVER__ + if ( NetPlayStateLoadReq(is) ) + { + return false; + } +#endif + + size_t totalsize = FCEU_de32lsb(header + 4); + int stateversion = FCEU_de32lsb(header + 8); + uint32_t comprlen = FCEU_de32lsb(header + 12); // reinit memory_savestate // memory_savestate is global variable which already has its vector of bytes, so no need to allocate memory every time we use save/loadstate - if ((int)(memory_savestate.get_vec())->size() < totalsize) + if ((memory_savestate.get_vec())->size() < totalsize) (memory_savestate.get_vec())->resize(totalsize); memory_savestate.set_len(totalsize); memory_savestate.unfail(); memory_savestate.fseek(0, SEEK_SET); - if(comprlen != -1) + if(comprlen != ~0u) { // the savestate is compressed: read from is to compressed_buf, then decompress from compressed_buf to memory_savestate.vec - if ((int)compressed_buf.size() < comprlen) compressed_buf.resize(comprlen); + if (compressed_buf.size() < comprlen) compressed_buf.resize(comprlen); is->fread(&compressed_buf[0], comprlen); uLongf uncomprlen = totalsize; int error = uncompress(memory_savestate.buf(), &uncomprlen, &compressed_buf[0], comprlen); if(error != Z_OK || uncomprlen != totalsize) return false; // we dont need to restore the backup here because we havent messed with the emulator state yet - } else + } + else { // the savestate is not compressed: just read from is to memory_savestate.vec is->fread(memory_savestate.buf(), totalsize); @@ -700,15 +724,25 @@ bool FCEUSS_LoadFP(EMUFILE* is, ENUM_SSLOADPARAMS params) FCEUPPU_LoadState(stateversion); FCEUSND_LoadState(stateversion); x=FCEUMOV_PostLoad(); - } else if (backup) + } + else if (backup) { msBackupSavestate.fseek(0,SEEK_SET); FCEUSS_LoadFP(&msBackupSavestate,SSLOADPARAM_NOBACKUP); } + // Post state load callback that is used to notify driver code that a new state load occurred. + if (SPostLoad != NULL) + { + SPostLoad(x); + } return x; } +void FCEUSS_SetLoadCallback( void (*cb)(bool) ) +{ + SPostLoad = cb; +} bool FCEUSS_Load(const char *fname, bool display_message) { @@ -759,18 +793,19 @@ bool FCEUSS_Load(const char *fname, bool display_message) { char szFilename[260]={0}; splitpath(fname, 0, 0, szFilename, 0); - if (display_message) + if (display_message) { - FCEU_DispMessage("State %s loaded.", 0, szFilename); - //FCEU_DispMessage("State %s loaded. Filename: %s", 0, szFilename, fn); - } - } else + FCEU_DispMessage("State %s loaded.", 0, szFilename); + //FCEU_DispMessage("State %s loaded. Filename: %s", 0, szFilename, fn.c_str()); + } + } + else { - if (display_message) + if (display_message) { - FCEU_DispMessage("State %d loaded.", 0, CurrentState); - //FCEU_DispMessage("State %d loaded. Filename: %s", 0, CurrentState, fn); - } + FCEU_DispMessage("State %d loaded.", 0, CurrentState); + //FCEU_DispMessage("State %d loaded. Filename: %s", 0, CurrentState, fn.c_str()); + } SaveStateStatus[CurrentState] = 1; } delete st; @@ -780,11 +815,10 @@ bool FCEUSS_Load(const char *fname, bool display_message) { LuaSaveData saveData; - char luaSaveFilename [512]; - strncpy(luaSaveFilename, fn, 512); - luaSaveFilename[512-(1+7/*strlen(".luasav")*/)] = '\0'; - strcat(luaSaveFilename, ".luasav"); - FILE* luaSaveFile = fopen(luaSaveFilename, "rb"); + std::string luaSaveFilename; + luaSaveFilename.assign(fn.c_str()); + luaSaveFilename.append(".luasav"); + FILE* luaSaveFile = fopen(luaSaveFilename.c_str(), "rb"); if(luaSaveFile) { saveData.ImportRecords(luaSaveFile); @@ -796,7 +830,7 @@ bool FCEUSS_Load(const char *fname, bool display_message) #endif #ifdef __WIN_DRIVER__ - Update_RAM_Search(); // Update_RAM_Watch() is also called. + Update_RAM_Search(); // Update_RAM_Watch() is also called. #endif //Update input display if movie is loaded @@ -806,7 +840,8 @@ bool FCEUSS_Load(const char *fname, bool display_message) cur_input_display = FCEU_GetJoyJoy(); //Input display should show the last buttons pressed (stored in the savestate) return true; - } else + } + else { if(!fname) SaveStateStatus[CurrentState] = 1; @@ -816,7 +851,6 @@ bool FCEUSS_Load(const char *fname, bool display_message) FCEU_DispMessage("Error(s) reading state %d!", 0, CurrentState); //FCEU_DispMessage("Error(s) reading state %d! Filename: %s", 0, CurrentState, fn); } - delete st; return 0; } } @@ -848,7 +882,7 @@ void ResetExState(void (*PreSave)(void), void (*PostSave)(void)) for(x=0;x +#include + //enables a hack designed for debugging dragon warrior 3 which treats BRK as a 3-byte opcode //#define BRK_3BYTE_HACK @@ -62,6 +65,8 @@ typedef signed int int32; #define alloca __builtin_alloca #endif +//#include + #include #include #include @@ -91,9 +96,6 @@ typedef uint32_t uint32; #define GINLINE /* Can't declare a function INLINE and global in MSVC. Bummer. */ - #define PSS_STYLE 2 /* Does MSVC compile for anything - other than Windows/DOS targets? - */ #if _MSC_VER >= 1300 #pragma warning(disable:4244) //warning C4244: '=' : conversion from 'uint32' to 'uint8', possible loss of data @@ -105,6 +107,17 @@ typedef uint32_t uint32; #endif #endif +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) + + #define PSS_STYLE 1 + +#elif defined(MSVC) + + #define PSS_STYLE 2 /* Does MSVC compile for anything + other than Windows/DOS targets? + */ +#endif + #if PSS_STYLE==2 #define PSS "\\" @@ -131,7 +144,7 @@ typedef uint32_t uint32; #endif -#if defined(WIN32) && !defined(__QT_DRIVER__) +#if defined(WIN32) && !defined(__QT_DRIVER__) && !defined(__WIN_DRIVER__) #define __WIN_DRIVER__ #endif @@ -143,6 +156,114 @@ typedef uint8 (*readfunc)(uint32 A); #define CTASSERT(x) typedef char __assert ## y[(x) ? 1 : -1]; #endif +#define __FCEU_STRINGIZE2(x) #x +#define __FCEU_STRINGIZE(x) __FCEU_STRINGIZE2(x) + +#define FCEU_CPP_HAS_STD(x) ( defined(__cplusplus) && (__cplusplus >= x) ) + +#ifdef __has_cpp_attribute +#define FCEU_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +#define FCEU_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#define FCEU_UNUSED(x) (void)(x) +#define FCEU_CRASH() int *_dumbPointer = nullptr; *_dumbPointer = 0xdeadbeef + +#if FCEU_CPP_HAS_STD(201603L) || FCEU_HAS_CPP_ATTRIBUTE(maybe_unused) +#define FCEU_MAYBE_UNUSED [[maybe_unused]] +#else +#define FCEU_MAYBE_UNUSED +#endif + +#if defined(_MSC_VER) + // Microsoft compiler won't catch format issues, but VS IDE can catch on analysis mode + #define __FCEU_PRINTF_FORMAT _In_z_ _Printf_format_string_ + #define __FCEU_PRINTF_ATTRIBUTE( fmt, va ) + +#elif defined(__GNUC__) || defined(__clang__) || FCEU_HAS_CPP_ATTRIBUTE(format) + // GCC and Clang compilers will perform printf format type checks, useful for catching format errors. + #define __FCEU_PRINTF_FORMAT + #define __FCEU_PRINTF_ATTRIBUTE( fmt, va ) __attribute__((__format__(__printf__, fmt, va))) + +#else + #define __FCEU_PRINTF_FORMAT + #define __FCEU_PRINTF_ATTRIBUTE( fmt, va ) +#endif + +#if defined(__cplusplus) +// Scoped pointer ensures that memory pointed to by this object gets cleaned up +// and deallocated when this object goes out of scope. Helps prevent memory leaks +// on temporary memory allocations in functions with early outs. +enum fceuAllocType +{ + FCEU_ALLOC_TYPE_NEW = 0, + FCEU_ALLOC_TYPE_NEW_ARRAY, + FCEU_ALLOC_TYPE_MALLOC +}; + +template +class fceuScopedPtr +{ + public: + fceuScopedPtr( T *ptrIn = nullptr, enum fceuAllocType allocType = FCEU_ALLOC_TYPE_NEW ) + { + //printf("Scoped Pointer Constructor <%s>: %p\n", typeid(T).name(), ptrIn ); + ptr = ptrIn; + _allocType = allocType; + } + + ~fceuScopedPtr(void) + { + //printf("Scoped Pointer Destructor <%s>: %p\n", typeid(T).name(), ptr ); + Delete(); + } + + T* operator= (T *ptrIn) + { + ptr = ptrIn; + return ptr; + } + + T* get(void) + { + return ptr; + } + + void Delete(void) + { + if (ptr) + { + switch (_allocType) + { + case FCEU_ALLOC_TYPE_MALLOC: + { + ::free(ptr); + } + break; + case FCEU_ALLOC_TYPE_NEW_ARRAY: + { + delete [] ptr; + } + break; + default: + case FCEU_ALLOC_TYPE_NEW: + { + delete ptr; + } + break; + } + ptr = nullptr; + } + } + + private: + T *ptr; + enum fceuAllocType _allocType; + +}; +#endif // __cplusplus + #include "utils/endian.h" #endif diff --git a/source/fceultra/unif.cpp b/source/fceultra/unif.cpp index 41da18e..a04880f 100644 --- a/source/fceultra/unif.cpp +++ b/source/fceultra/unif.cpp @@ -108,7 +108,7 @@ static void ResetUNIF(void) { vramo = 0; boardname = 0; mirrortodo = 0; - memset(&UNIFCart, 0, sizeof(UNIFCart)); + UNIFCart.clear(); UNIFchrrama = 0; } @@ -144,7 +144,7 @@ static int DoMirroring(FCEUFILE *fp) { return(0); FCEU_printf(" %02x", t); } - FCEU_printf("\n Default Name/Attribute Table Mirroring: Horizontal\n", uchead.info); + FCEU_printf("\n Default Name/Attribute Table Mirroring: Horizontal\n"); mirrortodo = 0; } return(1); @@ -165,9 +165,9 @@ static int NAME(FCEUFILE *fp) { namebuf[index] = 0; FCEU_printf("%s\n", namebuf); - if (!GameInfo->name) { - GameInfo->name = (uint8*)malloc(strlen(namebuf) + 1); //mbg merge 7/17/06 added cast - strcpy((char*)GameInfo->name, namebuf); //mbg merge 7/17/06 added cast + if (GameInfo->name == nullptr) + { + GameInfo->name = (uint8*)strdup(namebuf); } return(1); } @@ -479,6 +479,8 @@ static BMAPPING bmap[] = { { "FNS", FNS_Init, BMCFLAG_16KCHRR }, { "BS-400R", BS400R_Init, 0 }, { "BS-4040R", BS4040R_Init, 0 }, + { "COOLGIRL", COOLGIRL_Init, BMCFLAG_256KCHRR }, + { "JC-016-2", Mapper205_Init, 0 }, { 0, 0, 0 } }; diff --git a/source/fceultra/unif.h b/source/fceultra/unif.h index b6759bd..92fc5a3 100644 --- a/source/fceultra/unif.h +++ b/source/fceultra/unif.h @@ -162,7 +162,8 @@ void MINDKIDS_Init(CartInfo *info); void FNS_Init(CartInfo *info); void BS400R_Init(CartInfo *info); void BS4040R_Init(CartInfo *info); -void SMD132_SMD133_Init(CartInfo *info); +void AA6023_Init(CartInfo *info); +void COOLGIRL_Init(CartInfo* info); extern uint8 *UNIFchrrama; // Meh. So I can stop CHR RAM // bank switcherooing with certain boards... diff --git a/source/fceultra/utils/endian.cpp b/source/fceultra/utils/endian.cpp index 25b5c4e..0a4574d 100644 --- a/source/fceultra/utils/endian.cpp +++ b/source/fceultra/utils/endian.cpp @@ -121,7 +121,7 @@ int read32le(uint32 *Bufo, FILE *fp) uint32 buf; if(fread(&buf,1,4,fp)<4) return 0; -#ifdef LSB_FIRST +#ifdef FCEU_LITTLE_ENDIAN *(uint32*)Bufo=buf; #else *(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24); @@ -134,7 +134,7 @@ int read16le(uint16 *Bufo, std::istream *is) uint16 buf; if(is->read((char*)&buf,2).gcount() != 2) return 0; -#ifdef LSB_FIRST +#ifdef FCEU_LITTLE_ENDIAN *Bufo=buf; #else *Bufo = FCEU_de16lsb((uint8*)&buf); @@ -148,7 +148,7 @@ int read64le(uint64 *Bufo, std::istream *is) uint64 buf; if(is->read((char*)&buf,8).gcount() != 8) return 0; -#ifdef LSB_FIRST +#ifdef FCEU_LITTLE_ENDIAN *Bufo=buf; #else *Bufo = FCEU_de64lsb((uint8*)&buf); @@ -162,7 +162,7 @@ int read32le(uint32 *Bufo, std::istream *is) uint32 buf; if(is->read((char*)&buf,4).gcount() != 4) return 0; -#ifdef LSB_FIRST +#ifdef FCEU_LITTLE_ENDIAN *(uint32*)Bufo=buf; #else *(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24); @@ -173,7 +173,7 @@ int read32le(uint32 *Bufo, std::istream *is) ///reads a little endian 16bit value from the specified file int read16le(char *d, FILE *fp) { -#ifdef LSB_FIRST +#ifdef FCEU_LITTLE_ENDIAN return((fread(d,1,2,fp)<2)?0:2); #else int ret; diff --git a/source/fceultra/utils/endian.h b/source/fceultra/utils/endian.h index eb7ca31..389d41b 100644 --- a/source/fceultra/utils/endian.h +++ b/source/fceultra/utils/endian.h @@ -107,5 +107,13 @@ int writele(T *Bufo, EMUFILE*os) } } +#ifdef __BIG_ENDIAN__ +# define FCEU_BIG_ENDIAN +#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# define FCEU_BIG_ENDIAN +#else +# define FCEU_LITTLE_ENDIAN +#endif + #endif //__FCEU_ENDIAN diff --git a/source/fceultra/utils/memory.cpp b/source/fceultra/utils/memory.cpp index 80e072e..f1d11cc 100644 --- a/source/fceultra/utils/memory.cpp +++ b/source/fceultra/utils/memory.cpp @@ -28,53 +28,90 @@ #include "../fceu.h" #include "memory.h" -///allocates the specified number of bytes. exits process if this fails -void *FCEU_gmalloc(uint32 size) +void *FCEU_amalloc(size_t size, size_t alignment) { - - void *ret; - ret=malloc(size); - if(!ret) - { - FCEU_PrintError("Error allocating memory! Doing a hard exit."); - exit(1); - } - FCEU_MemoryRand((uint8*)ret,size,true); // initialize according to RAMInitOption, default zero - return ret; + size = (size + alignment - 1) & ~(alignment-1); + + #ifdef _MSC_VER + void *ret = _aligned_malloc(size,alignment); + #else + void *ret = aligned_alloc(alignment,size); + #endif + + if(!ret) + FCEU_abort("Error allocating memory!"); + + return ret; } -///allocates the specified number of bytes. returns null if this fails -void *FCEU_malloc(uint32 size) +void FCEU_afree(void* ptr) { - void *ret; - ret=malloc(size); - if(!ret) - { - FCEU_PrintError("Error allocating memory!"); - return(0); - } - memset(ret,0,size); // initialize to 0 - return ret; + #ifdef _MSC_VER + _aligned_free(ptr); + #else + free(ptr); + #endif +} + +static void *_FCEU_malloc(size_t size) +{ + void* ret = malloc(size); + + if(!ret) + FCEU_abort("Error allocating memory!"); + + return ret; +} + +static void _FCEU_free(void* ptr) +{ + free(ptr); +} + +void *FCEU_gmalloc(size_t size) +{ + void *ret = _FCEU_malloc(size); + + // initialize according to RAMInitOption, default zero + FCEU_MemoryRand((uint8*)ret,size,true); + + return ret; +} + +void *FCEU_malloc(size_t size) +{ + void *ret = _FCEU_malloc(size); + memset(ret, 0, size); + return ret; } -///frees memory allocated with FCEU_gmalloc void FCEU_gfree(void *ptr) { - free(ptr); + _FCEU_free(ptr); } -///frees memory allocated with FCEU_malloc -void FCEU_free(void *ptr) // Might do something with this and FCEU_malloc later... +void FCEU_free(void *ptr) { - free(ptr); + _FCEU_free(ptr); } -void *FCEU_dmalloc(uint32 size) +void *FCEU_dmalloc(size_t size) { - return malloc(size); + return FCEU_malloc(size); } void FCEU_dfree(void *ptr) { - free(ptr); + return FCEU_free(ptr); +} + +void* FCEU_realloc(void* ptr, size_t size) +{ + return realloc(ptr,size); +} + +void FCEU_abort(const char* message) +{ + if(message) FCEU_PrintError("%s", message); + abort(); } diff --git a/source/fceultra/utils/memory.h b/source/fceultra/utils/memory.h index 559c7ba..d0ee446 100644 --- a/source/fceultra/utils/memory.h +++ b/source/fceultra/utils/memory.h @@ -24,13 +24,34 @@ #define FCEU_dwmemset(d,c,n) {int _x; for(_x=n-4;_x>=0;_x-=4) *(uint32 *)&(d)[_x]=c;} -void *FCEU_malloc(uint32 size); // initialized to 0 -void *FCEU_gmalloc(uint32 size); // used by boards for WRAM etc, initialized to 0 (default) or other via RAMInitOption -void FCEU_gfree(void *ptr); -void FCEU_free(void *ptr); -void FCEU_memmove(void *d, void *s, uint32 l); +//returns a buffer initialized to 0 +void *FCEU_malloc(size_t size); -// wrapper for debugging when its needed, otherwise act like -// normal malloc/free -void *FCEU_dmalloc(uint32 size); +//returns a buffer, with jumbled initial contents +//used by boards for WRAM etc, initialized to 0 (default) or other via RAMInitOption +void *FCEU_gmalloc(size_t size); + +//free memory allocated with FCEU_gmalloc +void FCEU_gfree(void *ptr); + +//returns an aligned buffer, initialized to 0 +//the alignment will default to the largest thing you could ever sensibly want for massively aligned cache friendly buffers +void *FCEU_amalloc(size_t size, size_t alignment = 256); + +//frees memory allocated with FCEU_amalloc +void FCEU_afree(void* ptr); + +//free memory allocated with FCEU_malloc +void FCEU_free(void *ptr); + +//reallocate memory allocated with FCEU_malloc +void* FCEU_realloc(void* ptr, size_t size); + +//don't use these. change them if you find them. +void *FCEU_dmalloc(size_t size); + +//don't use these. change them if you find them. void FCEU_dfree(void *ptr); + +//aborts the process for fatal errors +void FCEU_abort(const char* message = nullptr); diff --git a/source/fceultra/utils/xstring.cpp b/source/fceultra/utils/xstring.cpp index 0c6df8c..f631662 100644 --- a/source/fceultra/utils/xstring.cpp +++ b/source/fceultra/utils/xstring.cpp @@ -194,7 +194,7 @@ static const struct Base64Table data[62] = '+'; // 62 data[63] = '/'; // 63 // create ascii->value mapping (but due to overlap, write it to highbit region) - for(a=0; a<64; ++a) data[data[a]^0x80] = a; // + for(a=0; a<64; ++a) data[data[a]^0x80] = static_cast(a); // data[((unsigned char)'=') ^ 0x80] = 0; } unsigned char operator[] (size_t pos) const { return data[pos]; } diff --git a/source/fceultra/version.h b/source/fceultra/version.h index dc2c1be..91200c3 100644 --- a/source/fceultra/version.h +++ b/source/fceultra/version.h @@ -61,15 +61,15 @@ #endif #define FCEU_VERSION_MAJOR 2 -#define FCEU_VERSION_MINOR 6 -#define FCEU_VERSION_PATCH 4 +#define FCEU_VERSION_MINOR 7 +#define FCEU_VERSION_PATCH 0 #define FCEU_VERSION_NUMERIC ( (FCEU_VERSION_MAJOR*10000) + (FCEU_VERSION_MINOR*100) + (FCEU_VERSION_PATCH) ) #define FCEU_VERSION_MAJOR_DECODE(x) ( (x / 10000) ) #define FCEU_VERSION_MINOR_DECODE(x) ( (x / 100) % 100 ) #define FCEU_VERSION_PATCH_DECODE(x) (x % 100) -#define FCEU_VERSION_STRING "2.6.4" FCEU_SUBVERSION_STRING FCEU_FEATURE_STRING FCEU_COMPILER +#define FCEU_VERSION_STRING "2.7.0" FCEU_SUBVERSION_STRING FCEU_FEATURE_STRING FCEU_COMPILER #define FCEU_NAME_AND_VERSION FCEU_NAME " " FCEU_VERSION_STRING #endif diff --git a/source/fceultra/video.cpp b/source/fceultra/video.cpp index 4905f8e..8d5878b 100644 --- a/source/fceultra/video.cpp +++ b/source/fceultra/video.cpp @@ -87,23 +87,25 @@ std::string AsSnapshotName =""; //adelikat:this will set the snapshot name whe void FCEUI_SetSnapshotAsName(std::string name) { AsSnapshotName = name; } std::string FCEUI_GetSnapshotAsName() { return AsSnapshotName; } +static void FCEU_DrawPauseCountDown(uint8 *XBuf) { } + void FCEU_KillVirtualVideo(void) { if ( XBuf ) { - FCEU_free(XBuf); XBuf = NULL; + FCEU_afree(XBuf); XBuf = NULL; } if ( XBackBuf ) { - FCEU_free(XBackBuf); XBackBuf = NULL; + FCEU_afree(XBackBuf); XBackBuf = NULL; } if ( XDBuf ) { - FCEU_free(XDBuf); XDBuf = NULL; + FCEU_afree(XDBuf); XDBuf = NULL; } if ( XDBackBuf ) { - FCEU_free(XDBackBuf); XDBackBuf = NULL; + FCEU_afree(XDBackBuf); XDBackBuf = NULL; } //printf("Video Core Cleanup\n"); } @@ -116,32 +118,22 @@ void FCEU_KillVirtualVideo(void) int FCEU_InitVirtualVideo(void) { //Some driver code may allocate XBuf externally. - //256 bytes per scanline, * 240 scanline maximum, +16 for alignment, + //256 bytes per scanline, * 240 scanline maximum if(XBuf) return 1; - XBuf = (u8*)FCEU_malloc(256 * 256 + 16); - XBackBuf = (u8*)FCEU_malloc(256 * 256 + 16); - XDBuf = (u8*)FCEU_malloc(256 * 256 + 16); - XDBackBuf = (u8*)FCEU_malloc(256 * 256 + 16); - if(!XBuf || !XBackBuf || !XDBuf || !XDBackBuf) - { - return 0; - } + XBuf = (u8*)FCEU_amalloc(256 * 256); + XBackBuf = (u8*)FCEU_amalloc(256 * 256); + XDBuf = (u8*)FCEU_amalloc(256 * 256); + XDBackBuf = (u8*)FCEU_amalloc(256 * 256); + xbsave = XBuf; - if( sizeof(uint8*) == 4 ) - { - uintptr_t m = (uintptr_t)XBuf; - m = ( 8 - m) & 7; - XBuf+=m; - } - - memset(XBuf,128,256*256); - memset(XBackBuf,128,256*256); memset(XBuf,128,256*256); memset(XBackBuf,128,256*256); + memset(XDBuf,0,256*256); + memset(XDBackBuf,0,256*256); return 1; } @@ -264,6 +256,7 @@ void FCEU_PutImage(void) FCEU_DrawLagCounter(XBuf); FCEU_DrawNTSCControlBars(XBuf); FCEU_DrawRecordingStatus(XBuf); + FCEU_DrawPauseCountDown(XBuf); ShowFPS(); } @@ -400,7 +393,7 @@ void snapAVI() FCEUI_AviVideoUpdate(XBuf); } -void FCEU_DispMessageOnMovie(const char *format, ...) +void FCEU_DispMessageOnMovie( __FCEU_PRINTF_FORMAT const char *format, ...) { va_list ap; @@ -419,7 +412,7 @@ void FCEU_DispMessageOnMovie(const char *format, ...) guiMessage.howlong = 0; } -void FCEU_DispMessage(const char *format, int disppos=0, ...) +void FCEU_DispMessage( __FCEU_PRINTF_FORMAT const char *format, int disppos=0, ...) { va_list ap; @@ -432,7 +425,7 @@ void FCEU_DispMessage(const char *format, int disppos=0, ...) vsnprintf(temp,sizeof(temp),format,ap); va_end(ap); strcat(temp, "\n"); - FCEU_printf(temp); + FCEU_printf("%s",temp); if ( vidGuiMsgEna ) { @@ -777,7 +770,7 @@ void ShowFPS(void) if ( da > FCEUD_GetTimeFreq() ) { - sprintf(fpsmsg, "%.1f", (double)boopcount / ((double)da / FCEUD_GetTimeFreq())); + snprintf(fpsmsg, sizeof(fpsmsg), "%.1f", (double)boopcount / ((double)da / FCEUD_GetTimeFreq())); boopcount = 0; boop_ts = ts; diff --git a/source/fceultra/video.h b/source/fceultra/video.h index 64e0688..7fbb6ba 100644 --- a/source/fceultra/video.h +++ b/source/fceultra/video.h @@ -7,10 +7,20 @@ int SaveSnapshot(char[]); void ResetScreenshotsCounter(); uint32 GetScreenPixel(int x, int y, bool usebackup); int GetScreenPixelPalette(int x, int y, bool usebackup); + +//in case we need more flags in the future we can change the size here +//bit0 : monochrome bit +//bit5 : emph red +//bit6 : emph green +//bit7 : emph blue +typedef uint8 xfbuf_t; + extern uint8 *XBuf; extern uint8 *XBackBuf; extern uint8 *XDBuf; extern uint8 *XDBackBuf; +extern xfbuf_t *XFBuf; + extern int ClipSidesOffset; struct GUIMESSAGE diff --git a/source/fceultra/vsuni.cpp b/source/fceultra/vsuni.cpp index 1fc6f52..8c0975f 100644 --- a/source/fceultra/vsuni.cpp +++ b/source/fceultra/vsuni.cpp @@ -26,35 +26,23 @@ #include "vsuni.h" #include "state.h" #include "driver.h" +#include "cart.h" +#include "ines.h" #include #include -#define IOPTION_GUN 0x1 -#define IOPTION_SWAPDIRAB 0x2 - -#define IOPTION_PREDIP 0x10 -typedef struct { - const char *name; - uint64 md5partial; - int mapper; - int mirroring; - int ppu; - int ioption; - int predip; -} VSUNIENTRY; - -VSUNIENTRY *curvs; - -static uint8 DIPS = 0; +static int DIPS_howlong = 0; uint8 vsdip = 0; -void FCEUI_VSUniToggleDIPView(void) { - DIPS = !DIPS; -} - void FCEU_VSUniToggleDIP(int w) { + if (GameInfo->type != GIT_VSUNI) { + FCEU_DispMessage("Not Vs. System; toggle DIP switch.", 0); + return; + } vsdip ^= 1 << w; + DIPS_howlong = 180; + FCEU_DispMessage("DIP switch %d is %s.", 0, w, vsdip & (1 << w) ? "on" : "off"); } void FCEUI_VSUniSetDIP(int w, int state) { @@ -66,20 +54,19 @@ uint8 FCEUI_VSUniGetDIPs(void) { return(vsdip); } -static uint8 secdata[2][32] = +static uint8 secdata_tko[32] = { - { - 0xff, 0xbf, 0xb7, 0x97, 0x97, 0x17, 0x57, 0x4f, - 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, 0x94, 0x14, - 0x56, 0x4e, 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, - 0xd4, 0x5c, 0x3e, 0x26, 0x87, 0x83, 0x13, 0x00 - }, - { - 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, - 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - } + 0xff, 0xbf, 0xb7, 0x97, 0x97, 0x17, 0x57, 0x4f, + 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, 0x94, 0x14, + 0x56, 0x4e, 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, + 0xd4, 0x5c, 0x3e, 0x26, 0x87, 0x83, 0x13, 0x00 +}; +static uint8 secdata_rbi[32] = +{ + 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8 *secptr; @@ -93,24 +80,30 @@ static DECLFR(VSSecRead) { } return(0x00); } -uint8 coinon = 0; -void FCEU_VSUniCoin(void) { - coinon = 6; +uint8 coinon = 0; +uint8 coinon2 = 0; +uint8 service = 0; + +void FCEU_VSUniCoin(uint8 slot) { + if (GameInfo->type != GIT_VSUNI) + FCEU_DispMessage("Not Vs. System; can't insert coin.", 0); + else { + switch (slot) { + case 0: + coinon = 6; break; + case 1: + coinon2 = 6; break; + } + } } -static int curppu; -static int64 curmd5; - -#define RP2C04_0001 1 -#define RP2C04_0002 2 -#define RP2C04_0003 3 -#define RP2C04_0004 4 -#define RCP2C03B 5 -#define RC2C05_01 6 -#define RC2C05_02 7 -#define RC2C05_03 8 -#define RC2C05_04 9 +void FCEU_VSUniService() { + if (GameInfo->type != GIT_VSUNI) + FCEU_DispMessage("Not Vs. System; can't press service button.", 0); + else + service = 6; +} static readfunc OldReadPPU; static writefunc OldWritePPU[2]; @@ -126,10 +119,10 @@ static DECLFR(A2002_Topgun) { static DECLFR(A2002_MBJ) { // Mighty Bomb Jack return((OldReadPPU(A) & ~0x3F) | 0x3D); } - static DECLFW(B2000_2001_2C05) { OldWritePPU[(A & 1) ^ 1](A ^ 1, V); } + static uint8 xevselect = 0; static DECLFR(XevRead) { if (A == 0x54FF) { @@ -146,7 +139,7 @@ static DECLFR(XevRead) { } void FCEU_VSUniSwap(uint8 *j0, uint8 *j1) { - if (curvs->ioption & IOPTION_SWAPDIRAB) { + if (GameInfo->vs_cswitch) { uint16 t = *j0; *j0 = (*j0 & 0xC) | (*j1 & 0xF3); *j1 = (*j1 & 0xC) | (t & 0xF3); @@ -154,28 +147,39 @@ void FCEU_VSUniSwap(uint8 *j0, uint8 *j1) { } void FCEU_VSUniPower(void) { - coinon = 0; + coinon = coinon2 = service = 0; VSindex = 0; if (secptr) SetReadHandler(0x5e00, 0x5e01, VSSecRead); - if (curppu == RC2C05_04) { + switch (GameInfo->vs_ppu) { + case GIPPU_RP2C04_0001: + case GIPPU_RP2C04_0002: + case GIPPU_RP2C04_0003: + case GIPPU_RP2C04_0004: + default_palette_selection = GameInfo->vs_ppu; + break; + default: + // nothing todo + break; + } + if (GameInfo->vs_ppu == GIPPU_RC2C05_04) { OldReadPPU = GetReadHandler(0x2002); SetReadHandler(0x2002, 0x2002, A2002_Topgun); - } else if (curppu == RC2C05_03) { + } else if (GameInfo->vs_ppu == GIPPU_RC2C05_03) { OldReadPPU = GetReadHandler(0x2002); SetReadHandler(0x2002, 0x2002, A2002_Gumshoe); - } else if (curppu == RC2C05_02) { + } else if (GameInfo->vs_ppu == GIPPU_RC2C05_02) { OldReadPPU = GetReadHandler(0x2002); SetReadHandler(0x2002, 0x2002, A2002_MBJ); } - if (curppu == RC2C05_04 || curppu == RC2C05_01 || curppu == RC2C05_03 || curppu == RC2C05_02) { + if (GameInfo->vs_ppu == GIPPU_RC2C05_01 || GameInfo->vs_ppu == GIPPU_RC2C05_02 || GameInfo->vs_ppu == GIPPU_RC2C05_03 || GameInfo->vs_ppu == GIPPU_RC2C05_04) { OldWritePPU[0] = GetWriteHandler(0x2000); OldWritePPU[1] = GetWriteHandler(0x2001); SetWriteHandler(0x2000, 0x2001, B2000_2001_2C05); } - if (curmd5 == 0x2d396247cf58f9faLL) { /* Super Xevious */ + if (GameInfo->vs_type == EGIVS_XEVIOUS) { /* Super Xevious */ SetReadHandler(0x5400, 0x57FF, XevRead); } } @@ -192,7 +196,7 @@ void FCEU_VSUniPower(void) { /* Games/PPU list. Information copied from MAME. ROMs are exchangable, so don't take this list as "this game must use this PPU". -RP2C04-0001: +RP2C04-001: - Baseball - Freedom Force - Gradius @@ -202,7 +206,7 @@ RP2C04-0001: - Platoon - Super Xevious -RP2C04-0002: +RP2C04-002: - Castlevania - Ladies golf - Mach Rider (Endurance Course) @@ -211,21 +215,21 @@ RP2C04-0002: - Stroke N' Match Golf - Wrecking Crew -RP2C04-0003: -- Dr. Mario +RP2C04-003: +- Dr mario - Excite Bike - Goonies - Soccer - TKO Boxing -RP2C04-0004: +RP2c05-004: - Clu Clu Land - Excite Bike (Japan) - Ice Climber - Ice Climber Dual (Japan) - Super Mario Bros. -RCP2C03B: +Rcp2c03b: - Battle City - Duck Hunt - Mahjang @@ -252,47 +256,47 @@ RC2C05-04: VSUNIENTRY VSUniGames[] = { - { "Baseball", 0x691d4200ea42be45LL, 99, 2, RP2C04_0001, 0 }, - { "Battle City", 0x8540949d74c4d0ebLL, 99, 2, RP2C04_0001, 0 }, - { "Battle City(Bootleg)", 0x8093cbe7137ac031LL, 99, 2, RP2C04_0001, 0 }, + { "Baseball", 0x691d4200ea42be45ULL, 99, 2, GIPPU_RP2C04_0001, 0, 0, EGIVS_NORMAL }, + { "Battle City", 0x8540949d74c4d0ebULL, 99, 2, GIPPU_RP2C04_0001, 0, 0, EGIVS_NORMAL }, + { "Battle City(Bootleg)", 0x8093cbe7137ac031ULL, 99, 2, GIPPU_RP2C04_0001, 0, 0, EGIVS_NORMAL }, - { "Clu Clu Land", 0x1b8123218f62b1eeLL, 99, 2, RP2C04_0004, IOPTION_SWAPDIRAB }, - { "Dr Mario", 0xe1af09c477dc0081LL, 1, 0, RP2C04_0003, IOPTION_SWAPDIRAB }, - { "Duck Hunt", 0x47735d1e5f1205bbULL, 99, 2, RCP2C03B, IOPTION_GUN }, - { "Excitebike", 0x3dcd1401bcafde77LL, 99, 2, RP2C04_0003, 0 }, - { "Excitebike (J)", 0x7ea51c9d007375f0LL, 99, 2, RP2C04_0004, 0 }, - { "Freedom Force", 0xed96436bd1b5e688LL, 4, 0, RP2C04_0001, IOPTION_GUN }, /* Wrong color in game select screen? */ - { "Stroke and Match Golf", 0x612325606e82bc66LL, 99, 2, RP2C04_0002, IOPTION_SWAPDIRAB | IOPTION_PREDIP, 0x01 }, + { "Clu Clu Land", 0x1b8123218f62b1eeULL, 99, 2, GIPPU_RP2C04_0004, VS_OPTION_SWAPDIRAB, 0, EGIVS_NORMAL }, + { "Dr Mario", 0xe1af09c477dc0081ULL, 1, 0, GIPPU_RP2C04_0003, VS_OPTION_SWAPDIRAB, 0, EGIVS_NORMAL }, + { "Duck Hunt", 0x47735d1e5f1205bbULL, 99, 2, GIPPU_RC2C03B, VS_OPTION_GUN, 0, EGIVS_NORMAL }, + { "Excitebike", 0x3dcd1401bcafde77ULL, 99, 2, GIPPU_RP2C04_0003, 0, 0, EGIVS_NORMAL }, + { "Excitebike (J)", 0x7ea51c9d007375f0ULL, 99, 2, GIPPU_RP2C04_0004, 0, 0, EGIVS_NORMAL }, + { "Freedom Force", 0xed96436bd1b5e688ULL, 4, 0, GIPPU_RP2C04_0001, VS_OPTION_GUN, 0, EGIVS_NORMAL }, /* Wrong color in game select screen? */ + { "Stroke and Match Golf", 0x612325606e82bc66ULL, 99, 2, GIPPU_RP2C04_0002, VS_OPTION_SWAPDIRAB | VS_OPTION_PREDIP, 0x01, EGIVS_NORMAL }, - { "Goonies", 0xb4032d694e1d2733LL, 151, 1, RP2C04_0003, 0 }, - { "Gradius", 0x50687ae63bdad976LL, 151, 1, RP2C04_0001, IOPTION_SWAPDIRAB }, - { "Gumshoe", 0x87161f8ee37758d3ULL, 99, 2, RC2C05_03, IOPTION_GUN }, - { "Gumshoe", 0xb8500780bf69ce29ULL, 99, 2, RC2C05_03, IOPTION_GUN }, - { "Hogan's Alley", 0xd78b7f0bb621fb45LL, 99, 2, RP2C04_0001, IOPTION_GUN }, - { "Ice Climber", 0xd21e999513435e2aLL, 99, 2, RP2C04_0004, IOPTION_SWAPDIRAB }, - { "Ladies Golf", 0x781b24be57ef6785LL, 99, 2, RP2C04_0002, IOPTION_SWAPDIRAB | IOPTION_PREDIP, 0x1 }, + { "Goonies", 0xb4032d694e1d2733ULL, 75, 2, GIPPU_RP2C04_0003, 0, 0, EGIVS_NORMAL }, + { "Gradius", 0x47b22dfda574b968ULL, 75, 2, GIPPU_RP2C04_0001, VS_OPTION_SWAPDIRAB, 0, EGIVS_NORMAL }, + { "Gumshoe", 0x87161f8ee37758d3ULL, 99, 2, GIPPU_RC2C05_03, VS_OPTION_GUN, 0, EGIVS_NORMAL }, + { "Gumshoe", 0xb8500780bf69ce29ULL, 99, 2, GIPPU_RC2C05_03, VS_OPTION_GUN, 0, EGIVS_NORMAL }, + { "Hogan's Alley", 0xd78b7f0bb621fb45ULL, 99, 2, GIPPU_RP2C04_0001, VS_OPTION_GUN, 0, EGIVS_NORMAL }, + { "Ice Climber", 0xd21e999513435e2aULL, 99, 2, GIPPU_RP2C04_0004, VS_OPTION_SWAPDIRAB, 0, EGIVS_NORMAL }, + { "Ladies Golf", 0x781b24be57ef6785ULL, 99, 2, GIPPU_RP2C04_0002, VS_OPTION_SWAPDIRAB | VS_OPTION_PREDIP, 0x1, EGIVS_NORMAL }, - { "Mach Rider", 0x015672618af06441LL, 99, 2, RP2C04_0002, 0 }, - { "Mach Rider (J)", 0xa625afb399811a8aLL, 99, 2, RP2C04_0001, 0 }, - { "Mighty Bomb Jack", 0xe6a89f4873fac37bULL, 0, 2, RC2C05_02, 0 }, - { "Ninja Jajamaru Kun", 0xb26a2c31474099c0ULL, 99, 2, RC2C05_01, IOPTION_SWAPDIRAB }, - { "Pinball", 0xc5f49d3de7f2e9b8LL, 99, 2, RP2C04_0001, IOPTION_PREDIP, 0x01 }, - { "Pinball (J)", 0x66ab1a3828cc901cULL, 99, 2, RCP2C03B, IOPTION_PREDIP, 0x1 }, - { "Platoon", 0x160f237351c19f1fLL, 68, 1, RP2C04_0001, 0 }, - { "RBI Baseball", 0x6a02d345812938afLL, 4, 1, RP2C04_0001, IOPTION_SWAPDIRAB }, - { "Soccer", 0xd4e7a9058780eda3LL, 99, 2, RP2C04_0003, IOPTION_SWAPDIRAB }, - { "Star Luster", 0x8360e134b316d94cULL, 99, 2, RCP2C03B, 0 }, - { "Stroke and Match Golf (J)", 0x869bb83e02509747ULL, 99, 2, RCP2C03B, IOPTION_SWAPDIRAB | IOPTION_PREDIP, 0x1 }, - { "Super Sky Kid", 0x78d04c1dd4ec0101ULL, 4, 1, RCP2C03B, IOPTION_SWAPDIRAB | IOPTION_PREDIP, 0x20 }, + { "Mach Rider", 0x015672618af06441ULL, 99, 2, GIPPU_RP2C04_0002, 0, 0, EGIVS_NORMAL }, + { "Mach Rider (J)", 0xa625afb399811a8aULL, 99, 2, GIPPU_RP2C04_0001, 0, 0, EGIVS_NORMAL }, + { "Mighty Bomb Jack", 0xe6a89f4873fac37bULL, 0, 2, GIPPU_RC2C05_02, 0, 0, EGIVS_NORMAL }, + { "Ninja Jajamaru Kun", 0xb26a2c31474099c0ULL, 99, 2, GIPPU_RC2C05_01, VS_OPTION_SWAPDIRAB, 0, EGIVS_NORMAL }, + { "Pinball", 0xc5f49d3def2e9b8ULL, 99, 2, GIPPU_RP2C04_0001, VS_OPTION_PREDIP, 0x01, EGIVS_NORMAL }, + { "Pinball (J)", 0x66ab1a3828cc901cULL, 99, 2, GIPPU_RC2C03B, VS_OPTION_PREDIP, 0x01, EGIVS_NORMAL }, + { "Platoon", 0x160f237351c19f1fULL, 68, 1, GIPPU_RP2C04_0001, 0, 0, EGIVS_NORMAL }, + { "RBI Baseball", 0x6a02d345812938afULL, 4, 1, GIPPU_RP2C04_0001, VS_OPTION_SWAPDIRAB, 0, EGIVS_RBI }, + { "Soccer", 0xd4e7a9058780eda3ULL, 99, 2, GIPPU_RP2C04_0003, VS_OPTION_SWAPDIRAB, 0, EGIVS_NORMAL }, + { "Star Luster", 0x8360e134b316d94cULL, 99, 2, GIPPU_RC2C03B, 0, 0, EGIVS_NORMAL }, + { "Stroke and Match Golf (J)", 0x869bb83e02509747ULL, 99, 2, GIPPU_RC2C03B, VS_OPTION_SWAPDIRAB | VS_OPTION_PREDIP, 0x01, EGIVS_NORMAL }, + { "Super Sky Kid", 0x78d04c1dd4ec0101ULL, 4, 1, GIPPU_RC2C03B, VS_OPTION_SWAPDIRAB | VS_OPTION_PREDIP, 0x20, EGIVS_NORMAL }, - { "Super Xevious", 0x2d396247cf58f9faLL, 206, 0, RP2C04_0001, 0 }, - { "Tetris", 0x531a5e8eea4ce157ULL, 99, 2, RCP2C03B, IOPTION_PREDIP, 0x20 }, - { "Top Gun", 0xf1dea36e6a7b531dULL, 2, 0, RC2C05_04, 0 }, - { "VS Castlevania", 0x92fd6909c81305b9LL, 2, 1, RP2C04_0002, 0 }, - { "VS Slalom", 0x4889b5a50a623215LL, 0, 1, RP2C04_0002, 0 }, - { "VS Super Mario Bros", 0x39d8cfa788e20b6cLL, 99, 2, RP2C04_0004, 0 }, - { "VS Super Mario Bros [a1]", 0xfc182e5aefbce14dLL, 99, 2, RP2C04_0004, 0 }, - { "VS TKO Boxing", 0x6e1ee06171d8ce3aLL, 4, 1, RP2C04_0003, IOPTION_PREDIP, 0x00 }, + { "Super Xevious", 0x2d396247cf58f9faULL, 206, 0, GIPPU_RP2C04_0001, 0, 0, EGIVS_XEVIOUS }, + { "Tetris", 0x531a5e8eea4ce157ULL, 99, 2, GIPPU_RC2C03B, VS_OPTION_PREDIP, 0x20, EGIVS_NORMAL }, + { "Top Gun", 0xf1dea36e6a7b531dULL, 2, 0, GIPPU_RC2C05_04, 0, 0, EGIVS_NORMAL }, + { "VS Castlevania", 0x92fd6909c81305b9ULL, 2, 1, GIPPU_RP2C04_0002, 0, 0, EGIVS_NORMAL }, + { "VS Slalom", 0x4889b5a50a623215ULL, 0, 1, GIPPU_RP2C04_0002, 0, 0, EGIVS_NORMAL }, + { "VS Super Mario Bros", 0x39d8cfa788e20b6cULL, 99, 2, GIPPU_RP2C04_0004, 0, 0, EGIVS_NORMAL }, + { "VS Super Mario Bros [a1]", 0xfc182e5aefbce14dULL, 99, 2, GIPPU_RP2C04_0004, 0, 0, EGIVS_NORMAL }, + { "VS TKO Boxing", 0x6e1ee06171d8ce3aULL, 4, 1, GIPPU_RP2C04_0003, VS_OPTION_PREDIP, 0x00, EGIVS_TKO }, { 0 } }; @@ -301,37 +305,101 @@ void FCEU_VSUniCheck(uint64 md5partial, int *MapperNo, uint8 *Mirroring) { while (vs->name) { if (md5partial == vs->md5partial) { - if (vs->ppu < RCP2C03B) default_palette_selection = vs->ppu; - else default_palette_selection = 5; - *MapperNo = vs->mapper; - *Mirroring = vs->mirroring; - GameInfo->type = GIT_VSUNI; - GameInfo->cspecial = SIS_VSUNISYSTEM; - GameInfo->inputfc = SIFC_NONE; - curppu = vs->ppu; - curmd5 = md5partial; + int32 tofix = 0; + if (*MapperNo != vs->mapper) { + tofix |= 1; + *MapperNo = vs->mapper; + } + if (*Mirroring != vs->mirroring) { + tofix |= 2; + *Mirroring = vs->mirroring; + } + if (GameInfo->type != GIT_VSUNI) { + tofix |= 4; + GameInfo->type = GIT_VSUNI; + } + if (GameInfo->vs_type != vs->type) { + tofix |= 8; + GameInfo->vs_type = vs->type; + } + if (vs->ppu && (GameInfo->vs_ppu != vs->ppu)) { + tofix |= 16; + GameInfo->vs_ppu = vs->ppu; + } secptr = 0; - + switch (GameInfo->vs_type) { - static int64 tko = 0x6e1ee06171d8ce3aULL, rbi = 0x6a02d345812938afULL; - if (md5partial == tko) - secptr = secdata[0]; - if (md5partial == rbi) - secptr = secdata[1]; + case EGIVS_RBI: secptr = secdata_rbi; break; + case EGIVS_TKO: secptr = secdata_tko; break; + default: secptr = 0; break; } vsdip = 0x0; - if (vs->ioption & IOPTION_PREDIP) { + if (vs->ioption & VS_OPTION_PREDIP) { vsdip = vs->predip; } - if (vs->ioption & IOPTION_GUN) { + if ((vs->ioption & VS_OPTION_GUN) && !head.expansion) { + tofix |= 32; GameInfo->input[0] = SI_ZAPPER; GameInfo->input[1] = SI_NONE; - } else { - GameInfo->input[0] = GameInfo->input[1] = SI_GAMEPAD; + GameInfo->inputfc = SIFC_NONE; } - curvs = vs; + else if (!head.expansion) { + GameInfo->input[0] = GameInfo->input[1] = SI_GAMEPAD; + GameInfo->inputfc = SIFC_NONE; + } + if ((vs->ioption & VS_OPTION_SWAPDIRAB) && !GameInfo->vs_cswitch) { + tofix |= 64; + GameInfo->vs_cswitch = 1; + } + + if (tofix) + { + char tmpStr[128]; + std::string gigastr; + gigastr.reserve(768); + gigastr.assign("The iNES header contains incorrect information. For now, the information will be corrected in RAM. "); + if (tofix & 4) { + snprintf(tmpStr, sizeof(tmpStr), "Game type should be set to Vs. System. "); + gigastr.append(tmpStr); + } + if (tofix & 1) + { + snprintf(tmpStr, sizeof(tmpStr), "The mapper number should be set to %d. ", *MapperNo); + gigastr.append(tmpStr); + } + if (tofix & 2) + { + const char* mstr[3] = { "Horizontal", "Vertical", "Four-screen" }; + snprintf(tmpStr, sizeof(tmpStr), "Mirroring should be set to \"%s\". ", mstr[vs->mirroring & 3]); + gigastr.append(tmpStr); + } + if (tofix & 8) { + const char* mstr[4] = { "Normal", "RBI Baseball protection", "TKO Boxing protection", "Super Xevious protection"}; + snprintf(tmpStr, sizeof(tmpStr), "Vs. System type should be set to \"%s\". ", mstr[vs->type]); + gigastr.append(tmpStr); + } + if (tofix & 16) + { + const char* mstr[10] = { "Default", "RP2C04-0001", "RP2C04-0002", "RP2C04-0003", "RP2C04-0004", "RC2C03B", "RC2C05-01", "RC2C05-02" , "RC2C05-03" , "RC2C05-04" }; + snprintf(tmpStr, sizeof(tmpStr), "Vs. System PPU should be set to \"%s\". ", mstr[vs->ppu]); + gigastr.append(tmpStr); + } + if (tofix & 32) + { + snprintf(tmpStr, sizeof(tmpStr), "The controller type should be set to zapper. "); + gigastr.append(tmpStr); + } + if (tofix & 64) + { + snprintf(tmpStr, sizeof(tmpStr), "The controllers should be swapped. "); + gigastr.append(tmpStr); + } + gigastr.append("\n"); + FCEU_printf("%s", gigastr.c_str()); + } + return; } vs++; @@ -342,7 +410,7 @@ void FCEU_VSUniDraw(uint8 *XBuf) { uint32 *dest; int y, x; - if (!DIPS) return; + if (DIPS_howlong-- <= 0) return; dest = (uint32*)(XBuf + 256 * 12 + 164); for (y = 24; y; y--, dest += (256 - 72) >> 2) { @@ -372,6 +440,8 @@ void FCEU_VSUniDraw(uint8 *XBuf) { SFORMAT FCEUVSUNI_STATEINFO[] = { { &vsdip, 1, "vsdp" }, { &coinon, 1, "vscn" }, + { &coinon2, 1, "vsc2" }, + { &service, 1, "vssv" }, { &VSindex, 1, "vsin" }, { 0 } }; diff --git a/source/fceultra/vsuni.h b/source/fceultra/vsuni.h index 14fb715..700badb 100644 --- a/source/fceultra/vsuni.h +++ b/source/fceultra/vsuni.h @@ -1,7 +1,25 @@ +enum IOPTION { + VS_OPTION_GUN = 0x1, + VS_OPTION_SWAPDIRAB = 0x2, + VS_OPTION_PREDIP = 0x10, +}; + +typedef struct { + const char* name; + uint64 md5partial; + int mapper; + int mirroring; + EGIPPU ppu; + int ioption; + int predip; + EGIVS type; +} VSUNIENTRY; + void FCEU_VSUniPower(void); void FCEU_VSUniCheck(uint64 md5partial, int *, uint8 *); void FCEU_VSUniDraw(uint8 *XBuf); void FCEU_VSUniToggleDIP(int); /* For movies and netplay */ -void FCEU_VSUniCoin(void); +void FCEU_VSUniCoin(uint8 slot); +void FCEU_VSUniService(); void FCEU_VSUniSwap(uint8 *j0, uint8 *j1); diff --git a/source/fceultra/x6502.cpp b/source/fceultra/x6502.cpp index fa6c98b..2358b1d 100644 --- a/source/fceultra/x6502.cpp +++ b/source/fceultra/x6502.cpp @@ -44,49 +44,182 @@ void (*MapIRQHook)(int a); if(!overclocking) soundtimestamp+=__x; \ } +static X6502_MemHook* readMemHook = nullptr; +static X6502_MemHook* writeMemHook = nullptr; +static X6502_MemHook* execMemHook = nullptr; + +void X6502_MemHook::Add(enum X6502_MemHook::Type type, void (*func)(unsigned int address, unsigned int value, void *userData), void *userData ) +{ + X6502_MemHook** hookStart = nullptr; + + switch (type) + { + case Read: + hookStart = &readMemHook; + break; + case Write: + hookStart = &writeMemHook; + break; + case Exec: + hookStart = &execMemHook; + break; + } + if (hookStart == nullptr) + { + return; + } + + if (*hookStart != nullptr) + { + X6502_MemHook* hook = *hookStart; + + while (hook->next != nullptr) + { + if ((hook->func == func) && (hook->userData == userData)) + { + // Already registered + //printf("LUA MemHook Already Registered\n"); + hook->refCount++; + return; + } + hook = hook->next; + } + X6502_MemHook* newHook = new X6502_MemHook(); + newHook->type = type; + newHook->func = func; + newHook->userData = userData; + newHook->refCount = 1; + hook->next = newHook; + } + else + { + X6502_MemHook* newHook = new X6502_MemHook(); + newHook->type = type; + newHook->func = func; + newHook->userData = userData; + newHook->refCount = 1; + *hookStart = newHook; + } + //printf("LUA MemHook Added: %p\n", func); +} + +void X6502_MemHook::Remove(enum X6502_MemHook::Type type, void (*func)(unsigned int address, unsigned int value, void *userData), void *userData ) +{ + X6502_MemHook** hookStart = nullptr; + + switch (type) + { + case Read: + hookStart = &readMemHook; + break; + case Write: + hookStart = &writeMemHook; + break; + case Exec: + hookStart = &execMemHook; + break; + } + if (hookStart == nullptr) + { + return; + } + + if (*hookStart != nullptr) + { + X6502_MemHook* hook = *hookStart; + X6502_MemHook* prev = nullptr; + + while (hook != nullptr) + { + if ((hook->func == func) && (hook->userData == userData)) + { + hook->refCount--; + + if (hook->refCount <= 0) + { + if (prev != nullptr) + { + prev->next = hook->next; + } + else + { + *hookStart = hook->next; + } + delete hook; + //printf("LUA MemHook Removed: %p\n", func); + } + return; + } + prev = hook; + hook = hook->next; + } + } +} + //normal memory read static INLINE uint8 RdMem(unsigned int A) { - return(_DB=ARead[A](A)); + _DB=ARead[A](A); + if (readMemHook) + { + readMemHook->call(A, _DB); + } + return(_DB); } //normal memory write static INLINE void WrMem(unsigned int A, uint8 V) { BWrite[A](A,V); - #ifdef _S9XLUA_H - CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE); - #endif + if (writeMemHook) + { + writeMemHook->call(A, V); + } + _DB = V; } static INLINE uint8 RdRAM(unsigned int A) { + _DB=ARead[A](A); + if (readMemHook) + { + readMemHook->call(A, _DB); + } //bbit edited: this was changed so cheat substituion would work - return(_DB=ARead[A](A)); // return(_DB=RAM[A]); + return(_DB); } static INLINE void WrRAM(unsigned int A, uint8 V) { RAM[A]=V; - #ifdef _S9XLUA_H - CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE); - #endif + if (writeMemHook) + { + writeMemHook->call(A, V); + } + _DB = V; } uint8 X6502_DMR(uint32 A) { ADDCYC(1); - return(X.DB=ARead[A](A)); + _DB=ARead[A](A); + if (readMemHook) + { + readMemHook->call(A, _DB); + } + return(_DB); } void X6502_DMW(uint32 A, uint8 V) { ADDCYC(1); BWrite[A](A,V); - #ifdef _S9XLUA_H - CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE); - #endif + if (writeMemHook) + { + writeMemHook->call(A, V); + } + _DB = V; } #define PUSH(V) \ @@ -318,8 +451,8 @@ static uint8 ZNTable[256]; #define LD_ZP(op) {uint8 A; uint8 x; GetZP(A); x=RdRAM(A); op; break;} #define LD_ZPX(op) {uint8 A; uint8 x; GetZPI(A,_X); x=RdRAM(A); op; break;} #define LD_ZPY(op) {uint8 A; uint8 x; GetZPI(A,_Y); x=RdRAM(A); op; break;} -#define LD_AB(op) {unsigned int A; uint8 x; GetAB(A); x=RdMem(A); op; break; } -#define LD_ABI(reg,op) {unsigned int A; uint8 x; GetABIRD(A,reg); x=RdMem(A); op; break;} +#define LD_AB(op) {unsigned int A; FCEU_MAYBE_UNUSED uint8 x; GetAB(A); x=RdMem(A); op; break; } +#define LD_ABI(reg,op) {unsigned int A; FCEU_MAYBE_UNUSED uint8 x; GetABIRD(A,reg); x=RdMem(A); op; break;} #define LD_ABX(op) LD_ABI(_X,op) #define LD_ABY(op) LD_ABI(_Y,op) #define LD_IX(op) {unsigned int A; uint8 x; GetIX(A); x=RdMem(A); op; break;} @@ -505,9 +638,10 @@ extern int test; test++; if (!overclocking) FCEU_SoundCPUHook(temp); - #ifdef _S9XLUA_H - CallRegisteredLuaMemHook(_PC, 1, 0, LUAMEMHOOK_EXEC); - #endif + if (execMemHook) + { + execMemHook->call(_PC, 0); + } _PC++; switch(b1) { diff --git a/source/fceultra/x6502.h b/source/fceultra/x6502.h index 460683d..779fa6d 100644 --- a/source/fceultra/x6502.h +++ b/source/fceultra/x6502.h @@ -92,5 +92,29 @@ void X6502_IRQEnd(int w); int X6502_GetOpcodeCycles( int op ); +class X6502_MemHook +{ + public: + enum Type { Read = 0, Write, Exec } type; + + static void Add(enum Type type, void (*func)(unsigned int address, unsigned int value, void *userData), void *userData = nullptr ); + static void Remove(enum Type type, void (*func)(unsigned int address, unsigned int value, void *userData), void *userData = nullptr ); + + inline void call( unsigned int address, unsigned int value ) + { + func(address, value, userData); + + if (next != nullptr) + { + next->call(address, value); + } + } + private: + void (*func)(unsigned int address, unsigned int value, void *userData) = nullptr; + void *userData = nullptr; + X6502_MemHook* next = nullptr; + int refCount = 0; +}; + #define _X6502H #endif diff --git a/source/fceuram.cpp b/source/fceuram.cpp index ed35696..73236e1 100644 --- a/source/fceuram.cpp +++ b/source/fceuram.cpp @@ -31,19 +31,17 @@ static u32 WiiFCEU_GameSave(CartInfo *LocalHWInfo, int operation) { u32 offset = 0; - if(LocalHWInfo->battery && LocalHWInfo->SaveGame[0]) + if(LocalHWInfo->battery && !LocalHWInfo->SaveGame.empty()) { - int x; - - for(x=0;x<4;x++) + for (size_t x = 0; x < LocalHWInfo->SaveGame.size(); x++) { - if(LocalHWInfo->SaveGame[x]) + if(LocalHWInfo->SaveGame[x].bufptr) { if(operation == 0) // save to file - memcpy(savebuffer+offset, LocalHWInfo->SaveGame[x], LocalHWInfo->SaveGameLen[x]); + memcpy(savebuffer+offset, LocalHWInfo->SaveGame[x].bufptr, LocalHWInfo->SaveGame[x].buflen); else // load from file - memcpy(LocalHWInfo->SaveGame[x], savebuffer+offset, LocalHWInfo->SaveGameLen[x]); - offset += LocalHWInfo->SaveGameLen[x]; + memcpy(LocalHWInfo->SaveGame[x].bufptr, savebuffer+offset, LocalHWInfo->SaveGame[x].buflen); + offset += LocalHWInfo->SaveGame[x].buflen; } } } diff --git a/source/fceusupport.h b/source/fceusupport.h index 358a42e..13b5c3b 100644 --- a/source/fceusupport.h +++ b/source/fceusupport.h @@ -37,7 +37,7 @@ extern unsigned char * nesrom; int GetFCEUTiming(); void UpdateDendy(); void RebuildSubCheats(void); -int AddCheatEntry(const char *name, uint32 addr, uint8 val, int compare, int status, int type); +void AddCheatEntry(const char *name, uint32 addr, uint8 val, int compare, int status, int type); extern int FDSLoad(const char *name, FCEUFILE *fp); extern int iNESLoad(const char *name, FCEUFILE *fp, int o); diff --git a/source/menu.cpp b/source/menu.cpp index b1e3d9a..e9dc86a 100644 --- a/source/menu.cpp +++ b/source/menu.cpp @@ -2344,7 +2344,7 @@ static int MenuGameCheats() int ret; u16 i = 0; OptionList options; - char *name; // cheat name + std::string name; // cheat name int status; // cheat status (on/off) for(i=0; i < numcheats; i++) @@ -2352,7 +2352,7 @@ static int MenuGameCheats() if(!FCEUI_GetCheat(i,&name,NULL,NULL,NULL,&status,NULL)) break; - snprintf (options.name[i], 100, "%s", name); + snprintf (options.name[i], 100, "%s", name.c_str()); sprintf (options.value[i], status ? "On" : "Off"); }