diff --git a/EmuFramework/include/emuframework/Option.hh b/EmuFramework/include/emuframework/Option.hh index 05d23921d..4fdcb937a 100644 --- a/EmuFramework/include/emuframework/Option.hh +++ b/EmuFramework/include/emuframework/Option.hh @@ -97,14 +97,14 @@ inline bool readOptionValue(Readable auto &io, T &output) template inline bool readOptionValue(Readable auto &io, Prop &output) { - using T = Prop::Type; + using T = Prop::SerializedType; auto bytesToRead = io.size(); if(bytesToRead != sizeof(T)) { logMsg("skipping %zu byte option value, expected %zu bytes", bytesToRead, sizeof(T)); return false; } - return output.set(io.template get()); + return output.unserialize(io.template get()); } template @@ -176,7 +176,7 @@ inline void writeOptionValueIfNotDefault(Writable auto &io, PropertyOption auto { if(p.isDefault()) return; - writeOptionValue(io, p.uid, p.value()); + writeOptionValue(io, p.uid, p.serialize()); } inline void writeStringOptionValueAllowEmpty(Writable auto &io, uint16_t key, std::string_view s) diff --git a/NES.emu/src/fceu/asm.cpp b/NES.emu/src/fceu/asm.cpp index 0aa2a61ee..533bb0b16 100644 --- a/NES.emu/src/fceu/asm.cpp +++ b/NES.emu/src/fceu/asm.cpp @@ -3,6 +3,7 @@ #include "types.h" #include "utils/xstring.h" +#include "utils/StringBuilder.h" #include "debug.h" #include "asm.h" #include "x6502.h" @@ -256,8 +257,11 @@ int Assemble(unsigned char *output, int addr, char *str) { ///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}; + static char str[64]={0}; + const char *chr; + char indReg; uint16 tmp,tmp2; + StringBuilder sb(str); //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) @@ -286,7 +290,7 @@ char *Disassemble(int addr, uint8 *opcode) { #ifdef BRK_3BYTE_HACK case 0x00: - sprintf(str,"BRK %02X %02X", opcode[1], opcode[2]); + sb << "BRK " << sb_hex(opcode[1], 2) << ' ' << sb_hex(opcode[2], 2); break; #else case 0x00: strcpy(str,"BRK"); break; @@ -323,207 +327,228 @@ char *Disassemble(int addr, uint8 *opcode) { 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; + case 0x01: chr = "ORA"; goto _indirectx; + case 0x21: chr = "AND"; goto _indirectx; + case 0x41: chr = "EOR"; goto _indirectx; + case 0x61: chr = "ADC"; goto _indirectx; + case 0x81: chr = "STA"; goto _indirectx; + case 0xA1: chr = "LDA"; goto _indirectx; + case 0xC1: chr = "CMP"; goto _indirectx; + case 0xE1: chr = "SBC"; goto _indirectx; _indirectx: indirectX(tmp); - sprintf(str,"%s ($%02X,X) @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); + indReg = 'X'; + + _indirect: + sb << chr << " (" << sb_addr(opcode[1], 2) << ',' << indReg << ") @ " << sb_addr(tmp) << " = " << sb_lit(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; + case 0x05: chr = "ORA"; goto _zeropage; + case 0x06: chr = "ASL"; goto _zeropage; + case 0x24: chr = "BIT"; goto _zeropage; + case 0x25: chr = "AND"; goto _zeropage; + case 0x26: chr = "ROL"; goto _zeropage; + case 0x45: chr = "EOR"; goto _zeropage; + case 0x46: chr = "LSR"; goto _zeropage; + case 0x65: chr = "ADC"; goto _zeropage; + case 0x66: chr = "ROR"; goto _zeropage; + case 0x84: chr = "STY"; goto _zeropage; + case 0x85: chr = "STA"; goto _zeropage; + case 0x86: chr = "STX"; goto _zeropage; + case 0xA4: chr = "LDY"; goto _zeropage; + case 0xA5: chr = "LDA"; goto _zeropage; + case 0xA6: chr = "LDX"; goto _zeropage; + case 0xC4: chr = "CPY"; goto _zeropage; + case 0xC5: chr = "CMP"; goto _zeropage; + case 0xC6: chr = "DEC"; goto _zeropage; + case 0xE4: chr = "CPX"; goto _zeropage; + case 0xE5: chr = "SBC"; goto _zeropage; + case 0xE6: 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])); + sb << chr << ' ' << sb_addr(opcode[1], 2) << " = " << sb_lit(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; + case 0x09: chr = "ORA"; goto _immediate; + case 0x29: chr = "AND"; goto _immediate; + case 0x49: chr = "EOR"; goto _immediate; + case 0x69: chr = "ADC"; goto _immediate; + //case 0x89: chr = "STA"; goto _immediate; //baka, no STA #imm!! + case 0xA0: chr = "LDY"; goto _immediate; + case 0xA2: chr = "LDX"; goto _immediate; + case 0xA9: chr = "LDA"; goto _immediate; + case 0xC0: chr = "CPY"; goto _immediate; + case 0xC9: chr = "CMP"; goto _immediate; + case 0xE0: chr = "CPX"; goto _immediate; + case 0xE9: chr = "SBC"; goto _immediate; _immediate: - sprintf(str,"%s #$%02X", chr,opcode[1]); + sb << chr << ' ' << sb_lit(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; + case 0x0D: chr = "ORA"; goto _absolute; + case 0x0E: chr = "ASL"; goto _absolute; + case 0x2C: chr = "BIT"; goto _absolute; + case 0x2D: chr = "AND"; goto _absolute; + case 0x2E: chr = "ROL"; goto _absolute; + case 0x4D: chr = "EOR"; goto _absolute; + case 0x4E: chr = "LSR"; goto _absolute; + case 0x6D: chr = "ADC"; goto _absolute; + case 0x6E: chr = "ROR"; goto _absolute; + case 0x8C: chr = "STY"; goto _absolute; + case 0x8D: chr = "STA"; goto _absolute; + case 0x8E: chr = "STX"; goto _absolute; + case 0xAC: chr = "LDY"; goto _absolute; + case 0xAD: chr = "LDA"; goto _absolute; + case 0xAE: chr = "LDX"; goto _absolute; + case 0xCC: chr = "CPY"; goto _absolute; + case 0xCD: chr = "CMP"; goto _absolute; + case 0xCE: chr = "DEC"; goto _absolute; + case 0xEC: chr = "CPX"; goto _absolute; + case 0xED: chr = "SBC"; goto _absolute; + case 0xEE: chr = "INC"; goto _absolute; _absolute: absolute(tmp); - sprintf(str,"%s $%04X = #$%02X", chr,tmp,GetMem(tmp)); + + sb << chr << ' ' << sb_addr(tmp) << " = " << sb_lit(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; + case 0x10: chr = "BPL"; goto _branch; + case 0x30: chr = "BMI"; goto _branch; + case 0x50: chr = "BVC"; goto _branch; + case 0x70: chr = "BVS"; goto _branch; + case 0x90: chr = "BCC"; goto _branch; + case 0xB0: chr = "BCS"; goto _branch; + case 0xD0: chr = "BNE"; goto _branch; + case 0xF0: chr = "BEQ"; goto _branch; _branch: relative(tmp); - sprintf(str,"%s $%04X", chr,tmp); + + sb << chr << ' ' << sb_addr(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; + case 0x11: chr = "ORA"; goto _indirecty; + case 0x31: chr = "AND"; goto _indirecty; + case 0x51: chr = "EOR"; goto _indirecty; + case 0x71: chr = "ADC"; goto _indirecty; + case 0x91: chr = "STA"; goto _indirecty; + case 0xB1: chr = "LDA"; goto _indirecty; + case 0xD1: chr = "CMP"; goto _indirecty; + case 0xF1: chr = "SBC"; goto _indirecty; _indirecty: indirectY(tmp); - sprintf(str,"%s ($%02X),Y @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); - break; + indReg = 'Y'; + + goto _indirect; //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; + case 0x15: chr = "ORA"; goto _zeropagex; + case 0x16: chr = "ASL"; goto _zeropagex; + case 0x35: chr = "AND"; goto _zeropagex; + case 0x36: chr = "ROL"; goto _zeropagex; + case 0x55: chr = "EOR"; goto _zeropagex; + case 0x56: chr = "LSR"; goto _zeropagex; + case 0x75: chr = "ADC"; goto _zeropagex; + case 0x76: chr = "ROR"; goto _zeropagex; + case 0x94: chr = "STY"; goto _zeropagex; + case 0x95: chr = "STA"; goto _zeropagex; + case 0xB4: chr = "LDY"; goto _zeropagex; + case 0xB5: chr = "LDA"; goto _zeropagex; + case 0xD5: chr = "CMP"; goto _zeropagex; + case 0xD6: chr = "DEC"; goto _zeropagex; + case 0xF5: chr = "SBC"; goto _zeropagex; + case 0xF6: chr = "INC"; goto _zeropagex; _zeropagex: - zpIndex(tmp,RX); + zpIndex(tmp, RX); + indReg = 'X'; + + _indexed: // ################################## Start of SP CODE ########################### // Change width to %04X // don't! - sprintf(str,"%s $%02X,X @ $%04X = #$%02X", chr,opcode[1],tmp,GetMem(tmp)); + sb << chr << ' ' << sb_addr(opcode[1], 2) << ',' << indReg << " @ " << sb_addr(tmp) << " = " << sb_lit(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; + case 0x19: chr = "ORA"; goto _absolutey; + case 0x39: chr = "AND"; goto _absolutey; + case 0x59: chr = "EOR"; goto _absolutey; + case 0x79: chr = "ADC"; goto _absolutey; + case 0x99: chr = "STA"; goto _absolutey; + case 0xB9: chr = "LDA"; goto _absolutey; + case 0xBE: chr = "LDX"; goto _absolutey; + case 0xD9: chr = "CMP"; goto _absolutey; + case 0xF9: chr = "SBC"; goto _absolutey; _absolutey: absolute(tmp); - tmp2=(tmp+RY); - sprintf(str,"%s $%04X,Y @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)); + tmp2 = (tmp + RY); + indReg = 'Y'; + + _absindexed: + sb << chr << ' ' << sb_addr(tmp) << ',' << indReg << " @ " << sb_addr(tmp2) << " = " << sb_lit(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; + case 0x1D: chr = "ORA"; goto _absolutex; + case 0x1E: chr = "ASL"; goto _absolutex; + case 0x3D: chr = "AND"; goto _absolutex; + case 0x3E: chr = "ROL"; goto _absolutex; + case 0x5D: chr = "EOR"; goto _absolutex; + case 0x5E: chr = "LSR"; goto _absolutex; + case 0x7D: chr = "ADC"; goto _absolutex; + case 0x7E: chr = "ROR"; goto _absolutex; + case 0x9D: chr = "STA"; goto _absolutex; + case 0xBC: chr = "LDY"; goto _absolutex; + case 0xBD: chr = "LDA"; goto _absolutex; + case 0xDD: chr = "CMP"; goto _absolutex; + case 0xDE: chr = "DEC"; goto _absolutex; + case 0xFD: chr = "SBC"; goto _absolutex; + case 0xFE: chr = "INC"; goto _absolutex; _absolutex: absolute(tmp); - tmp2=(tmp+RX); - sprintf(str,"%s $%04X,X @ $%04X = #$%02X", chr,tmp,tmp2,GetMem(tmp2)); - break; + tmp2 = (tmp + RX); + indReg = 'X'; + + goto _absindexed; //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; + case 0x20: chr = "JSR"; goto _jump; + case 0x4C: chr = "JMP"; goto _jump; _jump: absolute(tmp); - sprintf(str,"%s $%04X", chr,tmp); + + sb << chr << ' ' << sb_addr(tmp); + + break; + + case 0x6C: + absolute(tmp); + + sb << "JMP (" << sb_addr(tmp); + sb << ") = " << sb_addr(GetMem(tmp) | GetMem(tmp + 1) << 8); + break; //Zero Page,Y - case 0x96: strcpy(chr,"STX"); goto _zeropagey; - case 0xB6: strcpy(chr,"LDX"); goto _zeropagey; + case 0x96: chr = "STX"; goto _zeropagey; + case 0xB6: 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; + zpIndex(tmp, RY); + indReg = 'Y'; - //UNDEFINED - default: strcpy(str,"ERROR"); break; + goto _indexed; + //UNDEFINED + default: strcpy(str, "ERROR"); break; } - return str; } diff --git a/NES.emu/src/fceu/boards/413.cpp b/NES.emu/src/fceu/boards/413.cpp new file mode 100644 index 000000000..3eb25cfa4 --- /dev/null +++ b/NES.emu/src/fceu/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/NES.emu/src/fceu/boards/coolgirl.cpp b/NES.emu/src/fceu/boards/coolgirl.cpp index 3e24994fb..e6187efee 100644 --- a/NES.emu/src/fceu/boards/coolgirl.cpp +++ b/NES.emu/src/fceu/boards/coolgirl.cpp @@ -227,7 +227,7 @@ static uint8 flash_buffer_v[10]; static uint8 cfi_mode = 0; // Micron 4-gbit memory CFI data -const uint8 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, @@ -247,7 +247,7 @@ const uint8 cfi_data[] = #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(char* bitsstr, int* bits) +static inline uint8 string_to_bits(const char* bitsstr, int* bits) { uint8 bit1, bit2, count = 0; for (int i = 0; i < 32; i++) @@ -298,7 +298,7 @@ static inline uint8 string_to_bits(char* bitsstr, int* bits) return count; } -static inline uint32 get_bits(uint32 V, char* bitsstr) +static inline uint32 get_bits(const uint32 V, const char* bitsstr) { uint32 result = 0; int bits[32]; @@ -311,7 +311,7 @@ static inline uint32 get_bits(uint32 V, char* bitsstr) return result; } -static inline uint32 set_bits(uint32 V, char* bitsstr, uint32 new_bits) +static inline uint32 set_bits(uint32 V, const char* bitsstr, const uint32 new_bits) { int bits[32]; uint8 count = string_to_bits(bitsstr, bits); diff --git a/NES.emu/src/fceu/driver.h b/NES.emu/src/fceu/driver.h index 47cff1a36..318ff76fa 100644 --- a/NES.emu/src/fceu/driver.h +++ b/NES.emu/src/fceu/driver.h @@ -285,6 +285,8 @@ void FCEUI_SetEmulationPaused(int val); 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(); @@ -375,7 +377,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/NES.emu/src/fceu/emufile.h b/NES.emu/src/fceu/emufile.h index 623f6c214..b5adab78a 100644 --- a/NES.emu/src/fceu/emufile.h +++ b/NES.emu/src/fceu/emufile.h @@ -177,7 +177,7 @@ class EMUFILE_MEMORY : public EMUFILE { va_end(argptr); va_start(argptr, format); - vsprintf(tempbuf,format,argptr); + vsnprintf(tempbuf,amt+1,format,argptr); fwrite(tempbuf,amt); delete[] tempbuf; diff --git a/NES.emu/src/fceu/fceu.cpp b/NES.emu/src/fceu/fceu.cpp index 0965c5204..dfe716d17 100644 --- a/NES.emu/src/fceu/fceu.cpp +++ b/NES.emu/src/fceu/fceu.cpp @@ -254,9 +254,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) { } @@ -788,7 +792,7 @@ void FCEUI_Emulate(uint8 **pXBuf, int32 **SoundBuf, int32 *SoundBufSize, int ski RefreshThrottleFPS(); } #endif - if (EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER) ) + if (EmulationPaused & (EMULATIONPAUSED_PAUSED | EMULATIONPAUSED_TIMER | EMULATIONPAUSED_NETPLAY) ) { // emulator is paused memcpy(XBuf, XBackBuf, 256*256); @@ -1294,6 +1298,23 @@ 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; void UpdateAutosave(void) { diff --git a/NES.emu/src/fceu/fceu.h b/NES.emu/src/fceu/fceu.h index b35bf4abc..e74d03c77 100644 --- a/NES.emu/src/fceu/fceu.h +++ b/NES.emu/src/fceu/fceu.h @@ -184,6 +184,7 @@ extern uint8 vsdip; #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/NES.emu/src/fceu/fceulua.h b/NES.emu/src/fceu/fceulua.h index 2a82d2df3..0222ee2a4 100644 --- a/NES.emu/src/fceu/fceulua.h +++ b/NES.emu/src/fceu/fceulua.h @@ -73,7 +73,7 @@ void FCEU_LuaReadZapper(const uint32* mouse_in, uint32* mouse_out); uint8 FCEU_LuaReadJoypad(int,uint8); // HACK - Function needs controller input int FCEU_LuaSpeed(); int FCEU_LuaFrameskip(); -int FCEU_LuaRerecordCountSkip(); +bool FCEU_LuaRerecordCountSkip(); void FCEU_LuaGui(uint8 *XBuf); void FCEU_LuaUpdatePalette(); diff --git a/NES.emu/src/fceu/fds.cpp b/NES.emu/src/fceu/fds.cpp index c66b4b618..013d0d1dd 100644 --- a/NES.emu/src/fceu/fds.cpp +++ b/NES.emu/src/fceu/fds.cpp @@ -864,8 +864,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/NES.emu/src/fceu/file.cpp b/NES.emu/src/fceu/file.cpp index 9f6594d3f..b132168a9 100644 --- a/NES.emu/src/fceu/file.cpp +++ b/NES.emu/src/fceu/file.cpp @@ -148,7 +148,7 @@ void ApplyIPS(FILE *ips, FCEUFILE* fp) 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; } @@ -452,11 +452,11 @@ std::string GetMfn() //Retrieves the movie filename from curMovieFilename (for a { std::string movieFilenamePart; if (!curMovieFilename.empty()) - { + { char drv[PATH_MAX], dir[PATH_MAX], name[PATH_MAX], ext[PATH_MAX]; splitpath(curMovieFilename.c_str(),drv,dir,name,ext); movieFilenamePart = std::string(".") + name; - } + } return movieFilenamePart; } @@ -621,9 +621,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; @@ -647,19 +647,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); } } } @@ -668,46 +668,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: @@ -725,52 +725,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/NES.emu/src/fceu/filter.cpp b/NES.emu/src/fceu/filter.cpp index 650f8c43a..d0e7a6251 100644 --- a/NES.emu/src/fceu/filter.cpp +++ b/NES.emu/src/fceu/filter.cpp @@ -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/NES.emu/src/fceu/ines.cpp b/NES.emu/src/fceu/ines.cpp index ae6f23440..592427deb 100644 --- a/NES.emu/src/fceu/ines.cpp +++ b/NES.emu/src/fceu/ines.cpp @@ -49,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,6 +59,7 @@ uint8 Mirroring = 0; uint8 MirroringAs2bits = 0; uint32 ROM_size = 0; uint32 VROM_size = 0; +uint32 MiscROM_size = 0; char LoadedRomFName[4096]; //mbg merge 7/17/06 added char LoadedRomFNamePatchToUse[4096]; @@ -475,21 +477,33 @@ static void CheckHInfo(uint64 partialmd5) { 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()); } } @@ -796,6 +810,7 @@ BMAPPINGLocal bmap[] = { {"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 }, {"KONAMI QTAi Board", 547, QTAi_Init }, @@ -821,9 +836,12 @@ int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { 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(); @@ -961,6 +979,7 @@ int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { if (head.ROM_type & 4) { /* Trainer */ trainerpoo = (uint8*)FCEU_gmalloc(512); FCEU_fread(trainerpoo, 512, 1, fp); + filesize -= 512; } ResetCartMapping(); @@ -973,6 +992,15 @@ int iNESLoad(const char *name, FCEUFILE *fp, int OverwriteVidMode) { 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_bytes); @@ -1023,6 +1051,7 @@ 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(); diff --git a/NES.emu/src/fceu/ines.h b/NES.emu/src/fceu/ines.h index ae0e4ef8b..c881bb192 100644 --- a/NES.emu/src/fceu/ines.h +++ b/NES.emu/src/fceu/ines.h @@ -42,8 +42,10 @@ class TMasterRomInfoParams : public std::map //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 @@ -310,6 +312,7 @@ void Mapper370_Init(CartInfo *); void Mapper380_Init(CartInfo *); void Mapper406_Init(CartInfo *); void Mapper411_Init(CartInfo *); +void Mapper413_Init(CartInfo *); void Mapper414_Init(CartInfo *); void Mapper422_Init(CartInfo *); void Mapper452_Init(CartInfo *); diff --git a/NES.emu/src/fceu/input.cpp b/NES.emu/src/fceu/input.cpp index 3659bd759..dc00b1a3b 100644 --- a/NES.emu/src/fceu/input.cpp +++ b/NES.emu/src/fceu/input.cpp @@ -234,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 @@ -247,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 { @@ -259,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) @@ -421,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) { @@ -438,6 +455,12 @@ void FCEU_UpdateInput(void) if (coinon2) coinon2--; if (service) service--; } + #ifdef __FCEU_QNETWORK_ENABLE__ + if (NetPlayActive()) + { + NetPlayReadInputFrame(joy); + } + #endif if(FCEUnetplay) NetplayUpdate(joy); diff --git a/NES.emu/src/fceu/input/shadow.cpp b/NES.emu/src/fceu/input/shadow.cpp index 85fa44e72..b5628e223 100644 --- a/NES.emu/src/fceu/input/shadow.cpp +++ b/NES.emu/src/fceu/input/shadow.cpp @@ -22,14 +22,7 @@ #include #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/NES.emu/src/fceu/lua-engine.cpp b/NES.emu/src/fceu/lua-engine.cpp index 6ca7a4bb5..c9711d85f 100644 --- a/NES.emu/src/fceu/lua-engine.cpp +++ b/NES.emu/src/fceu/lua-engine.cpp @@ -118,6 +118,19 @@ bool DemandLua() #endif } +static void luaReadMemHook(unsigned int address, unsigned int value, void *userData) +{ + CallRegisteredLuaMemHook(address, 1, value, LUAMEMHOOK_READ); +} +static void luaWriteMemHook(unsigned int address, unsigned int value, void *userData) +{ + CallRegisteredLuaMemHook(address, 1, value, LUAMEMHOOK_WRITE); +} +static void luaExecMemHook(unsigned int address, unsigned int value, void *userData) +{ + CallRegisteredLuaMemHook(address, 1, value, LUAMEMHOOK_EXEC); +} + extern "C" { #include @@ -232,6 +245,7 @@ extern void WinLuaOnStop(intptr_t hDlgAsInt); static lua_State *L; static int luaexiterrorcount = 8; +static int luaCallbackErrorCounter = 0; // Are we running any code right now? static char *luaScriptName = NULL; @@ -649,13 +663,6 @@ static int emu_loadrom(lua_State *L) extern void LoadRecentRom(int slot); LoadRecentRom(0); } - if ( GameInfo ) - { - //printf("Currently Loaded ROM: '%s'\n", GameInfo->filename ); - lua_pushstring(L, GameInfo->filename); - return 1; - } - return 0; #elif defined(__QT_DRIVER__) const char *nameo2 = luaL_checkstring(L,1); std::string nameo; @@ -671,15 +678,17 @@ static int emu_loadrom(lua_State *L) // //printf("Failed to Load ROM: '%s'\n", nameo ); // reloadLastGame(); //} +#endif + +#if defined(__WIN_DRIVER__) || defined(__QT_DRIVER__) if ( GameInfo ) { //printf("Currently Loaded ROM: '%s'\n", GameInfo->filename ); lua_pushstring(L, GameInfo->filename); return 1; - } else { - return 0; } #endif + return 0; } @@ -897,7 +906,7 @@ static void LuaStackToBinaryConverter(lua_State* L, int i, std::vectorMD5; + if (GameInfo != nullptr) + { + MD5DATA md5hash = GameInfo->MD5; + + if (!type) lua_pushstring(L, ""); + else if (!stricmp(type, "md5")) lua_pushstring(L, md5_asciistr(md5hash)); + else if (!stricmp(type, "base64")) lua_pushstring(L, BytesToString(md5hash.data, MD5DATA::size).c_str()); + else lua_pushstring(L, ""); + } + else + lua_pushnil(L); - if (!type) lua_pushstring(L, ""); - else if (!stricmp(type, "md5")) lua_pushstring(L, md5_asciistr(md5hash)); - else if (!stricmp(type, "base64")) lua_pushstring(L, BytesToString(md5hash.data, MD5DATA::size).c_str()); - else lua_pushstring(L, ""); return 1; } @@ -2063,7 +2078,7 @@ static int memory_setregister(lua_State *L) } // Forces a stack trace and returns the string -static const char *CallLuaTraceback(lua_State *L) { +static const char *CallLuaTraceback(lua_State *L, int msgDepth = -1) { lua_getfield(L, LUA_GLOBALSINDEX, "debug"); if (!lua_istable(L, -1)) { lua_pop(L, 1); @@ -2076,26 +2091,29 @@ static const char *CallLuaTraceback(lua_State *L) { return ""; } - lua_pushvalue(L, 1); + if (msgDepth < 0) + msgDepth -= 2; // We pushed 2 onto the stack + + lua_pushvalue(L, msgDepth); lua_call(L, 1, 1); return lua_tostring(L, -1); } -void HandleCallbackError(lua_State* L) +void HandleCallbackError(lua_State* L, bool stop, int msgDepth = -1) { //if(L->errfunc || L->errorJmp) // luaL_error(L, "%s", lua_tostring(L,-1)); //else { - const char *trace = CallLuaTraceback(L); + const char *trace = CallLuaTraceback(L, msgDepth); lua_pushnil(L); lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable); char errmsg [2048]; - sprintf(errmsg, "%s\n%s", lua_tostring(L,-1), trace); + snprintf(errmsg, sizeof(errmsg), "%s\n%s", lua_tostring(L,-1), trace); // Error? #ifdef __WIN_DRIVER__ @@ -2105,7 +2123,13 @@ void HandleCallbackError(lua_State* L) fprintf(stderr, "Lua thread bombed out: %s\n", errmsg); #endif - FCEU_LuaStop(); + // If stop flag is true, destruct the lua engine immediately. + // else it will be destructed later at the next frame boundary when callback errors are detected. + if (stop) + { + FCEU_LuaStop(); + } + luaCallbackErrorCounter++; } } @@ -2243,7 +2267,7 @@ static void CallRegisteredLuaMemHook_LuaMatch(unsigned int address, int size, un if(/*info.*/ numMemHooks) { // lua_State* L = info.L; - if(L/* && !info.panic*/) + if( (L != nullptr) && (luaCallbackErrorCounter == 0) ) { #ifdef USE_INFO_STACK infoStack.insert(infoStack.begin(), &info); @@ -2267,7 +2291,8 @@ static void CallRegisteredLuaMemHook_LuaMatch(unsigned int address, int size, un //RefreshScriptSpeedStatus(); if (errorcode) { - HandleCallbackError(L); + // Defer Lua destruction until x6502 memory hooks can fully return. + HandleCallbackError(L, false); //int uid = iter->first; //HandleCallbackError(L,info,uid,true); } @@ -2316,7 +2341,7 @@ void CallRegisteredLuaFunctions(LuaCallID calltype) { errorcode = lua_pcall(L, 0, 0, 0); if (errorcode) - HandleCallbackError(L); + HandleCallbackError(L, true); } else { @@ -2874,7 +2899,7 @@ static int joypad_getimmediate(lua_State *L) luaL_error(L,"Invalid input port (valid range 1-4, specified %d)", which); } // Currently only supports Windows, sorry... -#ifdef __WIN_DRIVER__ +#if defined(__WIN_DRIVER__) || defined(__QT_DRIVER__) extern uint32 GetGamepadPressedImmediate(); uint8 buttons = GetGamepadPressedImmediate() >> ((which - 1) * 8); @@ -6305,7 +6330,7 @@ void CallExitFunction() } if (errorcode) - HandleCallbackError(L); + HandleCallbackError(L, true); } void FCEU_LuaFrameBoundary() @@ -6313,8 +6338,19 @@ void FCEU_LuaFrameBoundary() //printf("Lua Frame\n"); // HA! - if (!L || !luaRunning) + if (L == nullptr) + { return; + } + if (luaCallbackErrorCounter > 0) + { + FCEU_LuaStop(); + return; + } + if (!luaRunning) + { + return; + } // Our function needs calling lua_settop(L,0); @@ -6339,7 +6375,7 @@ void FCEU_LuaFrameBoundary() lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable); char errmsg [1024]; - sprintf(errmsg, "%s\n%s", lua_tostring(thread,-1), trace); + snprintf(errmsg, sizeof(errmsg), "%s\n%s", lua_tostring(thread,-1), trace); // Error? #ifdef __WIN_DRIVER__ @@ -6378,6 +6414,7 @@ void FCEU_LuaFrameBoundary() #endif } + /** * Loads and runs the given Lua script. * The emulator MUST be paused for this function to be @@ -6412,6 +6449,7 @@ int FCEU_LoadLuaCode(const char *filename, const char *arg) //Reinit the error count luaexiterrorcount = 8; + luaCallbackErrorCounter = 0; if (!L) { @@ -6486,6 +6524,10 @@ int FCEU_LoadLuaCode(const char *filename, const char *arg) lua_newtable(L); lua_setfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]); } + + X6502_MemHook::Add( X6502_MemHook::Read , luaReadMemHook , nullptr ); + X6502_MemHook::Add( X6502_MemHook::Write, luaWriteMemHook, nullptr ); + X6502_MemHook::Add( X6502_MemHook::Exec , luaExecMemHook , nullptr ); } // We make our thread NOW because we want it at the bottom of the stack. @@ -6601,6 +6643,10 @@ void FCEU_LuaStop() { //already killed if (!L) return; + X6502_MemHook::Remove( X6502_MemHook::Read , luaReadMemHook , nullptr ); + X6502_MemHook::Remove( X6502_MemHook::Write, luaWriteMemHook, nullptr ); + X6502_MemHook::Remove( X6502_MemHook::Exec , luaExecMemHook , nullptr ); + // Since the script is exiting, we want to prevent an infinite loop. // CallExitFunction() > HandleCallbackError() > FCEU_LuaStop() > CallExitFunction() ... if (luaexiterrorcount > 0) { @@ -6692,7 +6738,7 @@ uint8 FCEU_LuaReadJoypad(int which, uint8 joyl) { * * This function will not return true if a script is not running. */ -int FCEU_LuaRerecordCountSkip() { +bool FCEU_LuaRerecordCountSkip() { // FIXME: return true if (there are any active callback functions && skipRerecords) return L && luaRunning && skipRerecords; } diff --git a/NES.emu/src/fceu/movie.cpp b/NES.emu/src/fceu/movie.cpp index f3d422db2..53e324fc6 100644 --- a/NES.emu/src/fceu/movie.cpp +++ b/NES.emu/src/fceu/movie.cpp @@ -1358,22 +1358,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); @@ -1381,7 +1381,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); @@ -1394,7 +1394,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); } @@ -1645,18 +1645,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; } diff --git a/NES.emu/src/fceu/nsf.cpp b/NES.emu/src/fceu/nsf.cpp index aedd369db..e1b30a816 100644 --- a/NES.emu/src/fceu/nsf.cpp +++ b/NES.emu/src/fceu/nsf.cpp @@ -581,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/NES.emu/src/fceu/pputile.inc b/NES.emu/src/fceu/pputile.inc index 02da9a220..af4299b5f 100644 --- a/NES.emu/src/fceu/pputile.inc +++ b/NES.emu/src/fceu/pputile.inc @@ -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/NES.emu/src/fceu/state.cpp b/NES.emu/src/fceu/state.cpp index bff3e3a52..2f1c228d4 100644 --- a/NES.emu/src/fceu/state.cpp +++ b/NES.emu/src/fceu/state.cpp @@ -345,7 +345,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; } diff --git a/NES.emu/src/fceu/types.h b/NES.emu/src/fceu/types.h index 16559ebd6..4fddd2d92 100644 --- a/NES.emu/src/fceu/types.h +++ b/NES.emu/src/fceu/types.h @@ -97,9 +97,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 @@ -111,6 +108,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 "\\" @@ -161,6 +169,7 @@ typedef uint8 (*readfunc)(uint32 A); #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]] @@ -183,6 +192,7 @@ typedef uint8 (*readfunc)(uint32 A); #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. @@ -253,6 +263,7 @@ class fceuScopedPtr enum fceuAllocType _allocType; }; +#endif // __cplusplus #include "utils/endian.h" diff --git a/NES.emu/src/fceu/version.h b/NES.emu/src/fceu/version.h index c810c1fd5..91200c3f1 100644 --- a/NES.emu/src/fceu/version.h +++ b/NES.emu/src/fceu/version.h @@ -61,15 +61,15 @@ #endif #define FCEU_VERSION_MAJOR 2 -#define FCEU_VERSION_MINOR 6 -#define FCEU_VERSION_PATCH 6 +#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.6" 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/NES.emu/src/fceu/video.cpp b/NES.emu/src/fceu/video.cpp index b0f7dd0d4..cab54fd04 100644 --- a/NES.emu/src/fceu/video.cpp +++ b/NES.emu/src/fceu/video.cpp @@ -765,7 +765,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; @@ -797,7 +797,7 @@ static void FCEU_DrawPauseCountDown(uint8 *XBuf) framesPerSec = 60; } - sprintf(text, "Unpausing in %d...", (pauseFramesLeft / framesPerSec) + 1); + snprintf(text, sizeof(text), "Unpausing in %d...", (pauseFramesLeft / framesPerSec) + 1); if (text[0]) { diff --git a/NES.emu/src/fceu/vsuni.cpp b/NES.emu/src/fceu/vsuni.cpp index 0111c8636..915d96b22 100644 --- a/NES.emu/src/fceu/vsuni.cpp +++ b/NES.emu/src/fceu/vsuni.cpp @@ -354,33 +354,50 @@ void FCEU_VSUniCheck(uint64 md5partial, int *MapperNo, uint8 *Mirroring) { GameInfo->vs_cswitch = 1; } - 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 & 4) { - sprintf(gigastr + strlen(gigastr), "Game type should be set to Vs. System. "); + snprintf(tmpStr, sizeof(tmpStr), "Game type should be set to Vs. System. "); + gigastr.append(tmpStr); } 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[vs->mirroring & 3]); + 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"}; - sprintf(gigastr + strlen(gigastr), "Vs. System type should be set to \"%s\". ", mstr[vs->type]); + 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" }; - sprintf(gigastr + strlen(gigastr), "Vs. System PPU should be set to \"%s\". ", mstr[vs->ppu]); + snprintf(tmpStr, sizeof(tmpStr), "Vs. System PPU should be set to \"%s\". ", mstr[vs->ppu]); + gigastr.append(tmpStr); } if (tofix & 32) - sprintf(gigastr + strlen(gigastr), "The controller type should be set to zapper. "); + { + snprintf(tmpStr, sizeof(tmpStr), "The controller type should be set to zapper. "); + gigastr.append(tmpStr); + } if (tofix & 64) - sprintf(gigastr + strlen(gigastr), "The controllers should be swapped. "); - strcat(gigastr, "\n"); - FCEU_printf("%s", gigastr); + { + snprintf(tmpStr, sizeof(tmpStr), "The controllers should be swapped. "); + gigastr.append(tmpStr); + } + gigastr.append("\n"); + FCEU_printf("%s", gigastr.c_str()); } return; diff --git a/NES.emu/src/fceu/x6502.cpp b/NES.emu/src/fceu/x6502.cpp index 85c593629..2358b1d92 100644 --- a/NES.emu/src/fceu/x6502.cpp +++ b/NES.emu/src/fceu/x6502.cpp @@ -44,13 +44,126 @@ 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) { _DB=ARead[A](A); - #ifdef _S9XLUA_H - CallRegisteredLuaMemHook(A, 1, _DB, LUAMEMHOOK_READ); - #endif + if (readMemHook) + { + readMemHook->call(A, _DB); + } return(_DB); } @@ -58,18 +171,20 @@ static INLINE uint8 RdMem(unsigned int A) static INLINE void WrMem(unsigned int A, uint8 V) { BWrite[A](A,V); - #ifdef _S9XLUA_H - CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE); - #endif - _DB = V; + if (writeMemHook) + { + writeMemHook->call(A, V); + } + _DB = V; } static INLINE uint8 RdRAM(unsigned int A) { _DB=ARead[A](A); - #ifdef _S9XLUA_H - CallRegisteredLuaMemHook(A, 1, _DB, LUAMEMHOOK_READ); - #endif + if (readMemHook) + { + readMemHook->call(A, _DB); + } //bbit edited: this was changed so cheat substituion would work // return(_DB=RAM[A]); return(_DB); @@ -78,19 +193,21 @@ static INLINE uint8 RdRAM(unsigned int A) static INLINE void WrRAM(unsigned int A, uint8 V) { RAM[A]=V; - #ifdef _S9XLUA_H - CallRegisteredLuaMemHook(A, 1, V, LUAMEMHOOK_WRITE); - #endif - _DB = V; + if (writeMemHook) + { + writeMemHook->call(A, V); + } + _DB = V; } uint8 X6502_DMR(uint32 A) { ADDCYC(1); _DB=ARead[A](A); - #ifdef _S9XLUA_H - CallRegisteredLuaMemHook(A, 1, _DB, LUAMEMHOOK_READ); - #endif + if (readMemHook) + { + readMemHook->call(A, _DB); + } return(_DB); } @@ -98,9 +215,10 @@ 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; } @@ -520,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/NES.emu/src/fceu/x6502.h b/NES.emu/src/fceu/x6502.h index 460683d84..779fa6d89 100644 --- a/NES.emu/src/fceu/x6502.h +++ b/NES.emu/src/fceu/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/NES.emu/src/main/EmuFileIO.hh b/NES.emu/src/main/EmuFileIO.hh index 899be0b27..1f4d43fb8 100644 --- a/NES.emu/src/main/EmuFileIO.hh +++ b/NES.emu/src/main/EmuFileIO.hh @@ -18,11 +18,6 @@ #include #include -namespace IG -{ -class IO; -} - namespace EmuEx { diff --git a/NES.emu/src/main/EmuMenuViews.cc b/NES.emu/src/main/EmuMenuViews.cc index 92fec6532..6fd9237a8 100644 --- a/NES.emu/src/main/EmuMenuViews.cc +++ b/NES.emu/src/main/EmuMenuViews.cc @@ -39,6 +39,8 @@ extern int pal_emulation; namespace EmuEx { +constexpr SystemLogger log{"NES.emu"}; + template using MainAppHelper = EmuAppHelper; @@ -68,45 +70,52 @@ class ConsoleOptionView : public TableView, public MainAppHelper unpackVideoLines(uint16_t packed) + { + return {uint8_t(packed & 0xFF), uint8_t(packed >> 8)}; + } + TextMenuItem visibleVideoLinesItem[4] { - {"8+224", attachParams(), setVisibleVideoLinesDel(8, 224)}, - {"8+232", attachParams(), setVisibleVideoLinesDel(8, 232)}, - {"0+232", attachParams(), setVisibleVideoLinesDel(0, 232)}, - {"0+240", attachParams(), setVisibleVideoLinesDel(0, 240)}, + {"8+224", attachParams(), {.id = packVideoLines(8, 224)}}, + {"8+232", attachParams(), {.id = packVideoLines(8, 232)}}, + {"0+232", attachParams(), {.id = packVideoLines(0, 232)}}, + {"0+240", attachParams(), {.id = packVideoLines(0, 240)}}, }; MultiChoiceMenuItem visibleVideoLines { "Visible Lines", attachParams(), - [this]() + MenuId{packVideoLines(system().optionStartVideoLine, system().optionVisibleVideoLines)}, + visibleVideoLinesItem, { - switch(system().optionVisibleVideoLines) + .defaultItemOnSelect = [this](TextMenuItem &item) { - default: return 0; - case 232: return system().optionStartVideoLine == 8 ? 1 : 2; - case 240: return 3; + auto [startLine, lines] = unpackVideoLines(item.id); + system().sessionOptionSet(); + system().optionStartVideoLine = startLine; + system().optionVisibleVideoLines = lines; + system().updateVideoPixmap(app().video, system().optionHorizontalVideoCrop, system().optionVisibleVideoLines); + system().renderFramebuffer(app().video); + app().viewController().placeEmuViews(); } - }(), - visibleVideoLinesItem + } }; - TextMenuItem::SelectDelegate setVisibleVideoLinesDel(uint8_t startLine, uint8_t lines) - { - return [this, startLine, lines]() - { - system().sessionOptionSet(); - system().optionStartVideoLine = startLine; - system().optionVisibleVideoLines = lines; - system().updateVideoPixmap(app().video, system().optionHorizontalVideoCrop, system().optionVisibleVideoLines); - system().renderFramebuffer(app().video); - app().viewController().placeEmuViews(); - }; - } - BoolMenuItem horizontalVideoCrop { "Crop 8 Pixels On Sides", attachParams(), @@ -256,10 +266,11 @@ class ConsoleOptionView : public TableView, public MainAppHelper menuItem + std::array menuItem { &inputPorts, &fourScore, + &fcMic, &compatibleFrameskip, &videoHeading, &videoSystem, @@ -278,8 +289,7 @@ class ConsoleOptionView : public TableView, public MainAppHelper @@ -571,7 +581,7 @@ class CustomFilePathOptionView : public FilePathOptionView, public MainAppHelper pushAndShow(makeViewWithName("Cheats", system().userPath(system().cheatsDir), [this](CStringView path) { - logMsg("set cheats path:%s", path.data()); + log.info("set cheats path:{}", path); system().cheatsDir = path; cheatsPath.compile(cheatsMenuName(appContext(), path)); }), e); @@ -586,7 +596,7 @@ class CustomFilePathOptionView : public FilePathOptionView, public MainAppHelper pushAndShow(makeViewWithName("Patches", system().userPath(system().patchesDir), [this](CStringView path) { - logMsg("set patches path:%s", path.data()); + log.info("set patches path:{}", path); system().patchesDir = path; patchesPath.compile(patchesMenuName(appContext(), path)); }), e); @@ -601,7 +611,7 @@ class CustomFilePathOptionView : public FilePathOptionView, public MainAppHelper pushAndShow(makeViewWithName("Palettes", system().userPath(system().palettesDir), [this](CStringView path) { - logMsg("set palettes path:%s", path.data()); + log.info("set palettes path:{}", path); system().palettesDir = path; palettesPath.compile(palettesMenuName(appContext(), path)); }), e); @@ -618,7 +628,7 @@ class CustomFilePathOptionView : public FilePathOptionView, public MainAppHelper [this](CStringView path, FS::file_type type) { system().fdsBiosPath = path; - logMsg("set fds bios:%s", path.data()); + log.info("set fds bios:{}", path); fdsBios.compile(biosMenuEntryStr(path)); return true; }, hasFDSBIOSExtension), e); @@ -773,7 +783,7 @@ class CustomSystemOptionView : public SystemOptionView, public MainAppHelper("{}.fc{}", name, saveSlotCharNES(slot)); + return format("{}.fc{}", name, saveSlotCharNES(slot)); } void NesSystem::readState(EmuApp &app, std::span buff) @@ -182,11 +182,6 @@ void NesSystem::closeSystem() fdsIsAccessing = false; } -void FCEUD_GetPalette(uint8 index, uint8 *r, uint8 *g, uint8 *b) -{ - bug_unreachable("called FCEUD_GetPalette()"); -} - void NesSystem::setDefaultPalette(IO &io) { auto colors = io.read(std::span{defaultPal}).items; @@ -202,7 +197,7 @@ void NesSystem::setDefaultPalette(IO &io) FCEU_setDefaultPalettePtr(defaultPal.data()); } -void NesSystem::setDefaultPalette(IG::ApplicationContext ctx, IG::CStringView palPath) +void NesSystem::setDefaultPalette(ApplicationContext ctx, CStringView palPath) { if(palPath.empty()) { @@ -210,7 +205,7 @@ void NesSystem::setDefaultPalette(IG::ApplicationContext ctx, IG::CStringView pa return; } log.info("setting default palette with path:{}", palPath); - if(palPath[0] != '/' && !IG::isUri(palPath)) + if(palPath[0] != '/' && !isUri(palPath)) { // load as asset IO io = ctx.openAsset(FS::pathString("palette", palPath), IO::AccessHint::All); @@ -286,13 +281,13 @@ void NesSystem::setupNESInputPorts() { if(!GameInfo) return; - for(auto i : iotaCount(2)) + for(auto &&[i, dev] : enumerate(std::array{inputPort1.value(), inputPort2.value()})) { - if(nesInputPortDev[i] == SI_UNSET) // user didn't specify device, go with auto settings + if(dev == SI_UNSET) // user didn't specify device, go with auto settings connectNESInput(i, GameInfo->input[i] == SI_UNSET ? SI_GAMEPAD : GameInfo->input[i]); else - connectNESInput(i, nesInputPortDev[i]); - log.info("attached {} to port {}{}", fceuInputToStr(joyports[i].type), i, nesInputPortDev[i] == SI_UNSET ? " (auto)" : ""); + connectNESInput(i, dev); + log.info("attached {} to port {}{}", fceuInputToStr(joyports[i].type), i, dev == SI_UNSET ? " (auto)" : ""); } if(GameInfo->inputfc == SIFC_HYPERSHOT) { @@ -330,12 +325,12 @@ const char *regionToStr(int region) static int regionFromName(std::string_view name) { - if(IG::containsAny(name, "(E)", "(e)", "(EU)", "(Europe)", "(PAL)", + if(containsAny(name, "(E)", "(e)", "(EU)", "(Europe)", "(PAL)", "(F)", "(f)", "(G)", "(g)", "(I)", "(i)")) { return 1; // PAL } - else if(IG::containsAny(name, "(RU)", "(ru)")) + else if(containsAny(name, "(RU)", "(ru)")) { return 2; // Dendy } @@ -392,7 +387,7 @@ void NesSystem::loadContent(IO &io, EmuSystemCreateParams, OnLoadProgressDelegat saveStateSize = stateMemFile.get_vec()->size(); } -bool NesSystem::onVideoRenderFormatChange(EmuVideo &video, IG::PixelFormat fmt) +bool NesSystem::onVideoRenderFormatChange(EmuVideo &video, PixelFormat fmt) { pixFmt = fmt; updateVideoPixmap(video, optionHorizontalVideoCrop, optionVisibleVideoLines); @@ -435,12 +430,12 @@ void NesSystem::renderVideo(EmuSystemTaskContext taskCtx, EmuVideo &video, uint8 { auto img = video.startFrame(taskCtx); auto pix = img.pixmap(); - IG::PixmapView ppuPix{{{256, 256}, IG::PIXEL_FMT_I8}, buf}; + PixmapView ppuPix{{{256, 256}, PIXEL_FMT_I8}, buf}; int xStart = pix.w() == 256 ? 0 : 8; int yStart = optionStartVideoLine; auto ppuPixRegion = ppuPix.subView({xStart, yStart}, pix.size()); assumeExpr(pix.size() == ppuPixRegion.size()); - if(pix.format() == IG::PIXEL_RGB565) + if(pix.format() == PIXEL_RGB565) { pix.writeTransformed([&](uint8 p){ return nativeCol.col16[p]; }, ppuPixRegion); } @@ -486,13 +481,13 @@ void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b) { using namespace EmuEx; auto &sys = static_cast(gSystem()); - if(sys.pixFmt == IG::PIXEL_RGB565) + if(sys.pixFmt == PIXEL_RGB565) { sys.nativeCol.col16[index] = sys.pixFmt.desc().build(r >> 3, g >> 2, b >> 3, 0); } else // RGBA8888 { - auto desc = sys.pixFmt == IG::PIXEL_BGRA8888 ? IG::PIXEL_DESC_BGRA8888.nativeOrder() : IG::PIXEL_DESC_RGBA8888_NATIVE; + auto desc = sys.pixFmt == PIXEL_BGRA8888 ? PIXEL_DESC_BGRA8888.nativeOrder() : PIXEL_DESC_RGBA8888_NATIVE; sys.nativeCol.col32[index] = desc.build(r, g, b, (uint8)0); } //log.debug("set palette {} {}", index, nativeCol[index]); diff --git a/NES.emu/src/main/MainSystem.hh b/NES.emu/src/main/MainSystem.hh index bc664937f..e01671e1c 100644 --- a/NES.emu/src/main/MainSystem.hh +++ b/NES.emu/src/main/MainSystem.hh @@ -39,7 +39,7 @@ enum CFGKEY_FF_DURING_FDS_ACCESS = 286, CFGKEY_CHEATS_PATH = 287, CFGKEY_PATCHES_PATH = 288, CFGKEY_PALETTE_PATH = 289, CFGKEY_OVERCLOCKING = 290, CFGKEY_OVERCLOCK_EXTRA_LINES = 291, - CFGKEY_OVERCLOCK_VBLANK_MULTIPLIER = 292, + CFGKEY_OVERCLOCK_VBLANK_MULTIPLIER = 292, CFGKEY_P2_START_AS_FC_MIC = 293, }; constexpr int maxExtraLinesPerFrame = 30000; @@ -74,7 +74,6 @@ public: using PalArray = std::array; size_t saveStateSize{}; - ESI nesInputPortDev[2]{SI_UNSET, SI_UNSET}; uint32 padData{}; uint32 zapperData[3]{}; uint8_t fcExtData{}; @@ -94,13 +93,13 @@ public: std::string defaultPalettePath; std::string fdsBiosPath; std::string loaderErrorString; - bool fastForwardDuringFdsAccess = true; + Property{.defaultValue = true}> fastForwardDuringFdsAccess; bool fdsIsAccessing{}; Property optionFourScore; - Property{.defaultValue = -1, .isValid = isValidWithMinMax<-1, 2>}> optionInputPort1; - Property{.defaultValue = -1, .isValid = isValidWithMinMax<-1, 2>}> optionInputPort2; + Property{.defaultValue = SI_UNSET, .isValid = isValidWithMinMax}> inputPort1; + Property{.defaultValue = SI_UNSET, .isValid = isValidWithMinMax}> inputPort2; Property{.defaultValue = 0, .isValid = isValidWithMax<3>}> optionVideoSystem; Property NesKey::Start ); +constexpr std::array p2StartKeyInfo +{ + KeyInfo{NesKey::Start, {.deviceId = 1}} +}; + constexpr auto faceKeyInfo = makeArray ( NesKey::B, @@ -198,12 +204,12 @@ void NesSystem::connectNESInput(int port, ESI type) assert(GameInfo); if(type == SI_GAMEPAD) { - //logMsg("gamepad to port %d", port); + //log.debug("gamepad to port {}", port); FCEUI_SetInput(port, SI_GAMEPAD, &padData, 0); } else if(type == SI_ZAPPER) { - //logMsg("zapper to port %d", port); + //log.debug("zapper to port {}", port); FCEUI_SetInput(port, SI_ZAPPER, &zapperData, 1); } else @@ -286,14 +292,14 @@ void NesSystem::handleInputAction(EmuApp *app, InputAction a) { hsKey = hsKey == 0x3 ? 0x3 : hsKey ^ 0x3; // swap the 2 bits auto hsPlayerInputShift = player == 1 ? 3 : 1; - fcExtData = IG::setOrClearBits(fcExtData, hsKey << hsPlayerInputShift, a.isPushed()); + fcExtData = setOrClearBits(fcExtData, hsKey << hsPlayerInputShift, a.isPushed()); } } padData = setOrClearBits(padData, gpBits << playerInputShift(player), a.isPushed()); } } -bool NesSystem::onPointerInputStart(const Input::MotionEvent &e, Input::DragTrackerState, IG::WindowRect gameRect) +bool NesSystem::onPointerInputStart(const Input::MotionEvent &e, Input::DragTrackerState, WRect gameRect) { if(!usingZapper) return false; @@ -301,9 +307,9 @@ bool NesSystem::onPointerInputStart(const Input::MotionEvent &e, Input::DragTrac if(gameRect.overlaps(e.pos())) { int xRel = e.pos().x - gameRect.x, yRel = e.pos().y - gameRect.y; - int xNes = IG::remap(xRel, 0, gameRect.xSize(), 0, 256); - int yNes = IG::remap(yRel, 0, gameRect.ySize(), 0, 224) + 8; - logMsg("zapper pushed @ %d,%d, on NES %d,%d", e.pos().x, e.pos().y, xNes, yNes); + int xNes = remap(xRel, 0, gameRect.xSize(), 0, 256); + int yNes = remap(yRel, 0, gameRect.ySize(), 0, 224) + 8; + log.info("zapper pushed @ {},{}, on NES {},{}", e.pos().x, e.pos().y, xNes, yNes); zapperData[0] = xNes; zapperData[1] = yNes; zapperData[2] |= 0x1; @@ -317,7 +323,7 @@ bool NesSystem::onPointerInputStart(const Input::MotionEvent &e, Input::DragTrac return true; } -bool NesSystem::onPointerInputEnd(const Input::MotionEvent &, Input::DragTrackerState, IG::WindowRect) +bool NesSystem::onPointerInputEnd(const Input::MotionEvent &, Input::DragTrackerState, WRect) { if(!usingZapper) return false; @@ -327,7 +333,7 @@ bool NesSystem::onPointerInputEnd(const Input::MotionEvent &, Input::DragTracker void NesSystem::clearInputBuffers(EmuInputView &) { - IG::fill(zapperData); + fill(zapperData); padData = {}; fcExtData = {}; } @@ -341,6 +347,7 @@ SystemInputDeviceDesc NesSystem::inputDeviceDesc(int idx) const InputComponentDesc{"Select", {¢erKeyInfo[0], 1}, InputComponent::button, LB2DO}, InputComponentDesc{"Start", {¢erKeyInfo[1], 1}, InputComponent::button, RB2DO}, InputComponentDesc{"Select/Start", centerKeyInfo, InputComponent::button, CB2DO, {.altConfig = true}}, + InputComponentDesc{"P2 Start (Famicom Microphone)", p2StartKeyInfo, InputComponent::button, RB2DO, {.altConfig = true}}, }; static constexpr SystemInputDeviceDesc gamepadDesc{"Gamepad", gamepadComponents}; diff --git a/NES.emu/src/main/options.cc b/NES.emu/src/main/options.cc index df17f4c6b..346be0d7a 100644 --- a/NES.emu/src/main/options.cc +++ b/NES.emu/src/main/options.cc @@ -43,8 +43,6 @@ void NesSystem::onOptionsLoaded() void NesSystem::onSessionOptionsLoaded(EmuApp &app) { - nesInputPortDev[0] = (ESI)(int)optionInputPort1; - nesInputPortDev[1] = (ESI)(int)optionInputPort2; updateVideoPixmap(app.video, optionHorizontalVideoCrop, optionVisibleVideoLines); } @@ -53,10 +51,9 @@ bool NesSystem::resetSessionOptions(EmuApp &app) optionFourScore.reset(); setupNESFourScore(); optionVideoSystem.reset(); - optionInputPort1.reset(); - optionInputPort2.reset(); - nesInputPortDev[0] = (ESI)(int)optionInputPort1; - nesInputPortDev[1] = (ESI)(int)optionInputPort2; + inputPort1.reset(); + inputPort2.reset(); + replaceP2StartWithMicrophone = false; setupNESInputPorts(); optionCompatibleFrameskip.reset(); optionStartVideoLine = optionDefaultStartVideoLine; @@ -100,8 +97,8 @@ bool NesSystem::readConfig(ConfigType type, MapIO &io, unsigned key) { case CFGKEY_FOUR_SCORE: return readOptionValue(io, optionFourScore); case CFGKEY_VIDEO_SYSTEM: return readOptionValue(io, optionVideoSystem); - case CFGKEY_INPUT_PORT_1: return readOptionValue(io, optionInputPort1); - case CFGKEY_INPUT_PORT_2: return readOptionValue(io, optionInputPort2); + case CFGKEY_INPUT_PORT_1: return readOptionValue(io, inputPort1); + case CFGKEY_INPUT_PORT_2: return readOptionValue(io, inputPort2); case CFGKEY_COMPATIBLE_FRAMESKIP: return readOptionValue(io, optionCompatibleFrameskip); case CFGKEY_START_VIDEO_LINE: return readOptionValue(io, optionStartVideoLine); case CFGKEY_VISIBLE_VIDEO_LINES: return readOptionValue(io, optionVisibleVideoLines); @@ -111,6 +108,7 @@ bool NesSystem::readConfig(ConfigType type, MapIO &io, unsigned key) [&](auto v){if(v >= 0 && v <= maxExtraLinesPerFrame) postrenderscanlines = v;}); case CFGKEY_OVERCLOCK_VBLANK_MULTIPLIER: return readOptionValue(io, [&](auto v){if(v >= 0 && v <= maxVBlankMultiplier) vblankscanlines = v;}); + case CFGKEY_P2_START_AS_FC_MIC: return readOptionValue(io, replaceP2StartWithMicrophone); } } return false; @@ -132,8 +130,7 @@ void NesSystem::writeConfig(ConfigType type, FileIO &io) writeOptionValueIfNotDefault(io, optionDefaultStartVideoLine); writeOptionValueIfNotDefault(io, optionDefaultVisibleVideoLines); writeOptionValueIfNotDefault(io, optionCorrectLineAspect); - if(!fastForwardDuringFdsAccess) - writeOptionValue(io, CFGKEY_FF_DURING_FDS_ACCESS, fastForwardDuringFdsAccess); + writeOptionValueIfNotDefault(io, fastForwardDuringFdsAccess); writeStringOptionValue(io, CFGKEY_CHEATS_PATH, cheatsDir); writeStringOptionValue(io, CFGKEY_PATCHES_PATH, patchesDir); writeStringOptionValue(io, CFGKEY_PALETTE_PATH, palettesDir); @@ -142,8 +139,8 @@ void NesSystem::writeConfig(ConfigType type, FileIO &io) { writeOptionValueIfNotDefault(io, optionFourScore); writeOptionValueIfNotDefault(io, optionVideoSystem); - writeOptionValueIfNotDefault(io, optionInputPort1); - writeOptionValueIfNotDefault(io, optionInputPort2); + writeOptionValueIfNotDefault(io, inputPort1); + writeOptionValueIfNotDefault(io, inputPort2); writeOptionValueIfNotDefault(io, optionCompatibleFrameskip); writeOptionValueIfNotDefault(io, optionStartVideoLine); writeOptionValueIfNotDefault(io, optionVisibleVideoLines); @@ -151,6 +148,7 @@ void NesSystem::writeConfig(ConfigType type, FileIO &io) writeOptionValueIfNotDefault(io, CFGKEY_OVERCLOCKING, bool(overclock_enabled), 0); writeOptionValueIfNotDefault(io, CFGKEY_OVERCLOCK_EXTRA_LINES, int16_t(postrenderscanlines), 0); writeOptionValueIfNotDefault(io, CFGKEY_OVERCLOCK_VBLANK_MULTIPLIER, int8_t(vblankscanlines), 0); + writeOptionValueIfNotDefault(io, CFGKEY_P2_START_AS_FC_MIC, replaceP2StartWithMicrophone, false); } } diff --git a/imagine/include/imagine/util/Property.hh b/imagine/include/imagine/util/Property.hh index 5988ea441..ef4b60064 100644 --- a/imagine/include/imagine/util/Property.hh +++ b/imagine/include/imagine/util/Property.hh @@ -17,23 +17,28 @@ #include #include +#include +#include namespace IG { -template Validator = bool(*)(const T&)> +template Validator = bool(*)(const T&)> struct PropertyDesc { + using SerializedType = SerializedT; T defaultValue{}; bool mutableDefault{}; Validator isValid = [](const T&){return true;}; }; -template desc = PropertyDesc{}> +template{}> class Property { public: using Type = T; + using SerializedType = decltype(desc)::SerializedType; + static constexpr bool sameSerializedType = std::is_same_v; static constexpr auto uid{uid_}; static_assert(desc.isValid(desc.defaultValue)); @@ -76,6 +81,12 @@ public: return reset(); } + // If serialized types match, no need to copy/convert values + constexpr const T& serialize() const requires (sameSerializedType) { return value(); } + constexpr bool unserialize(auto &&v) requires (sameSerializedType) { return set(IG_forward(v)); } + constexpr auto serialize() const requires (!sameSerializedType) { return SerializedType(value()); } + constexpr bool unserialize(auto &&v) requires (!sameSerializedType) { return set(T(IG_forward(v))); } + private: T value_{desc.defaultValue}; [[no_unique_address]] std::conditional_t