////////////////////////////////////////////////////////////////////////// // // // NES Emulation core // // Norix // // written 2001/02/22 // // last modify ----/--/-- // ////////////////////////////////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include "typedef.h" #include "macro.h" #include "VirtuaNESres.h" #include "DebugOut.h" #include "App.h" #include "Pathlib.h" #include "Config.h" #include "Crclib.h" #include "nes.h" #include "mmu.h" #include "cpu.h" #include "ppu.h" #include "apu.h" #include "pad.h" #include "rom.h" #include "mapper.h" #include "DirectDraw.h" #include "DirectSound.h" #include "DirectInput.h" #include "pngwrite.h" NESCONFIG NESCONFIG_NTSC = { 21477270.0f, // Base clock 1789772.5f, // Cpu clock 262, // Total scanlines 1364, // Scanline total cycles(15.75KHz) 1024, // H-Draw cycles 340, // H-Blank cycles 4, // End cycles 1364 * 262, // Frame cycles 29830, // FrameIRQ cycles 60, // Frame rate(Be originally 59.94Hz) 1000.0f / 60.0f // Frame period(ms) }; NESCONFIG NESCONFIG_PAL = { // 21281364.0f, // Base clock 26601714.0f, // Base clock // 1773447.0f, // Cpu clock 1662607.125f, // Cpu clock 312, // Total scanlines // 1362, // Scanline total cycles(15.625KHz) 1278, // Scanline total cycles(15.625KHz) // 1024, // H-Draw cycles 960, // H-Draw cycles // 338, // H-Blank cycles 318, // H-Blank cycles 2, // End cycles // 1362*312, // Frame cycles 1278 * 312, // Frame cycles // 35469, // FrameIRQ cycles 33252, // FrameIRQ cycles 50, // Frame rate(Hz) 1000.0f / 50.0f // Frame period(ms) }; // Pad disp BYTE NES::m_PadImg[] = { 28, 8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; BYTE NES::m_KeyImg0[] = { 2, 2, 0x2A, 0x2A, 0x2A, 0x2A, }; BYTE NES::m_KeyImg1[] = { 3, 3, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, }; BYTE NES::m_KeyImg2[] = { 4, 4, 0xFF, 0x2A, 0x2A, 0xFF, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0xFF, 0x2A, 0x2A, 0xFF, }; //---------- #define ASMVER 0 #if ASMVER static INT cache = 0; static UINT cache_hiRem = 0; #endif // // // NES::NES(const char* fname) { DEBUGOUT("VirtuaNES - NES Emulator for Win32 by Norix (C)2001\n"); m_bDiskThrottle = FALSE; m_CommandRequest = 0; m_nSnapNo = 0; m_bNsfPlaying = FALSE; m_bMoviePlay = m_bMovieRec = FALSE; m_fpMovie = NULL; m_bTapePlay = m_bTapeRec = FALSE; m_fpTape = NULL; m_TapeCycles = 0.0; m_TapeIn = m_TapeOut = 0; m_bBarcode = FALSE; m_BarcodeOut = 0; m_BarcodePtr = 0; m_BarcodeCycles = 0; m_bBarcode2 = FALSE; m_TurboFileBank = 0; cpu = NULL; ppu = NULL; apu = NULL; rom = NULL; pad = NULL; mapper = NULL; SAVERAM_SIZE = 8 * 1024; // 8K byte // IRQ type nIRQtype = 0; // FrameIRQ mode bFrameIRQ = TRUE; // NTSC/PAL VideoMode bVideoMode = FALSE; // Default config nescfg = &NESCONFIG_NTSC; // Cheat CheatInitial(); // TEST #if NES_PROFILER m_dwTotalCycle = 0; m_dwTotalTempCycle = 0; m_dwProfileTotalCycle = 0; m_dwProfileTotalCount = 0; m_dwProfileCycle = 0; m_dwProfileTempCycle = 0; m_dwProfileAveCycle = 0; m_dwProfileMaxCycle = 0; #endif //------- #if ASMVER cache = 0; cache_hiRem = 0; #endif try { DEBUGOUT("Allocating CPU..."); if (!(cpu = new CPU(this))) throw "Allocating CPU failed."; DEBUGOUT("Ok.\n"); DEBUGOUT("Allocating PPU..."); if (!(ppu = new PPU(this))) throw "Allocating PPU failed."; DEBUGOUT("Ok.\n"); DEBUGOUT("Allocating APU..."); if (!(apu = new APU(this))) throw "Allocating APU failed."; DEBUGOUT("Ok.\n"); DEBUGOUT("Allocating PAD..."); if (!(pad = new PAD(this))) throw "Allocating PAD failed."; DEBUGOUT("Ok.\n"); DEBUGOUT("Loading ROM Image...\n"); if (!(rom = new ROM(fname))) throw "Allocating ROM failed."; if (!(mapper = CreateMapper(this, rom->GetMapperNo()))) { // 未サポートのマッパーです LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_UNSUPPORTMAPPER); sprintf(szErrorString, szErrStr, rom->GetMapperNo()); throw szErrorString; } DEBUGOUT("Ok.\n"); DEBUGOUT("ROM status\n"); DEBUGOUT(" %s\n", rom->GetRomName()); DEBUGOUT(" Mapper : %03d\n", rom->GetMapperNo()); DEBUGOUT(" PRG SIZE : %4dK\n", 16 * (INT)rom->GetPROM_SIZE()); DEBUGOUT(" CHR SIZE : %4dK\n", 8 * (INT)rom->GetVROM_SIZE()); DEBUGOUT(" V MIRROR : "); if (rom->IsVMIRROR()) DEBUGOUT("Yes\n"); else DEBUGOUT("No\n"); DEBUGOUT(" 4 SCREEN : "); if (rom->Is4SCREEN()) DEBUGOUT("Yes\n"); else DEBUGOUT("No\n"); DEBUGOUT(" SAVE RAM : "); if (rom->IsSAVERAM()) DEBUGOUT("Yes\n"); else DEBUGOUT("No\n"); DEBUGOUT(" TRAINER : "); if (rom->IsTRAINER()) DEBUGOUT("Yes\n"); else DEBUGOUT("No\n"); DEBUGOUT(" VS-Unisystem : "); if (rom->IsVSUNISYSTEM()) DEBUGOUT("Yes\n"); else DEBUGOUT("No\n"); NesSub_MemoryInitial(); LoadSRAM(); LoadDISK(); { // Padクラス内だと初期化タイミングが遅いのでここで DWORD crc = rom->GetPROM_CRC(); if (crc == 0xe792de94 // Best Play - Pro Yakyuu (New) (J) || crc == 0xf79d684a // Best Play - Pro Yakyuu (Old) (J) || crc == 0xc2ef3422 // Best Play - Pro Yakyuu 2 (J) || crc == 0x974e8840 // Best Play - Pro Yakyuu '90 (J) || crc == 0xb8747abf // Best Play - Pro Yakyuu Special (J) || crc == 0x9fa1c11f // Castle Excellent (J) || crc == 0x0b0d4d1b // Derby Stallion - Zenkoku Ban (J) || crc == 0x728c3d98 // Downtown - Nekketsu Monogatari (J) || crc == 0xd68a6f33 // Dungeon Kid (J) || crc == 0x3a51eb04 // Fleet Commander (J) || crc == 0x7c46998b // Haja no Fuuin (J) || crc == 0x7e5d2f1a // Itadaki Street - Watashi no Mise ni Yottette (J) || crc == 0xcee5857b // Ninjara Hoi! (J) || crc == 0x50ec5e8b // Wizardry - Legacy of Llylgamyn (J) || crc == 0x343e9146 // Wizardry - Proving Grounds of the Mad Overlord (J) || crc == 0x33d07e45) { // Wizardry - The Knight of Diamonds (J) pad->SetExController(PAD::EXCONTROLLER_TURBOFILE); } } LoadTurboFile(); // VS-Unisystemのデフォルト設定 if (rom->IsVSUNISYSTEM()) { DWORD crc = rom->GetPROM_CRC(); m_VSDipValue = GetVSDefaultDipSwitchValue(crc); m_VSDipTable = FindVSDipSwitchTable(crc); #include "VS_Setting.h" } else { m_VSDipValue = 0; m_VSDipTable = vsdip_default; } Reset(); // ゲーム固有のデフォルトオプションを設定(設定戻す時に使う為) GameOption.defRenderMethod = (INT)GetRenderMethod(); GameOption.defIRQtype = (INT)GetIrqType(); GameOption.defFrameIRQ = GetFrameIRQmode(); GameOption.defVideoMode = GetVideoMode(); // 設定をロードして設定する(エントリが無ければデフォルトが入る) if (rom->GetMapperNo() != 20) { GameOption.Load(rom->GetPROM_CRC()); } else { GameOption.Load(rom->GetGameID(), rom->GetMakerID()); } SetRenderMethod((RENDERMETHOD)GameOption.nRenderMethod); SetIrqType(GameOption.nIRQtype); SetFrameIRQmode(GameOption.bFrameIRQ); SetVideoMode(GameOption.bVideoMode); } catch (CHAR* str) { DELETEPTR(cpu); DELETEPTR(ppu); DELETEPTR(apu); DELETEPTR(pad); DELETEPTR(rom); DELETEPTR(mapper); throw str; #ifndef _DEBUG } catch (...) { DELETEPTR(cpu); DELETEPTR(ppu); DELETEPTR(apu); DELETEPTR(pad); DELETEPTR(rom); DELETEPTR(mapper); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } DEBUGOUT("Starting emulation!\n"); } NES::~NES() { MovieStop(); SaveSRAM(); SaveDISK(); SaveTurboFile(); DEBUGOUT("Free NES..."); DELETEPTR(cpu); DELETEPTR(ppu); DELETEPTR(apu); DELETEPTR(pad); DELETEPTR(rom); DELETEPTR(mapper); DEBUGOUT("Ok.\n"); } void NES::SetVideoMode(BOOL bMode) { bVideoMode = bMode; if (!bVideoMode) { nescfg = &NESCONFIG_NTSC; } else { nescfg = &NESCONFIG_PAL; } apu->SoundSetup(); } void NES::Reset() { SaveSRAM(); SaveDISK(); SaveTurboFile(); // RAM Clear ZEROMEMORY(RAM, sizeof(RAM)); if (rom->GetPROM_CRC() == 0x29401686) { // Minna no Taabou no Nakayoshi Dai Sakusen(J) ::memset(RAM, 0xFF, sizeof(RAM)); } // RAM set if (!rom->IsSAVERAM() && rom->GetMapperNo() != 20) { ::memset(WRAM, 0xFF, sizeof(WRAM)); } ZEROMEMORY(CRAM, sizeof(CRAM)); ZEROMEMORY(VRAM, sizeof(VRAM)); ZEROMEMORY(SPRAM, sizeof(SPRAM)); ZEROMEMORY(BGPAL, sizeof(BGPAL)); ZEROMEMORY(SPPAL, sizeof(SPPAL)); ZEROMEMORY(CPUREG, sizeof(CPUREG)); ZEROMEMORY(PPUREG, sizeof(PPUREG)); m_bDiskThrottle = FALSE; SetRenderMethod(PRE_RENDER); if (rom->IsPAL()) { SetVideoMode(TRUE); } PROM = rom->GetPROM(); VROM = rom->GetVROM(); PROM_8K_SIZE = rom->GetPROM_SIZE() * 2; PROM_16K_SIZE = rom->GetPROM_SIZE(); PROM_32K_SIZE = rom->GetPROM_SIZE() / 2; VROM_1K_SIZE = rom->GetVROM_SIZE() * 8; VROM_2K_SIZE = rom->GetVROM_SIZE() * 4; VROM_4K_SIZE = rom->GetVROM_SIZE() * 2; VROM_8K_SIZE = rom->GetVROM_SIZE(); // デフォルトバンク if (VROM_8K_SIZE) { SetVROM_8K_Bank(0); } else { SetCRAM_8K_Bank(0); } // ミラー if (rom->Is4SCREEN()) { SetVRAM_Mirror(VRAM_MIRROR4); } else if (rom->IsVMIRROR()) { SetVRAM_Mirror(VRAM_VMIRROR); } else { SetVRAM_Mirror(VRAM_HMIRROR); } apu->SelectExSound(0); ppu->Reset(); mapper->Reset(); // Trainer if (rom->IsTRAINER()) { ::memcpy(WRAM + 0x1000, rom->GetTRAINER(), 512); } pad->Reset(); cpu->Reset(); apu->Reset(); if (rom->IsNSF()) { mapper->Reset(); } base_cycles = emul_cycles = 0; } void NES::SoftReset() { pad->Reset(); cpu->Reset(); apu->Reset(); if (rom->IsNSF()) { mapper->Reset(); } m_bDiskThrottle = FALSE; base_cycles = emul_cycles = 0; } INT CPU_CALL_COUNT = 0; void NES::EmulationCPU(INT basecycles) { INT cycles; base_cycles += basecycles; #if !ASMVER cycles = (INT)((base_cycles / 12) - emul_cycles); #else SQWORD& rBase = base_cycles; SQWORD& rEmul = emul_cycles; __asm { // check if high DWORD changed mov edi, 12 mov eax, cache mov ebx, DWORD PTR[rBase] mov ecx, DWORD PTR[rEmul] cmp eax, [ebx + 4] jz low_dword // high_dword: // temp = (base_cycles/12) // Calculate upper DWORD and remainder xor edx, edx mov eax, [ebx + 4] mov cache, eax div edi mov cache_hiRem, edx low_dword : // Calculate bottom DWORD with upper remainder mov edx, cache_hiRem mov eax, [ebx] div edi // diff: // cycles = temp - emul_cycles // eax carried from DIV above xor edx, edx sub edx, [ecx] add eax, edx mov cycles, eax } #endif if (cycles > 0) { INT cycleAdd = cpu->EXEC(cycles); DEBUGOUT("[%d] add:[%d]\n",CPU_CALL_COUNT,cycleAdd); emul_cycles += cycleAdd; } CPU_CALL_COUNT++; } void NES::EmulationCPU_BeforeNMI(INT cycles) { base_cycles += cycles; emul_cycles += cpu->EXEC(cycles / 12); } /* 描画シーケンス 0 ダミースキャンライン(描画しない) 1 - 239 描画 240 ダミースキャンライン,VBLANKフラグON 241 VINT期間,NMI発生 242-261 VINT期間 261 VINT期間,VBLANKフラグOFF */ INT FrameCount = 0; void NES::EmulateFrame(BOOL bDraw) { FrameCount++; INT scanline = 0; // NSFプレイヤの時 if (rom->IsNSF()) { EmulateNSF(); return; } // Cheat CheatCodeProcess(); // NES_scanline = scanline; if (RenderMethod != TILE_RENDER) { bZapper = FALSE; while (TRUE) { ppu->SetRenderScanline(scanline); if (scanline == 0) { // ダミースキャンライン if (RenderMethod < POST_RENDER) { EmulationCPU(nescfg->ScanlineCycles); ppu->FrameStart(); ppu->ScanlineNext(); mapper->HSync(scanline); ppu->ScanlineStart(); } else { EmulationCPU(nescfg->HDrawCycles); ppu->FrameStart(); ppu->ScanlineNext(); mapper->HSync(scanline); EmulationCPU(FETCH_CYCLES * 32); ppu->ScanlineStart(); EmulationCPU(FETCH_CYCLES * 10 + nescfg->ScanlineEndCycles); } } else if (scanline < 240) { if (RenderMethod < POST_RENDER) { if (RenderMethod == POST_ALL_RENDER) EmulationCPU(nescfg->ScanlineCycles); if (bDraw) { ppu->Scanline(scanline, Config.graphics.bAllSprite, Config.graphics.bLeftClip); } else { if (pad->IsZapperMode() && scanline == ZapperY) { ppu->Scanline(scanline, Config.graphics.bAllSprite, Config.graphics.bLeftClip); } else { if (!ppu->IsSprite0(scanline)) { ppu->DummyScanline(scanline); } else { ppu->Scanline(scanline, Config.graphics.bAllSprite, Config.graphics.bLeftClip); } } } ppu->ScanlineNext(); // これの位置でラスター系は画面が違う if (RenderMethod == PRE_ALL_RENDER) EmulationCPU(nescfg->ScanlineCycles); // ppu->ScanlineNext(); // これの位置でラスター系は画面が違う mapper->HSync(scanline); ppu->ScanlineStart(); } else { if (RenderMethod == POST_RENDER) EmulationCPU(nescfg->HDrawCycles); if (bDraw) { ppu->Scanline(scanline, Config.graphics.bAllSprite, Config.graphics.bLeftClip); } else { if (pad->IsZapperMode() && scanline == ZapperY) { ppu->Scanline(scanline, Config.graphics.bAllSprite, Config.graphics.bLeftClip); } else { if (!ppu->IsSprite0(scanline)) { ppu->DummyScanline(scanline); } else { ppu->Scanline(scanline, Config.graphics.bAllSprite, Config.graphics.bLeftClip); } } } if (RenderMethod == PRE_RENDER) EmulationCPU(nescfg->HDrawCycles); ppu->ScanlineNext(); mapper->HSync(scanline); EmulationCPU(FETCH_CYCLES * 32); ppu->ScanlineStart(); EmulationCPU(FETCH_CYCLES * 10 + nescfg->ScanlineEndCycles); } } else if (scanline == 240) { mapper->VSync(); if (RenderMethod < POST_RENDER) { EmulationCPU(nescfg->ScanlineCycles); mapper->HSync(scanline); } else { EmulationCPU(nescfg->HDrawCycles); mapper->HSync(scanline); EmulationCPU(nescfg->HBlankCycles); } } else if (scanline <= nescfg->TotalScanlines - 1) { pad->VSync(); // VBLANK期間 if (scanline == nescfg->TotalScanlines - 1) { ppu->VBlankEnd(); } if (RenderMethod < POST_RENDER) { if (scanline == 241) { ppu->VBlankStart(); if (PPUREG[0] & PPU_VBLANK_BIT) { cpu->NMI(); } } EmulationCPU(nescfg->ScanlineCycles); mapper->HSync(scanline); } else { if (scanline == 241) { ppu->VBlankStart(); if (PPUREG[0] & PPU_VBLANK_BIT) { cpu->NMI(); } } EmulationCPU(nescfg->HDrawCycles); mapper->HSync(scanline); EmulationCPU(nescfg->HBlankCycles); } if (scanline == nescfg->TotalScanlines - 1) { break; } } if (pad->IsZapperMode()) { if (scanline == ZapperY) bZapper = TRUE; else bZapper = FALSE; } scanline++; NES_scanline = scanline; } } else { bZapper = FALSE; while (TRUE) { ppu->SetRenderScanline(scanline); if (scanline == 0) { // ダミースキャンライン // H-Draw (4fetches*32) EmulationCPU(FETCH_CYCLES * 128); ppu->FrameStart(); ppu->ScanlineNext(); EmulationCPU(FETCH_CYCLES * 10); mapper->HSync(scanline); EmulationCPU(FETCH_CYCLES * 22); ppu->ScanlineStart(); EmulationCPU(FETCH_CYCLES * 10 + nescfg->ScanlineEndCycles); } else if (scanline < 240) { // スクリーン描画(Scanline 1〜239) if (bDraw) { ppu->Scanline(scanline, Config.graphics.bAllSprite, Config.graphics.bLeftClip); ppu->ScanlineNext(); EmulationCPU(FETCH_CYCLES * 10); mapper->HSync(scanline); EmulationCPU(FETCH_CYCLES * 22); ppu->ScanlineStart(); EmulationCPU(FETCH_CYCLES * 10 + nescfg->ScanlineEndCycles); } else { if (pad->IsZapperMode() && scanline == ZapperY) { ppu->Scanline(scanline, Config.graphics.bAllSprite, Config.graphics.bLeftClip); ppu->ScanlineNext(); EmulationCPU(FETCH_CYCLES * 10); mapper->HSync(scanline); EmulationCPU(FETCH_CYCLES * 22); ppu->ScanlineStart(); EmulationCPU(FETCH_CYCLES * 10 + nescfg->ScanlineEndCycles); } else { if (!ppu->IsSprite0(scanline)) { // H-Draw (4fetches*32) EmulationCPU(FETCH_CYCLES * 128); ppu->DummyScanline(scanline); ppu->ScanlineNext(); EmulationCPU(FETCH_CYCLES * 10); mapper->HSync(scanline); EmulationCPU(FETCH_CYCLES * 22); ppu->ScanlineStart(); EmulationCPU(FETCH_CYCLES * 10 + nescfg->ScanlineEndCycles); } else { ppu->Scanline(scanline, Config.graphics.bAllSprite, Config.graphics.bLeftClip); ppu->ScanlineNext(); EmulationCPU(FETCH_CYCLES * 10); mapper->HSync(scanline); EmulationCPU(FETCH_CYCLES * 22); ppu->ScanlineStart(); EmulationCPU(FETCH_CYCLES * 10 + nescfg->ScanlineEndCycles); } } } } else if (scanline == 240) { // ダミースキャンライン (Scanline 240) mapper->VSync(); EmulationCPU(nescfg->HDrawCycles); // H-Sync mapper->HSync(scanline); EmulationCPU(nescfg->HBlankCycles); } else if (scanline <= nescfg->TotalScanlines - 1) { pad->VSync(); // VBLANK期間 if (scanline == nescfg->TotalScanlines - 1) { ppu->VBlankEnd(); } if (scanline == 241) { ppu->VBlankStart(); if (PPUREG[0] & PPU_VBLANK_BIT) { cpu->NMI(); } } EmulationCPU(nescfg->HDrawCycles); // H-Sync mapper->HSync(scanline); EmulationCPU(nescfg->HBlankCycles); if (scanline == nescfg->TotalScanlines - 1) { break; } } if (pad->IsZapperMode()) { if (scanline == ZapperY) bZapper = TRUE; else bZapper = FALSE; } scanline++; NES_scanline = scanline; } } // Movie pad // if( Config.movie.bPadDisplay && bDraw ) { // DrawPad(); // } // Movie pad if (bDraw) { DrawPad(); } #if NES_PROFILER { CHAR str[256]; ::wsprintf(str, "Cyc:%10d", m_dwProfileCycle); DrawString(9, 240 - 32, str, 0x1F); DrawString(8, 240 - 33, str, 0x30); ::wsprintf(str, "Ave:%10d", m_dwProfileAveCycle); DrawString(9, 240 - 23, str, 0x1F); DrawString(8, 240 - 24, str, 0x30); ::wsprintf(str, "Max:%10d", m_dwProfileMaxCycle); DrawString(9, 240 - 14, str, 0x1F); DrawString(8, 240 - 15, str, 0x30); } #endif } void NES::EmulateNSF() { R6502 reg; ppu->Reset(); mapper->VSync(); //DEBUGOUT( "Frame\n" ); if (m_bNsfPlaying) { if (m_bNsfInit) { ZEROMEMORY(RAM, sizeof(RAM)); if (!(rom->GetNsfHeader()->ExtraChipSelect & 0x04)) { ZEROMEMORY(WRAM, 0x2000); } apu->Reset(); apu->Write(0x4015, 0x0F); apu->Write(0x4017, 0xC0); apu->ExWrite(0x4080, 0x80); // FDS Volume 0 apu->ExWrite(0x408A, 0xE8); // FDS Envelope Speed cpu->GetContext(reg); reg.PC = 0x4710; // Init Address reg.A = (BYTE)m_nNsfSongNo; reg.X = (BYTE)m_nNsfSongMode; reg.Y = 0; reg.S = 0xFF; reg.P = Z_FLAG | R_FLAG | I_FLAG; cpu->SetContext(reg); // 安全対策を兼ねてあえてループに(1秒分) for (INT i = 0; i < nescfg->TotalScanlines * 60; i++) { EmulationCPU(nescfg->ScanlineCycles); cpu->GetContext(reg); // 無限ループに入ったことを確認したら抜ける if (reg.PC == 0x4700) { break; } } m_bNsfInit = FALSE; } cpu->GetContext(reg); // 無限ループに入っていたら再設定する if (reg.PC == 0x4700) { reg.PC = 0x4720; // Play Address reg.A = 0; reg.S = 0xFF; cpu->SetContext(reg); } for (INT i = 0; i < nescfg->TotalScanlines; i++) { EmulationCPU(nescfg->ScanlineCycles); } } else { cpu->GetContext(reg); reg.PC = 0x4700; // 無限ループ reg.S = 0xFF; cpu->SetContext(reg); EmulationCPU(nescfg->ScanlineCycles * nescfg->TotalScanlines); } } void NES::SetNsfPlay(INT songno, INT songmode) { m_bNsfPlaying = TRUE; m_bNsfInit = TRUE; m_nNsfSongNo = songno; m_nNsfSongMode = songmode; } void NES::SetNsfStop() { m_bNsfPlaying = FALSE; apu->Reset(); } void NES::Clock(INT cycles) { Tape(cycles); Barcode(cycles); } BYTE NES::Read(WORD addr) { switch (addr >> 13) { case 0x00: // $0000-$1FFF return RAM[addr & 0x07FF]; case 0x01: // $2000-$3FFF return ppu->Read(addr & 0xE007); case 0x02: // $4000-$5FFF if (addr < 0x4100) { return ReadReg(addr); } else { return mapper->ReadLow(addr); } break; case 0x03: // $6000-$7FFF return mapper->ReadLow(addr); case 0x04: // $8000-$9FFF case 0x05: // $A000-$BFFF case 0x06: // $C000-$DFFF case 0x07: // $E000-$FFFF return CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; } return 0x00; // Warning予防 } void NES::SetRenderMethod(RENDERMETHOD type) { RenderMethod = type; } void NES::Write(WORD addr, BYTE data) { switch (addr >> 13) { case 0x00: // $0000-$1FFF RAM[addr & 0x07FF] = data; break; case 0x01: // $2000-$3FFF if (!rom->IsNSF()) { ppu->Write(addr & 0xE007, data); } break; case 0x02: // $4000-$5FFF if (addr < 0x4100) { WriteReg(addr, data); } else { mapper->WriteLow(addr, data); } break; case 0x03: // $6000-$7FFF mapper->WriteLow(addr, data); break; case 0x04: // $8000-$9FFF case 0x05: // $A000-$BFFF case 0x06: // $C000-$DFFF case 0x07: // $E000-$FFFF mapper->Write(addr, data); GenieCodeProcess(); break; } } BYTE NES::ReadReg(WORD addr) { switch (addr & 0xFF) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: return apu->Read(addr); break; case 0x15: return apu->Read(addr); break; case 0x14: return addr & 0xFF; break; case 0x16: if (rom->IsVSUNISYSTEM()) { return pad->Read(addr); } else { return pad->Read(addr) | 0x40 | m_TapeOut; } break; case 0x17: if (rom->IsVSUNISYSTEM()) { return pad->Read(addr); } else { return pad->Read(addr) | apu->Read(addr); } break; default: return mapper->ExRead(addr); break; } } void NES::WriteReg(WORD addr, BYTE data) { switch (addr & 0xFF) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: case 0x15: apu->Write(addr, data); CPUREG[addr & 0xFF] = data; break; case 0x14: ppu->DMA(data); cpu->DMA(514); // DMA Pending cycle CPUREG[addr & 0xFF] = data; break; case 0x16: mapper->ExWrite(addr, data); // For VS-Unisystem pad->Write(addr, data); CPUREG[addr & 0xFF] = data; m_TapeIn = data; break; case 0x17: CPUREG[addr & 0xFF] = data; pad->Write(addr, data); apu->Write(addr, data); break; // VirtuaNES固有ポート case 0x18: apu->Write(addr, data); break; #if NES_PROFILER case 0x1D: m_dwProfileAveCycle = 0; m_dwProfileMaxCycle = 0; break; case 0x1E: m_dwProfileTempCycle = cpu->GetTotalCycles(); break; case 0x1F: m_dwProfileCycle = cpu->GetTotalCycles() - m_dwProfileTempCycle - 4; if (!m_dwProfileAveCycle) { m_dwProfileAveCycle += m_dwProfileCycle; } else { m_dwProfileAveCycle += m_dwProfileCycle; m_dwProfileAveCycle /= 2; } if (m_dwProfileMaxCycle < m_dwProfileCycle) { m_dwProfileMaxCycle = m_dwProfileCycle; } break; #endif #if 0 case 0x1C: m_dwProfileTempCycle = cpu->GetTotalCycles(); break; case 0x1D: m_dwProfileCycle = cpu->GetTotalCycles() - m_dwProfileTempCycle - 4; m_dwProfileTotalCycle += m_dwProfileCycle; m_dwProfileTotalCount++; break; case 0x1E: m_dwProfileTotalCount = 0; m_dwProfileTotalCycle = 0; m_dwTotalTempCycle = cpu->GetTotalCycles(); break; case 0x1F: m_dwTotalCycle = cpu->GetTotalCycles() - m_dwTotalTempCycle - 4; break; #endif default: mapper->ExWrite(addr, data); break; } } void NES::LoadSRAM() { if (rom->IsNSF()) return; ZEROMEMORY(WRAM, sizeof(WRAM)); if (!rom->IsSAVERAM()) return; string pathstr, tempstr; if (Config.path.bSavePath) { pathstr = CPathlib::CreatePath(CApp::GetModulePath(), Config.path.szSavePath); } else { pathstr = rom->GetRomPath(); } tempstr = CPathlib::MakePathExt(pathstr.c_str(), rom->GetRomName(), "sav"); DEBUGOUT("Path: %s\n", tempstr.c_str()); FILE* fp = NULL; try { if (!(fp = ::fopen(tempstr.c_str(), "rb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, tempstr.c_str()); throw szErrorString; } DEBUGOUT("Loading SAVERAM..."); LONG size; // ファイルサイズ取得 ::fseek(fp, 0, SEEK_END); size = ftell(fp); ::fseek(fp, 0, SEEK_SET); if (size <= 128 * 1024) { if (::fread(WRAM, size, 1, fp) != 1) throw "File Read error."; } DEBUGOUT("Ok.\n"); FCLOSE(fp); } catch (CHAR* str) { FCLOSE(fp); DEBUGOUT("Loading SAVERAM Error.\n"); DEBUGOUT("%s\n", str); // throw str; #ifndef _DEBUG } catch (...) { FCLOSE(fp); DEBUGOUT("Loading SAVERAM Error.\n"); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } } void NES::SaveSRAM() { INT i; if (rom->IsNSF()) return; if (!rom->IsSAVERAM()) return; for (i = 0; i < SAVERAM_SIZE; i++) { if (WRAM[i] != 0x00) break; } if (i < SAVERAM_SIZE) { DEBUGOUT("Saving SAVERAM...\n"); string pathstr, tempstr; if (Config.path.bSavePath) { pathstr = CPathlib::CreatePath(CApp::GetModulePath(), Config.path.szSavePath); ::CreateDirectory(pathstr.c_str(), NULL); } else { pathstr = rom->GetRomPath(); } tempstr = CPathlib::MakePathExt(pathstr.c_str(), rom->GetRomName(), "sav"); DEBUGOUT("Path: %s\n", tempstr.c_str()); FILE* fp = NULL; try { if (!(fp = ::fopen(tempstr.c_str(), "wb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, tempstr.c_str()); throw szErrorString; } if (::fwrite(WRAM, SAVERAM_SIZE, 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } DEBUGOUT("Ok.\n"); FCLOSE(fp); } catch (CHAR* str) { DEBUGOUT("Writing SAVERAM Error.\n"); FCLOSE(fp); throw str; #ifndef _DEBUG } catch (...) { DEBUGOUT("Writing SAVERAM Error.\n"); FCLOSE(fp); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } } } void NES::LoadDISK() { if (rom->GetMapperNo() != 20) return; BOOL bExit = FALSE; INT i, j, diskno; FILE* fp = NULL; DISKIMGFILEHDR ifh; DISKIMGHDR hdr; LPBYTE disk; WORD Version; string pathstr, tempstr; if (Config.path.bSavePath) { pathstr = CPathlib::CreatePath(CApp::GetModulePath(), Config.path.szSavePath); } else { pathstr = rom->GetRomPath(); } tempstr = CPathlib::MakePathExt(pathstr.c_str(), rom->GetRomName(), "dsv"); DEBUGOUT("Path: %s\n", tempstr.c_str()); try { if (!(fp = ::fopen(tempstr.c_str(), "rb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, tempstr.c_str()); throw szErrorString; } if (::fread(&ifh, sizeof(DISKIMGFILEHDR), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } if (::memcmp(ifh.ID, "VirtuaNES DI", sizeof(ifh.ID)) == 0) { if (ifh.BlockVersion < 0x0100 && ifh.BlockVersion > 0x200) { // 未対応形式です throw CApp::GetErrorString(IDS_ERROR_UNSUPPORTFORMAT); } Version = ifh.BlockVersion; } else { // 未対応形式です throw CApp::GetErrorString(IDS_ERROR_UNSUPPORTFORMAT); } if (Version == 0x0100) { // Ver0.24以前 if (ifh.DiskNumber > 4) { // 未対応形式です throw CApp::GetErrorString(IDS_ERROR_UNSUPPORTFORMAT); } for (i = 0; i < (INT)ifh.DiskNumber; i++) { if (::fread(&hdr, sizeof(DISKIMGHDR), 1, fp) != 1) { if (i == 0) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } else { break; } } if (::memcmp(hdr.ID, "SIDE0A", sizeof(hdr.ID)) == 0) { diskno = 0; } else if (::memcmp(hdr.ID, "SIDE0B", sizeof(hdr.ID)) == 0) { diskno = 1; } else if (::memcmp(hdr.ID, "SIDE1A", sizeof(hdr.ID)) == 0) { diskno = 2; } else if (::memcmp(hdr.ID, "SIDE1B", sizeof(hdr.ID)) == 0) { diskno = 3; } else { // 未対応形式です throw CApp::GetErrorString(IDS_ERROR_UNSUPPORTFORMAT); } for (j = 0; j < 16; j++) { if (hdr.DiskTouch[j]) { disk = rom->GetPROM() + 16 + 65500 * diskno + (4 * 1024) * j; if (j < 15) { if (::fread(disk, 4 * 1024, 1, fp) != 1) { bExit = TRUE; // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } else { if (::fread(disk, 4 * 1024 - 36, 1, fp) != 1) { bExit = TRUE; // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } } } } } else if (Version == 0x0200 || Version == 0x0210) { // Ver0.30以降 DISKFILEHDR dfh; LPBYTE lpDisk = rom->GetPROM(); LPBYTE lpWrite = rom->GetDISK(); LONG DiskSize = 16 + 65500 * rom->GetDiskNo(); DWORD pos; BYTE data; // 書き換えFLAG消去 ::ZeroMemory(lpWrite, 16 + 65500 * rom->GetDiskNo()); // ヘッダ読み直し if (::fseek(fp, 0, SEEK_SET)) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } if (::fread(&dfh, sizeof(DISKFILEHDR), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } if (Config.emulator.bCrcCheck) { // 現在ロード中のタイトルと違うかをチェック if (dfh.ProgID != rom->GetGameID() || dfh.MakerID != (WORD)rom->GetMakerID() || dfh.DiskNo != (WORD)rom->GetDiskNo()) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } for (i = 0; i < dfh.DifferentSize; i++) { if (::fread(&pos, sizeof(DWORD), 1, fp) != 1) { // ファイルの読み込みに失敗しました bExit = TRUE; throw CApp::GetErrorString(IDS_ERROR_READ); } data = (BYTE)(pos >> 24); pos &= 0x00FFFFFF; if (pos >= 16 && pos < DiskSize) { lpDisk[pos] = data; lpWrite[pos] = 0xFF; } } } FCLOSE(fp); } catch (CHAR* str) { FCLOSE(fp); DEBUGOUT("%s\n", str); if (bExit) throw str; #ifndef _DEBUG } catch (...) { FCLOSE(fp); DEBUGOUT("Loading DISKIMAGE Error.\n"); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } } void NES::SaveDISK() { if (rom->GetMapperNo() != 20) return; INT i; FILE* fp = NULL; DISKFILEHDR ifh; LPBYTE lpDisk = rom->GetPROM(); LPBYTE lpWrite = rom->GetDISK(); LONG DiskSize = 16 + 65500 * rom->GetDiskNo(); DWORD data; try { ::ZeroMemory(&ifh, sizeof(ifh)); ::memcpy(ifh.ID, "VirtuaNES DI", sizeof(ifh.ID)); ifh.BlockVersion = 0x0210; ifh.ProgID = rom->GetGameID(); ifh.MakerID = (WORD)rom->GetMakerID(); ifh.DiskNo = (WORD)rom->GetDiskNo(); // 相違数をカウント for (i = 16; i < DiskSize; i++) { if (lpWrite[i]) ifh.DifferentSize++; } if (!ifh.DifferentSize) return; string pathstr, tempstr; if (Config.path.bSavePath) { pathstr = CPathlib::CreatePath(CApp::GetModulePath(), Config.path.szSavePath); ::CreateDirectory(pathstr.c_str(), NULL); } else { pathstr = rom->GetRomPath(); } tempstr = CPathlib::MakePathExt(pathstr.c_str(), rom->GetRomName(), "dsv"); DEBUGOUT("Path: %s\n", tempstr.c_str()); if (!(fp = ::fopen(tempstr.c_str(), "wb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); ::wsprintf(szErrorString, szErrStr, tempstr.c_str()); throw szErrorString; } if (::fwrite(&ifh, sizeof(DISKFILEHDR), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } for (i = 16; i < DiskSize; i++) { if (lpWrite[i]) { data = i & 0x00FFFFFF; data |= ((DWORD)lpDisk[i] & 0xFF) << 24; // Write File if (::fwrite(&data, sizeof(DWORD), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } } FCLOSE(fp); } catch (CHAR* str) { FCLOSE(fp); DEBUGOUT("%s\n", str); #ifndef _DEBUG } catch (...) { FCLOSE(fp); DEBUGOUT("Saving DISKIMAGE Error.\n"); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } } void NES::LoadTurboFile() { ZEROMEMORY(ERAM, sizeof(ERAM)); if (pad->GetExController() != PAD::EXCONTROLLER_TURBOFILE) return; string pathstr, tempstr; if (Config.path.bSavePath) { pathstr = CPathlib::CreatePath(CApp::GetModulePath(), Config.path.szSavePath); } else { pathstr = rom->GetRomPath(); } tempstr = CPathlib::MakePathExt(pathstr.c_str(), "TurboFile", "vtf"); DEBUGOUT("Path: %s\n", tempstr.c_str()); FILE* fp = NULL; try { if (!(fp = ::fopen(tempstr.c_str(), "rb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, tempstr.c_str()); throw szErrorString; } DEBUGOUT("Loading TURBOFILE..."); LONG size; // ファイルサイズ取得 ::fseek(fp, 0, SEEK_END); size = ftell(fp); ::fseek(fp, 0, SEEK_SET); if (size > 32 * 1024) { size = 32 * 1024; } if (::fread(ERAM, size, 1, fp) != 1) throw "File Read error."; DEBUGOUT("Ok.\n"); FCLOSE(fp); } catch (CHAR* str) { FCLOSE(fp); DEBUGOUT("Loading TurboFile Error.\n"); DEBUGOUT("%s\n", str); // throw str; #ifndef _DEBUG } catch (...) { FCLOSE(fp); DEBUGOUT("Loading TurboFile Error.\n"); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } } void NES::SaveTurboFile() { INT i; if (pad->GetExController() != PAD::EXCONTROLLER_TURBOFILE) return; for (i = 0; i < sizeof(ERAM); i++) { if (ERAM[i] != 0x00) break; } if (i < sizeof(ERAM)) { DEBUGOUT("Saving TURBOFILE...\n"); string pathstr, tempstr; if (Config.path.bSavePath) { pathstr = CPathlib::CreatePath(CApp::GetModulePath(), Config.path.szSavePath); ::CreateDirectory(pathstr.c_str(), NULL); } else { pathstr = rom->GetRomPath(); } tempstr = CPathlib::MakePathExt(pathstr.c_str(), "TurboFile", "vtf"); DEBUGOUT("Path: %s\n", tempstr.c_str()); FILE* fp = NULL; try { if (!(fp = ::fopen(tempstr.c_str(), "wb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, tempstr.c_str()); throw szErrorString; } if (::fwrite(ERAM, sizeof(ERAM), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } DEBUGOUT("Ok.\n"); FCLOSE(fp); } catch (CHAR* str) { DEBUGOUT("Writing TurboFile Error.\n"); FCLOSE(fp); throw str; #ifndef _DEBUG } catch (...) { DEBUGOUT("Writing TurboFile Error.\n"); FCLOSE(fp); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } } } INT NES::IsStateFile(const char* fname, ROM* rom) { FILE* fp = NULL; FILEHDR2 header; if (!(fp = ::fopen(fname, "rb"))) return -1; if (::fread(&header, sizeof(header), 1, fp) != 1) { FCLOSE(fp); return -1; } FCLOSE(fp); if (::memcmp(header.ID, "VirtuaNES ST", sizeof(header.ID)) == 0) { if (header.BlockVersion < 0x0100) return 0; if (Config.emulator.bCrcCheck) { if (header.BlockVersion >= 0x200) { if (rom->GetMapperNo() != 20) { // FDS以外 if (header.Ext0 != rom->GetPROM_CRC()) { return IDS_ERROR_ILLEGALSTATECRC; // 違うじゃん } } else { // FDS if (header.Ext0 != rom->GetGameID() || header.Ext1 != (WORD)rom->GetMakerID() || header.Ext2 != (WORD)rom->GetDiskNo()) return IDS_ERROR_ILLEGALSTATECRC; // 違うじゃん } } } return 0; } return -1; } BOOL NES::LoadState(const char* fname) { FILE* fp = NULL; BOOL bRet = FALSE; if (rom->IsNSF()) return TRUE; try { if (!(fp = ::fopen(fname, "rb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, fname); throw szErrorString; } bRet = ReadState(fp); FCLOSE(fp); } catch (CHAR* str) { DEBUGOUT("State load error.\n"); DEBUGOUT("%s\n", str); FCLOSE(fp); return FALSE; #ifndef _DEBUG } catch (...) { DEBUGOUT("State load error.\n"); FCLOSE(fp); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } return bRet; } BOOL NES::SaveState(const char* fname) { FILE* fp = NULL; if (rom->IsNSF()) return TRUE; try { if (!(fp = ::fopen(fname, "wb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, fname); throw szErrorString; } WriteState(fp); FCLOSE(fp); } catch (CHAR* str) { DEBUGOUT("State save error.\n"); FCLOSE(fp); throw str; #ifndef _DEBUG } catch (...) { DEBUGOUT("State save error.\n"); FCLOSE(fp); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } return TRUE; } BOOL NES::ReadState(FILE* fp) { INT i; BOOL bHeader = FALSE; WORD Version = 0; BLOCKHDR hdr; INT type; while (TRUE) { // Read File if (::fread(&hdr, sizeof(BLOCKHDR), 1, fp) != 1) break; // File Header check if (!bHeader) { LPFILEHDR fh = (LPFILEHDR)&hdr; if (::memcmp(fh->ID, "VirtuaNES ST", sizeof(fh->ID)) == 0) { Version = fh->BlockVersion; if (Version == 0x0100) { // Ver0.24まで bHeader = TRUE; // 古い奴はムービー中はロード出来ません if (m_bMoviePlay) { return FALSE; } // 古い奴のFDSはロード出来ません if (rom->GetMapperNo() == 20) { // 未対応形式です throw CApp::GetErrorString(IDS_ERROR_UNSUPPORTFORMAT); } } else if (Version == 0x0200 || Version == 0x0210) { // Ver0.30以降 Ver0.60以降 FILEHDR2 hdr2; // ヘッダ部読み直し if (::fseek(fp, -sizeof(BLOCKHDR), SEEK_CUR)) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } // Read File if (::fread(&hdr2, sizeof(FILEHDR2), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } #if 0 if (Config.emulator.bCrcCheck) { // 現在ロード中のタイトルと違うかをチェック if (rom->GetMapperNo() != 20) { // FDS以外 if (hdr2.Ext0 != rom->GetPROM_CRC()) { return FALSE; // 違うじゃん } } else { // FDS if (hdr2.Ext0 != rom->GetGameID() || hdr2.Ext1 != (WORD)rom->GetMakerID() || hdr2.Ext2 != (WORD)rom->GetDiskNo()) return FALSE; // 違うじゃん } } #endif // ムービー中はファイルポインタとステップ数を // 変更して,ムービーを記録モードに変更 if (m_bMoviePlay || m_bMovieRec) { // 撮り直し可能? if (m_hedMovie.Control & 0x80) { if (hdr2.MovieOffset && hdr2.MovieStep) { if (m_bMoviePlay) { // 再生中 // ステートのステップ数が記録より進んでたらダメ if (hdr2.MovieStep > m_hedMovie.MovieStep) return FALSE; } else { // 記録中 // ステートのステップ数が現在より進んでたらダメ if (hdr2.MovieStep > m_MovieStep) return FALSE; } //DEBUGOUT( "LD STEP=%d POS=%d\n", hdr2.MovieStep, hdr2.MovieOffset ); m_bMoviePlay = FALSE; m_bMovieRec = TRUE; m_MovieStep = hdr2.MovieStep; m_hedMovie.RecordTimes++; // 撮り直し回数+1 if (::fseek(m_fpMovie, hdr2.MovieOffset, SEEK_SET)) { //DEBUGOUT( "MOVIE:STATE LOAD SEEK 失敗\n" ); // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } else { return FALSE; } } else { return FALSE; } } } bHeader = TRUE; continue; } } if (!bHeader) { // 未対応形式です throw CApp::GetErrorString(IDS_ERROR_UNSUPPORTFORMAT); } //DEBUGOUT( "HEADER ID=%8s\n", hdr.ID ); type = -1; if (::memcmp(hdr.ID, "REG DATA", sizeof(hdr.ID)) == 0) type = 0; if (::memcmp(hdr.ID, "RAM DATA", sizeof(hdr.ID)) == 0) type = 1; if (::memcmp(hdr.ID, "MMU DATA", sizeof(hdr.ID)) == 0) type = 2; if (::memcmp(hdr.ID, "MMC DATA", sizeof(hdr.ID)) == 0) type = 3; if (::memcmp(hdr.ID, "CTR DATA", sizeof(hdr.ID)) == 0) type = 4; if (::memcmp(hdr.ID, "SND DATA", sizeof(hdr.ID)) == 0) type = 5; if (rom->GetMapperNo() == 20) { if (::memcmp(hdr.ID, "DISKDATA", sizeof(hdr.ID)) == 0) type = 6; } if (::memcmp(hdr.ID, "EXCTRDAT", sizeof(hdr.ID)) == 0) type = 7; if (type == -1) { //DEBUGOUT( "UNKNOWN HEADER ID=%8s\n", hdr.ID ); break; } switch (type) { case 0: // REGISTER STATE { if (hdr.BlockVersion < 0x0200) { REGSTAT_O reg; if (::fread(®, sizeof(REGSTAT_O), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } // LOAD CPU STATE R6502 R; R.PC = reg.cpureg.cpu.PC; R.A = reg.cpureg.cpu.A; R.X = reg.cpureg.cpu.X; R.Y = reg.cpureg.cpu.Y; R.S = reg.cpureg.cpu.S; R.P = reg.cpureg.cpu.P; R.INT_pending = reg.cpureg.cpu.I; cpu->SetContext(R); // FrameIRQ = reg.cpureg.cpu.FrameIRQ; if (hdr.BlockVersion < 0x0110) { emul_cycles = 0; base_cycles = reg.cpureg.cpu.mod_cycles; } else if (hdr.BlockVersion == 0x0110) { // FrameIRQ_cycles = reg.cpureg.cpu.mod_cycles; emul_cycles = reg.cpureg.cpu.emul_cycles; base_cycles = reg.cpureg.cpu.base_cycles; } // LOAD PPU STATE PPUREG[0] = reg.ppureg.ppu.reg0; PPUREG[1] = reg.ppureg.ppu.reg1; PPUREG[2] = reg.ppureg.ppu.reg2; PPUREG[3] = reg.ppureg.ppu.reg3; PPU7_Temp = reg.ppureg.ppu.reg7; loopy_t = reg.ppureg.ppu.loopy_t; loopy_v = reg.ppureg.ppu.loopy_v; loopy_x = reg.ppureg.ppu.loopy_x; PPU56Toggle = reg.ppureg.ppu.toggle56; } else { REGSTAT reg; if (::fread(®, sizeof(REGSTAT), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } // LOAD CPU STATE R6502 R; R.PC = reg.cpureg.cpu.PC; R.A = reg.cpureg.cpu.A; R.X = reg.cpureg.cpu.X; R.Y = reg.cpureg.cpu.Y; R.S = reg.cpureg.cpu.S; R.P = reg.cpureg.cpu.P; R.INT_pending = reg.cpureg.cpu.I; cpu->SetContext(R); if (hdr.BlockVersion == 0x0200) { // FrameIRQ = reg.cpureg.cpu.FrameIRQ; // bFrameIRQ_occur = (reg.cpureg.cpu.FrameIRQ_occur!=0)?TRUE:FALSE; // FrameIRQ_cycles = reg.cpureg.cpu.FrameIRQ_cycles; } else { apu->SetFrameIRQ(reg.cpureg.cpu.FrameIRQ_cycles, reg.cpureg.cpu.FrameIRQ_count, reg.cpureg.cpu.FrameIRQ_type, reg.cpureg.cpu.FrameIRQ, reg.cpureg.cpu.FrameIRQ_occur); } emul_cycles = reg.cpureg.cpu.emul_cycles; base_cycles = reg.cpureg.cpu.base_cycles; cpu->SetDmaCycles((INT)reg.cpureg.cpu.DMA_cycles); // LOAD PPU STATE PPUREG[0] = reg.ppureg.ppu.reg0; PPUREG[1] = reg.ppureg.ppu.reg1; PPUREG[2] = reg.ppureg.ppu.reg2; PPUREG[3] = reg.ppureg.ppu.reg3; PPU7_Temp = reg.ppureg.ppu.reg7; loopy_t = reg.ppureg.ppu.loopy_t; loopy_v = reg.ppureg.ppu.loopy_v; loopy_x = reg.ppureg.ppu.loopy_x; PPU56Toggle = reg.ppureg.ppu.toggle56; } // APU STATE // キューを消す apu->QueueClear(); // APUステートを保存するようにしたのでヤメ // // DMCは止めないとまずい事が起こる // for( i = 0x4010; i <= 0x4013; i++ ) { // apu->Write( i, 0 ); // } } break; case 1: // RAM STATE { RAMSTAT ram; if (::fread(&ram, sizeof(RAMSTAT), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } ::memcpy(RAM, ram.RAM, sizeof(ram.RAM)); ::memcpy(BGPAL, ram.BGPAL, sizeof(ram.BGPAL)); ::memcpy(SPPAL, ram.SPPAL, sizeof(ram.SPPAL)); ::memcpy(SPRAM, ram.SPRAM, sizeof(ram.SPRAM)); if (rom->IsSAVERAM()) { if (::fread(WRAM, SAVERAM_SIZE, 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } } break; case 2: // BANK STATE { MMUSTAT mmu; if (::fread(&mmu, sizeof(MMUSTAT), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } if (hdr.BlockVersion == 0x100) { // ちょっと前のバージョン if (mmu.CPU_MEM_TYPE[3] == BANKTYPE_RAM || mmu.CPU_MEM_TYPE[3] == BANKTYPE_DRAM) { if (::fread(CPU_MEM_BANK[3], 8 * 1024, 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } else if (!rom->IsSAVERAM()) { SetPROM_8K_Bank(3, mmu.CPU_MEM_PAGE[3]); } // バンク0〜3以外ロード for (i = 4; i < 8; i++) { CPU_MEM_TYPE[i] = mmu.CPU_MEM_TYPE[i]; CPU_MEM_PAGE[i] = mmu.CPU_MEM_PAGE[i]; if (CPU_MEM_TYPE[i] == BANKTYPE_ROM) { SetPROM_8K_Bank(i, CPU_MEM_PAGE[i]); } else { if (::fread(CPU_MEM_BANK[i], 8 * 1024, 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } } } else if (hdr.BlockVersion == 0x200) { // 最新バージョン // SRAMがあっても全部ロードしなおし for (i = 3; i < 8; i++) { CPU_MEM_TYPE[i] = mmu.CPU_MEM_TYPE[i]; CPU_MEM_PAGE[i] = mmu.CPU_MEM_PAGE[i]; if (CPU_MEM_TYPE[i] == BANKTYPE_ROM) { SetPROM_8K_Bank(i, CPU_MEM_PAGE[i]); } else { if (::fread(CPU_MEM_BANK[i], 8 * 1024, 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } } } // VRAM if (::fread(VRAM, 4 * 1024, 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } // CRAM for (i = 0; i < 8; i++) { if (mmu.CRAM_USED[i] != 0) { if (::fread(&CRAM[0x1000 * i], 4 * 1024, 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } } // BANK for (i = 0; i < 12; i++) { if (mmu.PPU_MEM_TYPE[i] == BANKTYPE_VROM) { SetVROM_1K_Bank(i, mmu.PPU_MEM_PAGE[i]); } else if (mmu.PPU_MEM_TYPE[i] == BANKTYPE_CRAM) { SetCRAM_1K_Bank(i, mmu.PPU_MEM_PAGE[i]); } else if (mmu.PPU_MEM_TYPE[i] == BANKTYPE_VRAM) { SetVRAM_1K_Bank(i, mmu.PPU_MEM_PAGE[i]); } else { throw "Unknown bank types."; } } } break; case 3: // MMC STATE { MMCSTAT mmc; if (::fread(&mmc, sizeof(MMCSTAT), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } mapper->LoadState(mmc.mmcdata); } break; case 4: // CTR STATE { CTRSTAT ctr; if (::fread(&ctr, sizeof(CTRSTAT), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } pad->pad1bit = ctr.ctrreg.ctr.pad1bit; pad->pad2bit = ctr.ctrreg.ctr.pad2bit; pad->pad3bit = ctr.ctrreg.ctr.pad3bit; pad->pad4bit = ctr.ctrreg.ctr.pad4bit; pad->SetStrobe((ctr.ctrreg.ctr.strobe != 0) ? TRUE : FALSE); } break; case 5: // SND STATE { SNDSTAT snd; if (::fread(&snd, sizeof(SNDSTAT), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } apu->LoadState(snd.snddata); } break; // Disk Images // Ver0.30以降 case 6: { DISKDATA ddata; DWORD pos; BYTE data; LONG DiskSize = 16 + 65500 * rom->GetDiskNo(); LPBYTE lpDisk = rom->GetPROM(); LPBYTE lpWrite = rom->GetDISK(); // 書き換えFLAG消去 ::ZeroMemory(lpWrite, 16 + 65500 * rom->GetDiskNo()); if (::fread(&ddata, sizeof(DISKDATA), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } for (i = 0; i < ddata.DifferentSize; i++) { if (::fread(&pos, sizeof(DWORD), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } data = (BYTE)(pos >> 24); pos &= 0x00FFFFFF; if (pos >= 16 && pos < DiskSize) { lpDisk[pos] = data; lpWrite[pos] = 0xFF; } } } break; // EXCTR STATE case 7: { EXCTRSTAT exctr; if (::fread(&exctr, sizeof(EXCTRSTAT), 1, fp) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } pad->SetSyncExData(exctr.data); } break; } } return TRUE; } void NES::WriteState(FILE* fp) { INT i; // HEADER { FILEHDR2 hdr; ZEROMEMORY(&hdr, sizeof(FILEHDR2)); ::memcpy(hdr.ID, "VirtuaNES ST", sizeof(hdr.ID)); hdr.BlockVersion = 0x0200; if (rom->GetMapperNo() != 20) { hdr.Ext0 = rom->GetPROM_CRC(); } else { hdr.Ext0 = rom->GetGameID(); hdr.Ext1 = (WORD)rom->GetMakerID(); hdr.Ext2 = (WORD)rom->GetDiskNo(); } // ムービー再生や記録中であればその位置を記録する if (m_bMoviePlay || m_bMovieRec) { hdr.MovieStep = m_MovieStep; hdr.MovieOffset = ::ftell(m_fpMovie); //DEBUGOUT( "\nSV STEP=%d POS=%d\n", m_MovieStep, hdr.MovieOffset ); } // Write File if (::fwrite(&hdr, sizeof(FILEHDR2), 1, fp) != 1) // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } BLOCKHDR hdr; // REGISTER STATE { REGSTAT reg; ZEROMEMORY(&hdr, sizeof(BLOCKHDR)); ZEROMEMORY(®, sizeof(REGSTAT)); // Create Header ::memcpy(hdr.ID, "REG DATA", sizeof(hdr.ID)); hdr.BlockVersion = 0x0210; hdr.BlockSize = sizeof(REGSTAT); // SAVE CPU STATE R6502 R; cpu->GetContext(R); reg.cpureg.cpu.PC = R.PC; reg.cpureg.cpu.A = R.A; reg.cpureg.cpu.X = R.X; reg.cpureg.cpu.Y = R.Y; reg.cpureg.cpu.S = R.S; reg.cpureg.cpu.P = R.P; reg.cpureg.cpu.I = R.INT_pending; INT cycles; apu->GetFrameIRQ(cycles, reg.cpureg.cpu.FrameIRQ_count, reg.cpureg.cpu.FrameIRQ_type, reg.cpureg.cpu.FrameIRQ, reg.cpureg.cpu.FrameIRQ_occur); reg.cpureg.cpu.FrameIRQ_cycles = (LONG)cycles; // 参照がINTな為(ぉ reg.cpureg.cpu.DMA_cycles = (LONG)cpu->GetDmaCycles(); reg.cpureg.cpu.emul_cycles = emul_cycles; reg.cpureg.cpu.base_cycles = base_cycles; // SAVE PPU STATE reg.ppureg.ppu.reg0 = PPUREG[0]; reg.ppureg.ppu.reg1 = PPUREG[1]; reg.ppureg.ppu.reg2 = PPUREG[2]; reg.ppureg.ppu.reg3 = PPUREG[3]; reg.ppureg.ppu.reg7 = PPU7_Temp; reg.ppureg.ppu.loopy_t = loopy_t; reg.ppureg.ppu.loopy_v = loopy_v; reg.ppureg.ppu.loopy_x = loopy_x; reg.ppureg.ppu.toggle56 = PPU56Toggle; // Write File if (::fwrite(&hdr, sizeof(BLOCKHDR), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (::fwrite(®, sizeof(REGSTAT), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } // RAM STATE { RAMSTAT ram; DWORD size = 0; ::ZeroMemory(&hdr, sizeof(BLOCKHDR)); ::ZeroMemory(&ram, sizeof(RAMSTAT)); // SAVE RAM STATE ::memcpy(ram.RAM, RAM, sizeof(ram.RAM)); ::memcpy(ram.BGPAL, BGPAL, sizeof(ram.BGPAL)); ::memcpy(ram.SPPAL, SPPAL, sizeof(ram.SPPAL)); ::memcpy(ram.SPRAM, SPRAM, sizeof(ram.SPRAM)); // S-RAM STATE(使用/未使用に関わらず存在すればセーブする) if (rom->IsSAVERAM()) { size = SAVERAM_SIZE; } // Create Header ::memcpy(hdr.ID, "RAM DATA", sizeof(hdr.ID)); hdr.BlockVersion = 0x0100; hdr.BlockSize = size + sizeof(RAMSTAT); // Write File if (::fwrite(&hdr, sizeof(BLOCKHDR), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (::fwrite(&ram, sizeof(RAMSTAT), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (rom->IsSAVERAM()) { if (::fwrite(WRAM, SAVERAM_SIZE, 1, fp) != 1) // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } // BANK STATE { MMUSTAT mmu; DWORD size; ::ZeroMemory(&hdr, sizeof(BLOCKHDR)); ::ZeroMemory(&mmu, sizeof(MMUSTAT)); size = 0; // SAVE CPU MEMORY BANK DATA // BANK0,1,2はバンクセーブに関係なし // VirtuaNES0.30から // バンク3はSRAM使用に関わらずセーブ for (i = 3; i < 8; i++) { mmu.CPU_MEM_TYPE[i] = CPU_MEM_TYPE[i]; mmu.CPU_MEM_PAGE[i] = CPU_MEM_PAGE[i]; if (CPU_MEM_TYPE[i] == BANKTYPE_RAM || CPU_MEM_TYPE[i] == BANKTYPE_DRAM) { size += 8 * 1024; // 8K BANK } } // SAVE VRAM MEMORY DATA for (i = 0; i < 12; i++) { mmu.PPU_MEM_TYPE[i] = PPU_MEM_TYPE[i]; mmu.PPU_MEM_PAGE[i] = PPU_MEM_PAGE[i]; } size += 4 * 1024; // 1K BANK x 4 (VRAM) for (i = 0; i < 8; i++) { mmu.CRAM_USED[i] = CRAM_USED[i]; if (CRAM_USED[i] != 0) { size += 4 * 1024; // 4K BANK } } // Create Header ::memcpy(hdr.ID, "MMU DATA", sizeof(hdr.ID)); hdr.BlockVersion = 0x0200; hdr.BlockSize = size + sizeof(MMUSTAT); // Write File if (::fwrite(&hdr, sizeof(BLOCKHDR), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (::fwrite(&mmu, sizeof(MMUSTAT), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } // WRITE CPU RAM MEMORY BANK for (i = 3; i < 8; i++) { if (mmu.CPU_MEM_TYPE[i] != BANKTYPE_ROM) { if (::fwrite(CPU_MEM_BANK[i], 8 * 1024, 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } } // WRITE VRAM MEMORY(常に4K分すべて書き込む) if (::fwrite(VRAM, 4 * 1024, 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } // WRITE CRAM MEMORY for (i = 0; i < 8; i++) { if (CRAM_USED[i] != 0) { if (::fwrite(&CRAM[0x1000 * i], 4 * 1024, 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } } } // MMC STATE { MMCSTAT mmc; ::ZeroMemory(&hdr, sizeof(BLOCKHDR)); ::ZeroMemory(&mmc, sizeof(MMCSTAT)); // Create Header ::memcpy(hdr.ID, "MMC DATA", sizeof(hdr.ID)); hdr.BlockVersion = 0x0100; hdr.BlockSize = sizeof(MMCSTAT); if (mapper->IsStateSave()) { mapper->SaveState(mmc.mmcdata); // Write File if (::fwrite(&hdr, sizeof(BLOCKHDR), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (::fwrite(&mmc, sizeof(MMCSTAT), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } } // CONTROLLER STATE { CTRSTAT ctr; ::ZeroMemory(&hdr, sizeof(BLOCKHDR)); ::ZeroMemory(&ctr, sizeof(CTRSTAT)); // Create Header ::memcpy(hdr.ID, "CTR DATA", sizeof(hdr.ID)); hdr.BlockVersion = 0x0100; hdr.BlockSize = sizeof(CTRSTAT); ctr.ctrreg.ctr.pad1bit = pad->pad1bit; ctr.ctrreg.ctr.pad2bit = pad->pad2bit; ctr.ctrreg.ctr.pad3bit = pad->pad3bit; ctr.ctrreg.ctr.pad4bit = pad->pad4bit; ctr.ctrreg.ctr.strobe = pad->GetStrobe() ? 0xFF : 0; //DEBUGOUT( "SV pad1bit=%08X Strobe=%d\n", pad->pad1bit, pad->GetStrobe()?1:0 ); if (::fwrite(&hdr, sizeof(BLOCKHDR), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (::fwrite(&ctr, sizeof(CTRSTAT), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } // SND STATE { SNDSTAT snd; ::ZeroMemory(&hdr, sizeof(BLOCKHDR)); ::ZeroMemory(&snd, sizeof(SNDSTAT)); // Create Header ::memcpy(hdr.ID, "SND DATA", sizeof(hdr.ID)); hdr.BlockVersion = 0x0100; hdr.BlockSize = sizeof(SNDSTAT); apu->SaveState(snd.snddata); if (::fwrite(&hdr, sizeof(BLOCKHDR), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (::fwrite(&snd, sizeof(SNDSTAT), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } // DISKIMAGE STATE if (rom->GetMapperNo() == 20) { DISKDATA dsk; LPBYTE lpDisk = rom->GetPROM(); LPBYTE lpWrite = rom->GetDISK(); LONG DiskSize = 16 + 65500 * rom->GetDiskNo(); DWORD data; ::ZeroMemory(&hdr, sizeof(BLOCKHDR)); ::ZeroMemory(&dsk, sizeof(DISKDATA)); // 相違数をカウント for (i = 16; i < DiskSize; i++) { if (lpWrite[i]) dsk.DifferentSize++; } ::memcpy(hdr.ID, "DISKDATA", sizeof(hdr.ID)); hdr.BlockVersion = 0x0210; hdr.BlockSize = 0; // Write File if (::fwrite(&hdr, sizeof(BLOCKHDR), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } // Write File if (::fwrite(&dsk, sizeof(DISKDATA), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } for (i = 16; i < DiskSize; i++) { if (lpWrite[i]) { data = i & 0x00FFFFFF; data |= ((DWORD)lpDisk[i] & 0xFF) << 24; // Write File if (::fwrite(&data, sizeof(DWORD), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } } } // EXCTR STATE if (pad->GetExController()) { EXCTRSTAT exctr; ::ZeroMemory(&hdr, sizeof(BLOCKHDR)); ::ZeroMemory(&exctr, sizeof(EXCTRSTAT)); // Create Header ::memcpy(hdr.ID, "EXCTRDAT", sizeof(hdr.ID)); hdr.BlockVersion = 0x0100; hdr.BlockSize = sizeof(EXCTRSTAT); // Some excontrollers will default 0 exctr.data = pad->GetSyncExData(); if (::fwrite(&hdr, sizeof(BLOCKHDR), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (::fwrite(&exctr, sizeof(EXCTRSTAT), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } } INT NES::GetDiskNo() { return rom->GetDiskNo(); } void NES::SoundSetup() { apu->SoundSetup(); } void NES::Command(NESCOMMAND cmd) { CommandParam(cmd, 0); } BOOL NES::CommandParam(NESCOMMAND cmd, INT param) { switch (cmd) { case NESCMD_NONE: break; case NESCMD_DISK_THROTTLE_ON: if (Config.emulator.bDiskThrottle) { m_bDiskThrottle = TRUE; } break; case NESCMD_DISK_THROTTLE_OFF: m_bDiskThrottle = FALSE; break; case NESCMD_DISK_EJECT: mapper->ExCmdWrite(Mapper::EXCMDWR_DISKEJECT, 0); m_CommandRequest = (INT)cmd; break; case NESCMD_DISK_0A: if (rom->GetDiskNo() > 0) { mapper->ExCmdWrite(Mapper::EXCMDWR_DISKINSERT, 0); m_CommandRequest = (INT)cmd; } break; case NESCMD_DISK_0B: if (rom->GetDiskNo() > 1) { mapper->ExCmdWrite(Mapper::EXCMDWR_DISKINSERT, 1); m_CommandRequest = (INT)cmd; } break; case NESCMD_DISK_1A: if (rom->GetDiskNo() > 2) { mapper->ExCmdWrite(Mapper::EXCMDWR_DISKINSERT, 2); m_CommandRequest = (INT)cmd; } break; case NESCMD_DISK_1B: if (rom->GetDiskNo() > 3) { mapper->ExCmdWrite(Mapper::EXCMDWR_DISKINSERT, 3); m_CommandRequest = (INT)cmd; } break; case NESCMD_DISK_2A: if (rom->GetDiskNo() > 4) { mapper->ExCmdWrite(Mapper::EXCMDWR_DISKINSERT, 4); m_CommandRequest = (INT)cmd; } break; case NESCMD_DISK_2B: if (rom->GetDiskNo() > 5) { mapper->ExCmdWrite(Mapper::EXCMDWR_DISKINSERT, 5); m_CommandRequest = (INT)cmd; } break; case NESCMD_DISK_3A: if (rom->GetDiskNo() > 6) { mapper->ExCmdWrite(Mapper::EXCMDWR_DISKINSERT, 6); m_CommandRequest = (INT)cmd; } break; case NESCMD_DISK_3B: if (rom->GetDiskNo() > 7) { mapper->ExCmdWrite(Mapper::EXCMDWR_DISKINSERT, 7); m_CommandRequest = (INT)cmd; } break; case NESCMD_HWRESET: Reset(); m_CommandRequest = (INT)cmd; break; case NESCMD_SWRESET: SoftReset(); m_CommandRequest = (INT)cmd; break; case NESCMD_EXCONTROLLER: pad->SetExController(param & 0xFF); m_CommandRequest = 0x0100 | (param & 0xFF); break; case NESCMD_SOUND_MUTE: return apu->SetChannelMute((BOOL)param); // リターン値は変更後のミュート状態 } return TRUE; } BOOL NES::Snapshot() { FILE* fp = NULL; try { SYSTEMTIME now; ::GetLocalTime(&now); CHAR name[_MAX_PATH]; if (!Config.emulator.bPNGsnapshot) { sprintf(name, "%s %04d%02d%02d%02d%02d%02d%01d.bmp", rom->GetRomName(), now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond, now.wMilliseconds / 100); } else { sprintf(name, "%s %04d%02d%02d%02d%02d%02d%01d.png", rom->GetRomName(), now.wYear, now.wMonth, now.wDay, now.wHour, now.wMinute, now.wSecond, now.wMilliseconds / 100); } string pathstr, tempstr; if (Config.path.bSnapshotPath) { pathstr = CPathlib::CreatePath(CApp::GetModulePath(), Config.path.szSnapshotPath); ::CreateDirectory(pathstr.c_str(), NULL); } else { pathstr = rom->GetRomPath(); } tempstr = CPathlib::MakePath(pathstr.c_str(), name); DEBUGOUT("Snapshot: %s\n", tempstr.c_str()); if (!Config.emulator.bPNGsnapshot) { if (!(fp = ::fopen(tempstr.c_str(), "wb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, tempstr.c_str()); throw szErrorString; } LPBYTE lpScn = ppu->GetScreenPtr(); BITMAPFILEHEADER bfh; BITMAPINFOHEADER bih; RGBQUAD rgb[256]; ZEROMEMORY(&bfh, sizeof(bfh)); ZEROMEMORY(&bih, sizeof(bih)); ZEROMEMORY(rgb, sizeof(rgb)); bfh.bfType = 0x4D42; // 'BM' bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256; bfh.bfSize = bfh.bfOffBits + 256 * 240; bih.biSize = sizeof(bih); bih.biWidth = 256; bih.biHeight = 240; bih.biPlanes = 1; bih.biBitCount = 8; bih.biCompression = BI_RGB; bih.biSizeImage = 0; bih.biXPelsPerMeter = 0; bih.biYPelsPerMeter = 0; bih.biClrUsed = 256; bih.biClrImportant = 0; DirectDraw.GetPaletteData(rgb); if (::fwrite(&bfh, sizeof(bfh), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (::fwrite(&bih, sizeof(bih), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (::fwrite(&rgb, sizeof(rgb), 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } lpScn += 8; for (INT i = 239; i >= 0; i--) { if (::fwrite(&lpScn[(256 + 16) * i], 256, 1, fp) != 1) { // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } FCLOSE(fp); } else { LPBYTE lpScn = ppu->GetScreenPtr(); RGBQUAD rgb[256]; ZEROMEMORY(rgb, sizeof(rgb)); DirectDraw.GetPaletteData(rgb); PNGWRITE png; png.Write(tempstr.c_str(), 256, 240, rgb, lpScn + 8, CDirectDraw::RENDER_WIDTH); } } catch (CHAR* str) { DEBUGOUT("Snapshot error.\n"); FCLOSE(fp); throw str; #ifndef _DEBUG } catch (...) { DEBUGOUT("Snapshot error.\n"); FCLOSE(fp); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } return TRUE; } INT NES::IsMovieFile(const char* fname, ROM* rom) { FILE* fp = NULL; MOVIEFILEHDR header; if (!(fp = ::fopen(fname, "rb"))) return -1; if (::fread(&header, sizeof(header), 1, fp) != 1) { FCLOSE(fp); return -1; } FCLOSE(fp); if (::memcmp(header.ID, "VirtuaNES MV", sizeof(header.ID)) == 0) { if (header.BlockVersion < 0x0300) { return IDS_ERROR_ILLEGALMOVIEOLD; } else if (header.BlockVersion >= 0x0300) { if (rom->GetMapperNo() != 20) { // FDS以外 if (header.Ext0 != rom->GetPROM_CRC()) { return IDS_ERROR_ILLEGALMOVIECRC; // 違うじゃん } } else { // FDS if (header.Ext0 != rom->GetGameID() || header.Ext1 != (WORD)rom->GetMakerID() || header.Ext2 != (WORD)rom->GetDiskNo()) return IDS_ERROR_ILLEGALMOVIECRC; // 違うじゃん } if (header.RecordVersion != VIRTUANES_VERSION) { return IDS_ERROR_ILLEGALMOVIEVER; } return 0; } } return -1; } BOOL NES::MoviePlay(const char* fname) { if (rom->IsNSF()) return FALSE; if (IsMoviePlay() || IsMovieRec()) { MovieStop(); } DEBUGOUT("NES::MoviePlay\n"); try { if (!(m_fpMovie = ::fopen(fname, "rb+"))) { DEBUGOUT("Movie play error. File not found.\n"); // ファイル無いです return FALSE; } // 読み込み if (::fread(&m_hedMovie, sizeof(m_hedMovie), 1, m_fpMovie) != 1) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } if (::memcmp(m_hedMovie.ID, "VirtuaNES MV", sizeof(m_hedMovie.ID)) == 0) { m_MovieVersion = m_hedMovie.BlockVersion; // if( m_hedMovie.BlockVersion == 0x0300 ) { if (m_hedMovie.BlockVersion >= 0x0300) { if (m_hedMovie.CRC != 0) { if (CRC::Crc(sizeof(m_hedMovie) - sizeof(DWORD), (LPBYTE)&m_hedMovie) != m_hedMovie.CRC) { FCLOSE(m_fpMovie); return FALSE; // 違うじゃん } } // おっけ〜 } else { // 先生!古いのでダメっす。 FCLOSE(m_fpMovie); return FALSE; } } // ゲーム固有オプション m_saveRenderMethod = (INT)GetRenderMethod(); m_saveIrqType = GetIrqType(); m_saveFrameIRQ = GetFrameIRQmode(); m_saveVideoMode = GetVideoMode(); SetRenderMethod((RENDERMETHOD)m_hedMovie.RenderMethod); SetIrqType((INT)m_hedMovie.IRQtype); SetFrameIRQmode((m_hedMovie.FrameIRQ != 0) ? TRUE : FALSE); SetVideoMode((m_hedMovie.VideoMode != 0) ? TRUE : FALSE); LONG MovieOffset; m_MovieControl = m_hedMovie.Control; m_MovieStepTotal = m_hedMovie.MovieStep; MovieOffset = m_hedMovie.MovieOffset; if (m_hedMovie.BlockVersion < 0x0400) { // ステート読み込み ReadState(m_fpMovie); } else if (!(m_MovieControl & 0x40)) { // ステート読み込み ReadState(m_fpMovie); } else { Reset(); } if (::fseek(m_fpMovie, MovieOffset, SEEK_SET)) { // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } // ムービーが記録されていない? if (m_MovieStepTotal == 0) { MovieStop(); return FALSE; } m_bMoviePlay = TRUE; m_MovieStep = 0; } catch (CHAR* str) { DEBUGOUT("Movie play error. %s\n", str); FCLOSE(m_fpMovie); throw str; #ifndef _DEBUG } catch (...) { DEBUGOUT("Movie play error.\n"); FCLOSE(m_fpMovie); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } return TRUE; } BOOL NES::MovieRec(const char* fname) { if (rom->IsNSF()) return FALSE; if (IsMoviePlay() || IsMovieRec()) { MovieStop(); } DEBUGOUT("NES::MovieRec\n"); try { if (!(m_fpMovie = ::fopen(fname, "wb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, fname); throw szErrorString; } ::ZeroMemory(&m_hedMovie, sizeof(m_hedMovie)); ::memcpy(m_hedMovie.ID, "VirtuaNES MV", sizeof(m_hedMovie.ID)); // m_hedMovie.BlockVersion = 0x0300; m_hedMovie.BlockVersion = 0x0400; m_hedMovie.RecordVersion = VIRTUANES_VERSION; m_hedMovie.StateStOffset = sizeof(m_hedMovie); m_hedMovie.Control |= Config.movie.bUsePlayer[0] ? 0x01 : 0x00; m_hedMovie.Control |= Config.movie.bUsePlayer[1] ? 0x02 : 0x00; m_hedMovie.Control |= Config.movie.bUsePlayer[2] ? 0x04 : 0x00; m_hedMovie.Control |= Config.movie.bUsePlayer[3] ? 0x08 : 0x00; m_hedMovie.Control |= Config.movie.bResetRec ? 0x40 : 0x00; m_hedMovie.Control |= Config.movie.bRerecord ? 0x80 : 0x00; m_MovieControl = m_hedMovie.Control; // ゲーム固有オプション m_hedMovie.RenderMethod = (BYTE)GetRenderMethod(); m_hedMovie.IRQtype = (BYTE)GetIrqType(); m_hedMovie.FrameIRQ = GetFrameIRQmode() ? 0xFF : 0; m_hedMovie.VideoMode = GetVideoMode() ? 0xFF : 0; // CRC,ID値を書き込む(誤動作防止用) if (rom->GetMapperNo() != 20) { // FDS以外 m_hedMovie.Ext0 = rom->GetPROM_CRC(); } else { // FDS m_hedMovie.Ext0 = rom->GetGameID(); m_hedMovie.Ext1 = (WORD)rom->GetMakerID(); m_hedMovie.Ext2 = (WORD)rom->GetDiskNo(); } // ダミー書き込み if (::fwrite(&m_hedMovie, sizeof(m_hedMovie), 1, m_fpMovie) != 1) { FCLOSE(m_fpMovie); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (Config.movie.bResetRec) { Reset(); // ハードウェアリセットからの記録開始 } else { // ステート書き込み WriteState(m_fpMovie); } m_hedMovie.MovieOffset = ::ftell(m_fpMovie); m_bMovieRec = TRUE; m_MovieStep = m_MovieStepTotal = 0; // m_MovieVersion = 0x0300; m_MovieVersion = 0x0400; } catch (CHAR* str) { DEBUGOUT("Movie record error. %s\n", str); FCLOSE(m_fpMovie); throw str; #ifndef _DEBUG } catch (...) { DEBUGOUT("Movie record error.\n"); FCLOSE(m_fpMovie); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } return TRUE; } BOOL NES::MovieRecAppend(const char* fname) { if (rom->IsNSF()) return FALSE; // 記録中は意味が無いぞ if (IsMovieRec()) return FALSE; if (IsMoviePlay()) { MovieStop(); } DEBUGOUT("NES::MovieAppendRec\n"); try { if (!(m_fpMovie = ::fopen(fname, "rb"))) { // ファイルが無いとき if (!(m_fpMovie = ::fopen(fname, "wb"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, fname); throw szErrorString; } ::ZeroMemory(&m_hedMovie, sizeof(m_hedMovie)); ::memcpy(m_hedMovie.ID, "VirtuaNES MV", sizeof(m_hedMovie.ID)); // m_hedMovie.BlockVersion = 0x0300; m_hedMovie.BlockVersion = 0x0400; m_hedMovie.RecordVersion = VIRTUANES_VERSION; m_hedMovie.StateStOffset = sizeof(m_hedMovie); m_hedMovie.Control |= Config.movie.bUsePlayer[0] ? 0x01 : 0x00; m_hedMovie.Control |= Config.movie.bUsePlayer[1] ? 0x02 : 0x00; m_hedMovie.Control |= Config.movie.bUsePlayer[2] ? 0x04 : 0x00; m_hedMovie.Control |= Config.movie.bUsePlayer[3] ? 0x08 : 0x00; m_hedMovie.Control |= Config.movie.bRerecord ? 0x80 : 0x00; m_hedMovie.Control |= Config.movie.bResetRec ? 0x40 : 0x00; m_MovieControl = m_hedMovie.Control; // ゲーム固有オプション m_hedMovie.RenderMethod = (BYTE)GetRenderMethod(); m_hedMovie.IRQtype = (BYTE)GetIrqType(); m_hedMovie.FrameIRQ = GetFrameIRQmode() ? 0xFF : 0; m_hedMovie.VideoMode = GetVideoMode() ? 0xFF : 0; // ダミー書き込み if (::fwrite(&m_hedMovie, sizeof(m_hedMovie), 1, m_fpMovie) != 1) { FCLOSE(m_fpMovie); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } if (Config.movie.bResetRec) { Reset(); // ハードウェアリセットからの記録開始 } else { // ステート書き込み WriteState(m_fpMovie); } m_hedMovie.MovieOffset = ::ftell(m_fpMovie); m_MovieStep = m_MovieStepTotal = 0; // m_MovieVersion = 0x0300; m_MovieVersion = 0x0400; } else { if (!(m_fpMovie = ::fopen(fname, "rb+"))) { // xxx ファイルを開けません LPCSTR szErrStr = CApp::GetErrorString(IDS_ERROR_OPEN); sprintf(szErrorString, szErrStr, fname); throw szErrorString; } // 読み込み if (::fseek(m_fpMovie, 0, SEEK_SET)) { FCLOSE(m_fpMovie); // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } if (::fread(&m_hedMovie, sizeof(m_hedMovie), 1, m_fpMovie) != 1) { FCLOSE(m_fpMovie); // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } if (::memcmp(m_hedMovie.ID, "VirtuaNES MV", sizeof(m_hedMovie.ID)) != 0) { FCLOSE(m_fpMovie); return FALSE; } // 古いバージョンは捨て if (m_hedMovie.BlockVersion < 0x0300) { FCLOSE(m_fpMovie); return FALSE; } m_MovieControl = m_hedMovie.Control; m_MovieStep = m_MovieStepTotal = m_hedMovie.MovieStep; // m_MovieVersion = 0x0300; m_MovieVersion = 0x0400; if (::fseek(m_fpMovie, m_hedMovie.StateEdOffset, SEEK_SET)) { FCLOSE(m_fpMovie); // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } if (!ReadState(m_fpMovie)) { FCLOSE(m_fpMovie); // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } if (::fseek(m_fpMovie, m_hedMovie.StateEdOffset, SEEK_SET)) { FCLOSE(m_fpMovie); // ファイルの読み込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_READ); } } m_bMovieRec = TRUE; } catch (CHAR* str) { DEBUGOUT("Movie record error. %s\n", str); FCLOSE(m_fpMovie); throw str; #ifndef _DEBUG } catch (...) { DEBUGOUT("Movie record error.\n"); FCLOSE(m_fpMovie); // 不明なエラーが発生しました throw CApp::GetErrorString(IDS_ERROR_UNKNOWN); #endif } return TRUE; } BOOL NES::MovieStop() { if (!m_fpMovie && !(m_bMoviePlay || m_bMovieRec)) return FALSE; DEBUGOUT("NES::MovieStop\n"); DirectDraw.SetMessageString("Movie stop."); if (m_bMovieRec) { m_hedMovie.MovieStep = m_MovieStep; m_hedMovie.StateEdOffset = ::ftell(m_fpMovie); WriteState(m_fpMovie); // // 撮り直し禁止の場合は追記不可能 // if( m_MovieControl & 0x80 ) { // } else { // m_hedMovie.StateEdOffset = 0; // } if (::fseek(m_fpMovie, 0, SEEK_SET)) { FCLOSE(m_fpMovie); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } // CRC 書き込み m_hedMovie.CRC = CRC::Crc(sizeof(m_hedMovie) - sizeof(DWORD), (LPBYTE)&m_hedMovie); // 最終的なヘッダ書き込み if (::fwrite(&m_hedMovie, sizeof(m_hedMovie), 1, m_fpMovie) != 1) { FCLOSE(m_fpMovie); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } FCLOSE(m_fpMovie); m_bMovieRec = FALSE; } if (m_bMoviePlay) { FCLOSE(m_fpMovie); m_bMoviePlay = FALSE; // ゲーム固有オプションを元に戻す SetRenderMethod((RENDERMETHOD)m_saveRenderMethod); SetIrqType(m_saveIrqType); SetFrameIRQmode(m_saveFrameIRQ); SetVideoMode(m_saveVideoMode); } return TRUE; } void NES::GetMovieInfo(WORD& wRecVersion, WORD& wVersion, DWORD& dwRecordFrames, DWORD& dwRecordTimes) { wRecVersion = m_hedMovie.RecordVersion; wVersion = m_hedMovie.BlockVersion; dwRecordFrames = m_hedMovie.MovieStep; dwRecordTimes = m_hedMovie.RecordTimes; } // 毎フレーム呼び出す void NES::Movie() { if (!m_fpMovie && !(m_bMoviePlay || m_bMovieRec)) { m_CommandRequest = 0; // コレ入れないと死ぬ return; } INT exctr = pad->GetExController(); BYTE Data; WORD wData; DWORD dwData; if (m_bMovieRec) { // 最初から拡張コントローラが設定されていた場合 if (m_MovieStep == 0) { if (exctr == PAD::EXCONTROLLER_ZAPPER || exctr == PAD::EXCONTROLLER_PADDLE || exctr == PAD::EXCONTROLLER_CRAZYCLIMBER || exctr == PAD::EXCONTROLLER_TOPRIDER || exctr == PAD::EXCONTROLLER_SPACESHADOWGUN || exctr == PAD::EXCONTROLLER_FAMILYTRAINER_A || exctr == PAD::EXCONTROLLER_FAMILYTRAINER_B || exctr == PAD::EXCONTROLLER_MAHJANG || exctr == PAD::EXCONTROLLER_EXCITINGBOXING || exctr == PAD::EXCONTROLLER_OEKAKIDS_TABLET) { // コマンドID Data = 0xF0; // 書き込み if (::fwrite(&Data, sizeof(Data), 1, m_fpMovie) != 1) { MovieStop(); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } // 種類 wData = (WORD)(0x0100 | (pad->GetExController() & 0x0FF)); // 書き込み if (::fwrite(&wData, sizeof(wData), 1, m_fpMovie) != 1) { MovieStop(); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } } if (m_CommandRequest) { // コマンドID Data = 0xF0; // 書き込み if (::fwrite(&Data, sizeof(Data), 1, m_fpMovie) != 1) { MovieStop(); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } // コマンド wData = (WORD)m_CommandRequest; // 書き込み if (::fwrite(&wData, sizeof(wData), 1, m_fpMovie) != 1) { MovieStop(); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } m_CommandRequest = 0; // 拡張コントローラ if (exctr == PAD::EXCONTROLLER_ZAPPER || exctr == PAD::EXCONTROLLER_PADDLE || exctr == PAD::EXCONTROLLER_CRAZYCLIMBER || exctr == PAD::EXCONTROLLER_TOPRIDER || exctr == PAD::EXCONTROLLER_SPACESHADOWGUN || exctr == PAD::EXCONTROLLER_FAMILYTRAINER_A || exctr == PAD::EXCONTROLLER_FAMILYTRAINER_B || exctr == PAD::EXCONTROLLER_MAHJANG || exctr == PAD::EXCONTROLLER_EXCITINGBOXING || exctr == PAD::EXCONTROLLER_OEKAKIDS_TABLET) { // 拡張コントローラデータID Data = 0xF3; // 書き込み if (::fwrite(&Data, sizeof(Data), 1, m_fpMovie) != 1) { MovieStop(); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } // データ dwData = pad->GetSyncExData(); // 書き込み if (::fwrite(&dwData, sizeof(dwData), 1, m_fpMovie) != 1) { MovieStop(); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } dwData = pad->GetSyncData(); for (INT i = 0; i < 4; i++) { Data = (BYTE)(dwData >> (i * 8)); if (m_MovieControl & (1 << i)) { // 書き込み if (::fwrite(&Data, sizeof(Data), 1, m_fpMovie) != 1) { MovieStop(); // ファイルの書き込みに失敗しました throw CApp::GetErrorString(IDS_ERROR_WRITE); } } } m_MovieStep++; } if (m_bMoviePlay) { DWORD dwPadData = 0; INT num = 0; BYTE PadBuf[4]; PadBuf[0] = PadBuf[1] = PadBuf[2] = PadBuf[3] = 0; // ムービー再生終了? if (m_MovieStep >= m_MovieStepTotal) { if (!Config.movie.bLoopPlay) { MovieStop(); return; } else { // 一旦再生中じゃないって事にする m_bMoviePlay = FALSE; m_MovieStep = 0; ::fseek(m_fpMovie, m_hedMovie.StateStOffset, SEEK_SET); // ステート読み込み ReadState(m_fpMovie); ::fseek(m_fpMovie, m_hedMovie.MovieOffset, SEEK_SET); // 再生中って事にする m_bMoviePlay = TRUE; } } do { // 読み込み if (::fread(&Data, sizeof(Data), 1, m_fpMovie) != 1) { // 終了? MovieStop(); return; } // コマンド? if ((Data & 0xF0) == 0xF0) { if (Data == 0xF0) { // 読み込み if (::fread(&wData, sizeof(wData), 1, m_fpMovie) != 1) { // 終了? MovieStop(); return; } if (wData < 0x0100) { Command((NESCOMMAND)((INT)wData)); } else { // 拡張コントローラ CommandParam(NESCMD_EXCONTROLLER, ((INT)wData) & 0x00FF); } } else if (Data == 0xF3) { // 読み込み if (::fread(&dwData, sizeof(dwData), 1, m_fpMovie) != 1) { // 終了? MovieStop(); return; } pad->SetSyncExData(dwData); } else { // データぶっ壊れてる?終了じゃ MovieStop(); return; } } else { // 未使用プレイヤー分すっ飛ばし while (!(m_MovieControl & (1 << num)) && (num < 4)) { PadBuf[num] = 0; num++; } PadBuf[num] = Data; num++; // 未使用プレイヤー分すっ飛ばし while (!(m_MovieControl & (1 << num)) && (num < 4)) { PadBuf[num] = 0; num++; } } } while (num < 4); dwData = (((DWORD)PadBuf[3]) << 24) | (((DWORD)PadBuf[2]) << 16) | (((DWORD)PadBuf[1]) << 8) | ((DWORD)PadBuf[0]); pad->SetSyncData(dwData); // カウンタ増やす m_MovieStep++; } m_CommandRequest = 0; } // For Cheat void NES::CheatInitial() { m_CheatCode.clear(); } BOOL NES::IsCheatCodeAdd() { BOOL bRet = m_bCheatCodeAdd; m_bCheatCodeAdd = FALSE; return bRet; } INT NES::GetCheatCodeNum() { return m_CheatCode.size(); } BOOL NES::GetCheatCode(INT no, CHEATCODE& code) { if (m_CheatCode.size() - 1 < no) return FALSE; code = m_CheatCode[no]; return TRUE; } void NES::SetCheatCodeFlag(INT no, BOOL bEnable) { if (m_CheatCode.size() - 1 < no) return; if (bEnable) { m_CheatCode[no].enable |= CHEAT_ENABLE; } else { m_CheatCode[no].enable &= ~CHEAT_ENABLE; } } void NES::SetCheatCodeAllFlag(BOOL bEnable, BOOL bKey) { for (INT i = 0; i < m_CheatCode.size(); i++) { if (!bKey) { if (bEnable) { m_CheatCode[i].enable |= CHEAT_ENABLE; } else { m_CheatCode[i].enable &= ~CHEAT_ENABLE; } } else if (!(m_CheatCode[i].enable & CHEAT_KEYDISABLE)) { if (bEnable) { m_CheatCode[i].enable |= CHEAT_ENABLE; } else { m_CheatCode[i].enable &= ~CHEAT_ENABLE; } } } } void NES::ReplaceCheatCode(INT no, CHEATCODE code) { if (m_CheatCode.size() - 1 < no) return; m_CheatCode[no] = code; } void NES::AddCheatCode(CHEATCODE code) { m_CheatCode.push_back(code); m_bCheatCodeAdd = TRUE; } void NES::DelCheatCode(INT no) { if (m_CheatCode.size() - 1 < no) return; m_CheatCode.erase(m_CheatCode.begin() + no); } DWORD NES::CheatRead(INT length, WORD addr) { DWORD data = 0; for (INT i = 0; i <= length; i++) { data |= (DWORD)Read(addr + i) * (1 << (i * 8)); } return data; } void NES::CheatWrite(INT length, WORD addr, DWORD data) { for (INT i = 0; i <= length; i++) { Write((WORD)(addr + i), data & 0xFF); data >>= 8; } } void NES::CheatCodeProcess() { for (vector::iterator it = m_CheatCode.begin(); it != m_CheatCode.end(); it++) { if (!(it->enable & CHEAT_ENABLE)) continue; switch (it->type) { case CHEAT_TYPE_ALWAYS: CheatWrite(it->length, it->address, it->data); break; case CHEAT_TYPE_ONCE: CheatWrite(it->length, it->address, it->data); it->enable = 0; break; case CHEAT_TYPE_GREATER: if (CheatRead(it->length, it->address) > it->data) { CheatWrite(it->length, it->address, it->data); } break; case CHEAT_TYPE_LESS: if (CheatRead(it->length, it->address) < it->data) { CheatWrite(it->length, it->address, it->data); } break; } } } void NES::GenieInitial() { m_bCheatCodeAdd = FALSE; m_GenieCode.clear(); } void NES::GenieLoad(char* fname) { FILE* fp = NULL; CHAR buf[256]; GENIECODE code; BYTE codetmp[9]; INT no; if ((fp = ::fopen(fname, "r"))) { m_GenieCode.clear(); while (::fgets(buf, sizeof(buf), fp)) { if (buf[0] == ';') continue; if (buf[0] == 0x0D || buf[0] == 0x0A) continue; if (::strlen(buf) < 6) continue; code.address = 0; code.data = 0; code.cmp = 0; for (no = 0; isalpha(buf[no]) && no < 8; no++) { switch (buf[no]) { case 'A': codetmp[no] = 0x00; break; case 'P': codetmp[no] = 0x01; break; case 'Z': codetmp[no] = 0x02; break; case 'L': codetmp[no] = 0x03; break; case 'G': codetmp[no] = 0x04; break; case 'I': codetmp[no] = 0x05; break; case 'T': codetmp[no] = 0x06; break; case 'Y': codetmp[no] = 0x07; break; case 'E': codetmp[no] = 0x08; break; case 'O': codetmp[no] = 0x09; break; case 'X': codetmp[no] = 0x0A; break; case 'U': codetmp[no] = 0x0B; break; case 'K': codetmp[no] = 0x0C; break; case 'S': codetmp[no] = 0x0D; break; case 'V': codetmp[no] = 0x0E; break; case 'N': codetmp[no] = 0x0F; break; } } if (no == 6) { // Address code.address |= (WORD)(codetmp[3] & 0x07) << 12; code.address |= (WORD)(codetmp[4] & 0x08) << 8; code.address |= (WORD)(codetmp[5] & 0x07) << 8; code.address |= (WORD)(codetmp[1] & 0x08) << 4; code.address |= (WORD)(codetmp[2] & 0x07) << 4; code.address |= (WORD)(codetmp[3] & 0x08); code.address |= (WORD)(codetmp[4] & 0x07); // Data code.data |= (codetmp[0] & 0x08) << 4; code.data |= (codetmp[1] & 0x07) << 4; code.data |= (codetmp[5] & 0x08); code.data |= (codetmp[0] & 0x07); m_GenieCode.push_back(code); } else if (no == 8) { // Address code.address |= 0x8000; code.address |= (WORD)(codetmp[3] & 0x07) << 12; code.address |= (WORD)(codetmp[4] & 0x08) << 8; code.address |= (WORD)(codetmp[5] & 0x07) << 8; code.address |= (WORD)(codetmp[1] & 0x08) << 4; code.address |= (WORD)(codetmp[2] & 0x07) << 4; code.address |= (WORD)(codetmp[3] & 0x08); code.address |= (WORD)(codetmp[4] & 0x07); // Data code.data |= (codetmp[0] & 0x08) << 4; code.data |= (codetmp[1] & 0x07) << 4; code.data |= (codetmp[7] & 0x08); code.data |= (codetmp[0] & 0x07); // Data code.cmp |= (codetmp[6] & 0x08) << 4; code.cmp |= (codetmp[7] & 0x07) << 4; code.cmp |= (codetmp[5] & 0x08); code.cmp |= (codetmp[6] & 0x07); m_GenieCode.push_back(code); } } GenieCodeProcess(); } FCLOSE(fp); } void NES::GenieCodeProcess() { WORD addr; for (INT i = 0; i < m_GenieCode.size(); i++) { addr = m_GenieCode[i].address; if (addr & 0x8000) { // 8character codes if (CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] == m_GenieCode[i].cmp) { CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = m_GenieCode[i].data; } } else { // 6character codes addr |= 0x8000; CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = m_GenieCode[i].data; } } } void NES::DrawPad() { if (m_bMoviePlay) { INT offset_h = 12; INT offset_v = Config.graphics.bAllLine ? (240 - 18) : (240 - 22); if (Config.movie.bPadDisplay) { DWORD dwData = pad->GetSyncData(); for (INT i = 0; i < 4; i++) { BYTE Data = (BYTE)(dwData >> (i * 8)); if (m_MovieControl & (1 << i)) { DrawBitmap(offset_h, offset_v, m_PadImg); // KEY if (Data & (1 << 4)) DrawBitmap(offset_h + 3, offset_v + 1, m_KeyImg0); // U if (Data & (1 << 5)) DrawBitmap(offset_h + 3, offset_v + 5, m_KeyImg0); // D if (Data & (1 << 6)) DrawBitmap(offset_h + 1, offset_v + 3, m_KeyImg0); // L if (Data & (1 << 7)) DrawBitmap(offset_h + 5, offset_v + 3, m_KeyImg0); // R // START,SELECT if (Data & (1 << 2)) DrawBitmap(offset_h + 9, offset_v + 5, m_KeyImg1); // SELECT if (Data & (1 << 3)) DrawBitmap(offset_h + 13, offset_v + 5, m_KeyImg1); // START // A,B if (Data & (1 << 0)) DrawBitmap(offset_h + 23, offset_v + 3, m_KeyImg2); // A if (Data & (1 << 1)) DrawBitmap(offset_h + 18, offset_v + 3, m_KeyImg2); // B offset_h += 30; } } } if (Config.movie.bTimeDisplay) { // Time display INT t = m_MovieStep; INT h = t / 216000; t -= h * 216000; INT m = t / 3600; t -= m * 3600; INT s = t / 60; t -= s * 60; CHAR szTemp[64]; sprintf(szTemp, "%02d:%02d:%02d.%02d", h, m, s, t * 100 / 60); DrawString(256 - 80 + 0, offset_v - 1, szTemp, 0x1F); DrawString(256 - 80 + 0, offset_v + 1, szTemp, 0x1F); DrawString(256 - 80 - 1, offset_v + 0, szTemp, 0x1F); DrawString(256 - 80 + 1, offset_v + 0, szTemp, 0x1F); DrawString(256 - 80, offset_v, szTemp, 0x30); } } } void NES::DrawBitmap(INT x, INT y, LPBYTE lpBitmap) { INT i, j; INT h, v; LPBYTE pScn = ppu->GetScreenPtr() + 8 + (256 + 16) * y + x; LPBYTE pPtr; h = (INT)*lpBitmap++; v = (INT)*lpBitmap++; for (j = 0; j < v; j++) { pPtr = pScn; for (i = 0; i < h; i++) { if (*lpBitmap != 0xFF) { *pPtr = *lpBitmap; } lpBitmap++; pPtr++; } pScn += 256 + 16; } } // TapeDevice BOOL NES::TapePlay(const char* fname) { if (rom->IsNSF()) return FALSE; if (IsTapePlay() || IsTapeRec()) { TapeStop(); } DEBUGOUT("NES::TapePlay\n"); if (!(m_fpTape = ::fopen(fname, "rb"))) { DEBUGOUT("Tape play error. File not found.\n"); // ファイル無いです return FALSE; } m_bTapePlay = TRUE; m_TapeCycles = 0; m_TapeOut = 0; cpu->SetClockProcess(TRUE); return TRUE; } BOOL NES::TapeRec(const char* fname) { if (rom->IsNSF()) return FALSE; if (IsTapePlay() || IsTapeRec()) { TapeStop(); } DEBUGOUT("NES::TapeRec\n"); if (!(m_fpTape = ::fopen(fname, "wb"))) { DEBUGOUT("Tape rec error. File not found.\n"); // ファイル無いです return FALSE; } m_bTapeRec = TRUE; m_TapeCycles = 0; m_TapeIn = 0; cpu->SetClockProcess(TRUE); return TRUE; } void NES::TapeStop() { DirectDraw.SetMessageString("Tape stop."); if (!m_bBarcode) { cpu->SetClockProcess(FALSE); } m_bTapePlay = m_bTapeRec = FALSE; FCLOSE(m_fpTape); } void NES::Tape(INT cycles) { if (!(IsTapePlay() || IsTapeRec())) { return; } if ((m_TapeCycles -= (double)cycles) > 0) return; m_TapeCycles += (nescfg->CpuClock / 32000.0); // m_TapeCycles += (nescfg->CpuClock / 22050.0); // 遅すぎてダメっぽい if (m_bTapePlay) { INT data = ::fgetc(m_fpTape); if (data != EOF) { if ((data & 0xFF) >= 0x8C) { m_TapeOut = 0x02; } else if ((data & 0xFF) <= 0x74) { m_TapeOut = 0x00; } } else { TapeStop(); } } if (m_bTapeRec) { ::fputc((int)((m_TapeIn & 7) == 7) ? 0x90 : 0x70, m_fpTape); } } void NES::Barcode(INT cycles) { if (m_bBarcode) { if ((m_BarcodeCycles += cycles) > 1000) { m_BarcodeCycles = 0; // 停止? if (m_BarcodeData[m_BarcodePtr] != 0xFF) { m_BarcodeOut = m_BarcodeData[m_BarcodePtr++]; } else { m_bBarcode = FALSE; m_BarcodeOut = 0; DEBUGOUT("Barcode data trasnfer complete!!\n"); if (!(IsTapePlay() || IsTapeRec())) { cpu->SetClockProcess(FALSE); } } } } } void NES::SetBarcodeData(LPBYTE code, INT len) { if (rom->GetPROM_CRC() == 0x67898319) { // Barcode World (J) SetBarcode2Data(code, len); return; } DEBUGOUT("NES::SetBarcodeData code=%s len=%d\n", code, len); bool prefix_parity_type[10][6] = { {0,0,0,0,0,0}, {0,0,1,0,1,1}, {0,0,1,1,0,1}, {0,0,1,1,1,0}, {0,1,0,0,1,1}, {0,1,1,0,0,1}, {0,1,1,1,0,0}, {0,1,0,1,0,1}, {0,1,0,1,1,0}, {0,1,1,0,1,0} }; bool data_left_odd[10][7] = { {0,0,0,1,1,0,1}, {0,0,1,1,0,0,1}, {0,0,1,0,0,1,1}, {0,1,1,1,1,0,1}, {0,1,0,0,0,1,1}, {0,1,1,0,0,0,1}, {0,1,0,1,1,1,1}, {0,1,1,1,0,1,1}, {0,1,1,0,1,1,1}, {0,0,0,1,0,1,1} }; bool data_left_even[10][7] = { {0,1,0,0,1,1,1}, {0,1,1,0,0,1,1}, {0,0,1,1,0,1,1}, {0,1,0,0,0,0,1}, {0,0,1,1,1,0,1}, {0,1,1,1,0,0,1}, {0,0,0,0,1,0,1}, {0,0,1,0,0,0,1}, {0,0,0,1,0,0,1}, {0,0,1,0,1,1,1} }; bool data_right[10][7] = { {1,1,1,0,0,1,0}, {1,1,0,0,1,1,0}, {1,1,0,1,1,0,0}, {1,0,0,0,0,1,0}, {1,0,1,1,1,0,0}, {1,0,0,1,1,1,0}, {1,0,1,0,0,0,0}, {1,0,0,0,1,0,0}, {1,0,0,1,0,0,0}, {1,1,1,0,1,0,0} }; INT i, j, count = 0;; // 数値に変換 for (i = 0; i < len; i++) { code[i] = code[i] - '0'; } // レフトマージン for (i = 0; i < 32; i++) { m_BarcodeData[count++] = 0x08; } // レフトガードバー m_BarcodeData[count++] = 0x00; m_BarcodeData[count++] = 0x08; m_BarcodeData[count++] = 0x00; if (len == 13) { #if 0 // チェックディジットの再計算 INT sum = 0; for (i = 0; i < 12; i++) { sum += (i & 1) ? (code[i] * 3) : code[i]; } code[12] = (10 - (sum % 10)) % 10; // test start // INT cs = (10-(sum%10))%10; // if( cs == 0 ) { // cs = 9; // } else { // cs--; // } // code[12] = cs; // test end #endif // 左側6キャラクタ for (i = 0; i < 6; i++) { if (prefix_parity_type[code[0]][i]) { // 偶数パリティ for (j = 0; j < 7; j++) { m_BarcodeData[count++] = data_left_even[code[i + 1]][j] ? 0x00 : 0x08; } } else { // 奇数パリティ for (j = 0; j < 7; j++) { m_BarcodeData[count++] = data_left_odd[code[i + 1]][j] ? 0x00 : 0x08; } } } // センターバー m_BarcodeData[count++] = 0x08; m_BarcodeData[count++] = 0x00; m_BarcodeData[count++] = 0x08; m_BarcodeData[count++] = 0x00; m_BarcodeData[count++] = 0x08; // 右側5キャラクタとチェックディジット for (i = 7; i < 13; i++) { // 偶数パリティ for (j = 0; j < 7; j++) { m_BarcodeData[count++] = data_right[code[i]][j] ? 0x00 : 0x08; } } } else if (len == 8) { // チェックディジットの再計算 INT sum = 0; for (i = 0; i < 7; i++) { sum += (i & 1) ? code[i] : (code[i] * 3); } code[7] = (10 - (sum % 10)) % 10; // 左側4キャラクタ for (i = 0; i < 4; i++) { // 奇数パリティ for (j = 0; j < 7; j++) { m_BarcodeData[count++] = data_left_odd[code[i]][j] ? 0x00 : 0x08; } } // センターバー m_BarcodeData[count++] = 0x08; m_BarcodeData[count++] = 0x00; m_BarcodeData[count++] = 0x08; m_BarcodeData[count++] = 0x00; m_BarcodeData[count++] = 0x08; // 右側3キャラクタとチェックディジット for (i = 4; i < 8; i++) { // 偶数パリティ for (j = 0; j < 7; j++) { m_BarcodeData[count++] = data_right[code[i]][j] ? 0x00 : 0x08; } } } // ライトガードバー m_BarcodeData[count++] = 0x00; m_BarcodeData[count++] = 0x08; m_BarcodeData[count++] = 0x00; // ライトマージン for (i = 0; i < 32; i++) { m_BarcodeData[count++] = 0x08; } // 終了マーク m_BarcodeData[count++] = 0xFF; // 転送開始 m_bBarcode = TRUE; m_BarcodeCycles = 0; m_BarcodePtr = 0; m_BarcodeOut = 0x08; cpu->SetClockProcess(TRUE); DEBUGOUT("BARCODE DATA MAX:%d\n", count); } BYTE NES::Barcode2(void) { BYTE ret = 0x00; if (!m_bBarcode2 || m_Barcode2seq < 0) return ret; switch (m_Barcode2seq) { case 0: m_Barcode2seq++; m_Barcode2ptr = 0; ret = 0x04; // d3 break; case 1: m_Barcode2seq++; m_Barcode2bit = m_Barcode2data[m_Barcode2ptr]; m_Barcode2cnt = 0; ret = 0x04; // d3 break; case 2: ret = (m_Barcode2bit & 0x01) ? 0x00 : 0x04; // Bit rev. m_Barcode2bit >>= 1; if (++m_Barcode2cnt > 7) { m_Barcode2seq++; } break; case 3: if (++m_Barcode2ptr > 19) { m_bBarcode2 = FALSE; m_Barcode2seq = -1; } else { m_Barcode2seq = 1; } break; default: break; } return ret; } void NES::SetBarcode2Data(LPBYTE code, INT len) { DEBUGOUT("NES::SetBarcodeData2 code=%s len=%d\n", code, len); if (len < 13) return; m_bBarcode2 = TRUE; m_Barcode2seq = 0; m_Barcode2ptr = 0; ::strcpy((char*)m_Barcode2data, (char*)code); m_Barcode2data[13] = 'S'; m_Barcode2data[14] = 'U'; m_Barcode2data[15] = 'N'; m_Barcode2data[16] = 'S'; m_Barcode2data[17] = 'O'; m_Barcode2data[18] = 'F'; m_Barcode2data[19] = 'T'; } //#if NES_PROFILER #if 1 BYTE NES::Font6x8[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, 0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x50,0xF8,0x50,0xF8,0x50,0x50,0x00, 0x20,0x78,0xA0,0x70,0x28,0xF0,0x20,0x00,0x48,0xB0,0x50,0x20,0x50,0x68,0x90,0x00, 0x40,0xA0,0xA8,0x68,0x90,0x90,0x68,0x00,0x30,0x20,0x00,0x00,0x00,0x00,0x00,0x00, 0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00,0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00, 0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x20,0x40,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x08,0x10,0x10,0x20,0x40,0x40,0x80,0x00, 0x70,0x88,0x98,0xA8,0xC8,0x88,0x70,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0xF8,0x00, 0x70,0x88,0x08,0x30,0x40,0x80,0xF8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00, 0x30,0x50,0x90,0x90,0xF8,0x10,0x10,0x00,0xF8,0x80,0x80,0xF0,0x08,0x08,0xF0,0x00, 0x70,0x88,0x80,0xF0,0x88,0x88,0x70,0x00,0xF8,0x08,0x10,0x10,0x20,0x20,0x20,0x00, 0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x78,0x08,0x88,0x70,0x00, 0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x40,0x00, 0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0xF8,0x00,0xF8,0x00,0x00,0x00, 0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00, 0x30,0x48,0x88,0x98,0xA8,0xA8,0x78,0x00,0x20,0x50,0x50,0x88,0xF8,0x88,0x88,0x00, 0xF0,0x88,0x88,0xF0,0x88,0x88,0xF0,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00, 0xF0,0x88,0x88,0x88,0x88,0x88,0xF0,0x00,0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00, 0xF8,0x80,0x80,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0xB8,0x88,0x88,0x70,0x00, 0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0xF8,0x00, 0x38,0x08,0x08,0x08,0x08,0x88,0x70,0x00,0x88,0x88,0x90,0xE0,0x90,0x88,0x88,0x00, 0x80,0x80,0x80,0x80,0x80,0x80,0xF8,0x00,0x88,0xD8,0xA8,0xA8,0xA8,0xA8,0xA8,0x00, 0x88,0xC8,0xA8,0xA8,0xA8,0x98,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00, 0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x88,0x88,0xA8,0x90,0x68,0x00, 0xF0,0x88,0x88,0xF0,0x88,0x88,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00, 0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00, 0x88,0x88,0x88,0x50,0x50,0x50,0x20,0x00,0x88,0xA8,0xA8,0xA8,0xA8,0xD8,0x88,0x00, 0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x88,0x88,0x88,0x70,0x20,0x20,0x20,0x00, 0xF8,0x08,0x10,0x20,0x40,0x80,0xF8,0x00,0x70,0x40,0x40,0x40,0x40,0x40,0x70,0x00, 0x88,0x50,0xF8,0x20,0xF8,0x20,0x20,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x70,0x00, 0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00, 0x80,0xC0,0xE0,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x70,0x08,0x78,0x88,0xF8,0x00, 0x80,0x80,0x80,0xF0,0x88,0x88,0xF0,0x00,0x00,0x00,0x78,0x80,0x80,0x80,0x78,0x00, 0x08,0x08,0x08,0x78,0x88,0x88,0x78,0x00,0x00,0x00,0x70,0x88,0xF8,0x80,0x78,0x00, 0x18,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0xF0,0x00, 0x80,0x80,0x80,0xF0,0x88,0x88,0x88,0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x00, 0x20,0x00,0x20,0x20,0x20,0x20,0xC0,0x00,0x80,0x80,0x88,0x90,0xE0,0x90,0x88,0x00, 0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x00,0x00,0x00,0xF0,0xA8,0xA8,0xA8,0xA8,0x00, 0x00,0x00,0xF0,0x88,0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00, 0x00,0x00,0xF0,0x88,0xF0,0x80,0x80,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0x08,0x00, 0x00,0x00,0xB8,0xC0,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xF0,0x00, 0x20,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x88,0x88,0x88,0x88,0x70,0x00, 0x00,0x00,0x88,0x88,0x50,0x50,0x20,0x00,0x00,0x00,0x88,0xA8,0xA8,0xD8,0x88,0x00, 0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x78,0x08,0xF0,0x00, 0x00,0x00,0xF8,0x08,0x70,0x80,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; void NES::DrawFont(INT x, INT y, BYTE chr, BYTE col) { INT i; LPBYTE pFnt; LPBYTE pPtr; LPBYTE pScn = ppu->GetScreenPtr() + 8; if (chr < 0x20 || chr > 0x7F) return; chr -= 0x20; pFnt = &Font6x8[chr * 8]; pPtr = pScn + (256 + 16) * y + x; for (i = 0; i < 8; i++) { if (pFnt[i] & 0x80) pPtr[0] = col; if (pFnt[i] & 0x40) pPtr[1] = col; if (pFnt[i] & 0x20) pPtr[2] = col; if (pFnt[i] & 0x10) pPtr[3] = col; if (pFnt[i] & 0x08) pPtr[4] = col; if (pFnt[i] & 0x04) pPtr[5] = col; pPtr += (256 + 16); } } void NES::DrawString(INT x, INT y, LPSTR str, BYTE col) { while (*str) { DrawFont(x, y, *str, col); str++; x += 6; } } #endif