4143 lines
198 KiB
C++
4143 lines
198 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// NES Emulation core //
|
||
// Norix //
|
||
// written 2001/02/22 //
|
||
// last modify ----/--/-- //
|
||
//////////////////////////////////////////////////////////////////////////
|
||
#define WIN32_LEAN_AND_MEAN
|
||
#include <windows.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <math.h>
|
||
#include <time.h>
|
||
|
||
#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<CHEATCODE>::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
|