⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nestreme.cpp

📁 nes游戏模拟器
💻 CPP
📖 第 1 页 / 共 5 页
字号:
HRESULT InsertListViewText(HWND hwndLV, UINT uRow, UINT uCol, LPSTR strText)
{
	LV_ITEM lvI; // List view item structure.

	// Fill out the item structure.
	lvI.mask = LVIF_TEXT;
	lvI.iItem = uRow;
	lvI.iSubItem = uCol;
	lvI.pszText = strText;
	lvI.state = 0;
	lvI.stateMask = 0;
    
	// Add the item. First try adding it as an item. If that doesn't work,
	// then try adding the item as a subitem. If no good, return an error.
	if (ListView_InsertItem(hwndLV, &lvI) == -1)
		if (ListView_SetItem(hwndLV, &lvI) == FALSE)
			return ERROR;

	return S_OK;
} // end InsertListViewText()


//------------------------------------------------------------------------------
// Name: LoadNESFile()
// Desc: Called when the user selected Open from the File menu. Loads
//       the ROM into memory and sets the program up for debugging or
//       runs the game depending on the option set.
//------------------------------------------------------------------------------
HRESULT LoadNESFile(HWND hwnd)
{
	char strMapperLibName[512]; // FileName for the mapper .dll.

	// Popup the open file dialog box.
	PromptForFileOpen(hwnd);

	// Load the ROM into memory.
	LoadROMToMem((LPSTR)strFileName);

	// Save the number of program and character rom pages.
	wNumPRGROMPages = abyRomHeader[4];
	wNumCHRROMPages = abyRomHeader[5];

	// Some games only hold one (1) 16K bank of PRG-ROM, 
	// which should be loaded into both $C000 and $8000.
	// Otherwise initialize the banks to the first 2 pages.
	if (abyRomHeader[4] == 1)
	{
		CPU.pbyPRGROMBank1 = &abyPRGROM[0];
		CPU.pbyPRGROMBank2 = &abyPRGROM[0];
	}
	else
	{
		CPU.pbyPRGROMBank1 = &abyPRGROM[0];
		CPU.pbyPRGROMBank2 = &abyPRGROM[NES_PRGROM_PAGESIZE];
	}

	// Initialize the pattern tables to point to the first and
	// second banks of CHR-ROM.
	PPU.apbyPatternTables[0] = &abyCHRROM[0];
	PPU.apbyPatternTables[1] = &abyCHRROM[NES_CHRROM_PAGESIZE];

	// Initialize the pointers to the name tables depending on the
	// mirroring of the ROM. Bit 1 of the rom header offset 6
	// is 1 for vertical mirroring and 0 for horizontal mirroring.
	if (abyRomHeader[6] & 1)
	{
		// For vertical mirroring, name tables 0 and 2 point to
		// the first name table and name tables 1 and 3 point to
		// the second name table.
		PPU.apbyNameTables[0] = &PPU.abyNameTables[0];
		PPU.apbyNameTables[1] = &PPU.abyNameTables[0x400];
		PPU.apbyNameTables[2] = &PPU.abyNameTables[0];
		PPU.apbyNameTables[3] = &PPU.abyNameTables[0x400];
	}
	else
	{
		// For horizontal mirroring, name tables 0 and 1 point to
		// the first name table and name tables 2 and 3 point to
		// the second name table.
		PPU.apbyNameTables[0] = &PPU.abyNameTables[0];
		PPU.apbyNameTables[1] = &PPU.abyNameTables[0];
		PPU.apbyNameTables[2] = &PPU.abyNameTables[0x400];
		PPU.apbyNameTables[3] = &PPU.abyNameTables[0x400];
	}

	// Get the string for the file of the mapper .dll
	sprintf(strMapperLibName, ".\\mappers\\mapper%d.dll", (abyRomHeader[6]>>4)|(abyRomHeader[7]&0xF0));

	// Load the mapper .dll
	if ((hinstMapperLib = LoadLibrary(strMapperLibName)) == NULL)
		FATAL(hwnd, "Could not find the .dll for the mapper.");

	// Get all the function addresses and save them.
	if (hinstMapperLib != NULL)
	{
		MapperOnLoad = (MAPPERLOAD)GetProcAddress(hinstMapperLib, "?OnLoad@@YAHPAUtagNESData@@@Z");
		MapperOnRead = (MAPPERREAD)GetProcAddress(hinstMapperLib, "?OnRead@@YAEG@Z");
		MapperOnWrite = (MAPPERWRITE)GetProcAddress(hinstMapperLib, "?OnWrite@@YAHGE@Z");
	}

	// Make a call to the mapper to do anything that may have to 
	// happen during a load. This usually involves setting up the
	// pointers to PRG-ROM and CHR-ROM. Also use this as a time to
	// pass the pointers to our variables that are needed by the
	// memory mapper.

	NESDATA NesData;
	NesData.pCPU = &CPU;
	NesData.pPPU = &PPU;
	NesData.pabyPRGROM = abyPRGROM;
	NesData.wNumPRGROMPages = wNumPRGROMPages;
	NesData.pabyCHRROM = abyCHRROM;
	NesData.wNumCHRROMPages = wNumCHRROMPages;

	MapperOnLoad(&NesData);

	// Reset the NES.
	ResetNES();

	// Now if debug is set in the options then create the window 
	// to hold our disassembly and register contents disassemble
	// the rom starting at the current PC position.
	CreateDebugWindow(hwnd);

	// Dissassemble the beginning of the NES Rom.
	DissassembleROM();

	// Update the debugging information.
	UpdateDebugInfo();

	return S_OK;
} // end LoadNESFile()


//------------------------------------------------------------------------------
// Name: LoadROMToMem()
// Desc: Loads the ROM into memory for use. The .NES file is 
//       structure as follow (copied from Yoshi's nes doc.
//
//    +--------+------+------------------------------------------+
//    | Offset | Size | Content(s)                               |
//    +--------+------+------------------------------------------+
//    |   0    |  3   | 'NES'                                    |
//    |   3    |  1   | $1A                                      |
//    |   4    |  1   | 16K PRG-ROM page count                   |
//    |   5    |  1   | 8K CHR-ROM page count                    |
//    |   6    |  1   | ROM Control Byte #1                      |
//    |        |      |   %####vTsM                              |
//    |        |      |    |  ||||+- 0=Horizontal mirroring      |
//    |        |      |    |  ||||   1=Vertical mirroring        |
//    |        |      |    |  |||+-- 1=SRAM enabled              |
//    |        |      |    |  ||+--- 1=512-byte trainer present  |
//    |        |      |    |  |+---- 1=Four-screen mirroring     |
//    |        |      |    |  |                                  |
//    |        |      |    +--+----- Mapper # (lower 4-bits)     |
//    |   7    |  1   | ROM Control Byte #2                      |
//    |        |      |   %####0000                              |
//    |        |      |    |  |                                  |
//    |        |      |    +--+----- Mapper # (upper 4-bits)     |
//    |  8-15  |  8   | $00                                      |
//    | 16-..  |      | Actual 16K PRG-ROM pages (in linear      |
//    |  ...   |      | order). If a trainer exists, it precedes |
//    |  ...   |      | the first PRG-ROM page.                  |
//    | ..-EOF |      | CHR-ROM pages (in ascending order).      |
//    +--------+------+------------------------------------------+
//
//------------------------------------------------------------------------------
HRESULT LoadROMToMem(LPSTR strRomFileName)
{
	FILE* pRomFile = NULL;  // File pointer for NES Rom.

	// Open the .NES file for reading.
	if ((pRomFile = fopen(strRomFileName, "rb")) == NULL)
		FATAL(hwndMain, "Couldn't open nes file for reading.");

	// Read the rom's header.
	fread(abyRomHeader, 16, 1, pRomFile);

	// For now skip the trainer if it exists.
	if (abyRomHeader[6] & 0x04)
		fseek(pRomFile, 512, SEEK_CUR);

	// Allocate our memory for PRG-ROM and load the pages into memory.
	abyPRGROM = new BYTE[abyRomHeader[4]*NES_PRGROM_PAGESIZE];
	fread(abyPRGROM, NES_PRGROM_PAGESIZE, abyRomHeader[4], pRomFile);

	// Allocate our memory for CHR-ROM and load the pages into memory.
	abyCHRROM = new BYTE[abyRomHeader[5]*(NES_CHRROM_PAGESIZE*2)];
	fread(abyCHRROM, (NES_CHRROM_PAGESIZE*2), abyRomHeader[5], pRomFile);

	// Close the file.
	fclose(pRomFile);

	return S_OK;
} // end LoadROMToMem()


//------------------------------------------------------------------------------
// Name: MemoryDumpToString()
// Desc: Writes the memory dump of the passed variable in bytes. It places
//       the memory text in the string passed to the function.
//------------------------------------------------------------------------------
HRESULT MemoryDumpToString(char** pstrMemory, BYTE* pMemory, UINT uStartByte, 
						   UINT uEndByte)
{
	// Start at the beginning byte passed to this function and begin
	// disassembling. Every 16 bytes put the address at the left corner.
	for (UINT i = 0; i < uEndByte - uStartByte; i++)
	{
		// Display the current address.
		if ((i % 16) == 0)
		{
			if (i != 0)
			{
				sprintf(*pstrMemory, "%c%c%c", 13, 13, 10);
				*pstrMemory += 3;
			}

			sprintf(*pstrMemory, "%04X: ", i + uStartByte);
			*pstrMemory += 6;
		}

		// Print the memory byte and move on in the string.
		sprintf(*pstrMemory, "%02X ", pMemory[i+uStartByte]);
		*pstrMemory += 3;
	}

	return S_OK;
} // MemoryDumpToString()


//------------------------------------------------------------------------------
// Name: PromptForFileOpen()
// Desc: Prompts the user with the open file common dialog and 
//       returns the file name and path of the file the user selected.
//       If the user presses cancel the this function returns FALSE.
//------------------------------------------------------------------------------
HRESULT PromptForFileOpen(HWND hwndOwner)
{
	OPENFILENAME ofn;

	// Initialize OPENFILENAME
	ZeroMemory(&ofn, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hwndOwner;
	ofn.hInstance = g_hInstance;
	ofn.lpstrFile = (LPSTR)strFileName;
	ofn.nMaxFile = 512;
	ofn.lpstrFilter = "NES Files (*.nes)\0*.nes;\0\0\0";
	ofn.nFilterIndex = 1;
	ofn.lpstrFileTitle = NULL;
	ofn.nMaxFileTitle = 0;
	ofn.lpstrInitialDir = NULL;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER;

	// Prompt the user to select the file.
	if (GetOpenFileName(&ofn) == FALSE)
		return FALSE;

	// Save the filename string for the user.
	strcpy((LPSTR)strFileName, ofn.lpstrFile);

	return TRUE;
} // end PromptForFileOpen()


//------------------------------------------------------------------------------
// Name: ResetNES()
// Desc: Pretty self explainitory, resets the NES.
//------------------------------------------------------------------------------
HRESULT ResetNES()
{
	// Initialize the registers to their values.
	CPU.A = 0;
	CPU.X = 0;
	CPU.Y = 0;
	CPU.S = 0;
	CPU.F = 0;
	CPU.P = MAKEWORD(GetMemoryByte(0xFFFC), GetMemoryByte(0xFFFD));

	// Zero all the memory.
	ZeroMemory(CPU.Memory, 0x8000);

	// Reset the number of cpu cycles until the next scanline.
	CPU.byCycles = NUM_CYCLES_PER_SCANLINE;

	// Now display the updated information.
	UpdateDebugInfo();

	return S_OK;
} // end ResetNES()


//------------------------------------------------------------------------------
// Name: ResizeDebugControls()
// Desc: Resizes the code dissassembly list box as well as the 
//       registers list box to fit the child window.
//------------------------------------------------------------------------------
HRESULT ResizeDebugControls(HWND hwndChild, HWND hwndCodeLB, HWND hwndRegLB)
{
	RECT rcChild;    // RECT for the window the controls are in.

	// The the client area of the window these controls are to occupy
	GetClientRect(hwndChild, &rcChild);

	// Compute the coordinates and dimensions that the
	// list boxes will ocupy in the child window. We 
	// want them to fill the whole window.
	int nWidth = rcChild.right - rcChild.left;
	int nHeight = rcChild.bottom - rcChild.top;

	// Now move and resize the listboxes.
	MoveWindow(hwndCodeLB, 0, 0, nWidth-REGISTER_LISTBOX_WIDTH, nHeight, TRUE);
	MoveWindow(hwndRegLB, nWidth-REGISTER_LISTBOX_WIDTH, 0, 
		REGISTER_LISTBOX_WIDTH, nHeight, TRUE);

	return S_OK;
} // end ResizeDebugControls()


//------------------------------------------------------------------------------
// Name: CPU_DoCyclicTasks()
// Desc: Does all the tasks that must be done at the end of a scanline.
//------------------------------------------------------------------------------
HRESULT CPU_DoCyclicTasks()
{
	// If the cpu cycles are less than 0 then we must do
	// everything that must happen at the end of a scanline.
	if ((signed char)CPU.byCycles < 0)
	{
		// Add the number of cycles per scanline to the cycles.
		CPU.byCycles += NUM_CYCLES_PER_SCANLINE;
		// Increment which scanline are on.
		wScanline++;

		// Check for the sprite #0 hit.
		DoSprite0();

		// If we are passed the number of scanlines on the screen then
		// we are in vblank and we must update register $2002 and 
		// execute an NMI interrupt if bit 7 of $2000 is set.
		if (wScanline == NUM_SCANLINES_SCREEN)
		{
			// Set bit 7 of the PPU status register.
			CPU.Memory[0x2002] |= 0x80;

			// Do the NMI if we need to.
			if (CPU.Memory[0x2000] & 0x80)
			{
				// Push the PC onto the stack high byte first.
				PUSH_BYTE((BYTE)(CPU.P >> 8));
				PUSH_BYTE((BYTE)(CPU.P & 0xFF));
				
				// Push the flags register onto the stack.
				PUSH_BYTE(CPU.F);

				// Set the interrupt flag.
				CPU.F |= 0x04;

				// Set the PC equal to the address specified in the 
				// vector table for the NMI interrupt.
				CPU.P = MAKEWORD(GetMemoryByte(0xFFFA), GetMemoryByte(0xFFFB));

				// Subtract the interrupt latency from the CPU cycles.
				CPU.byCycles -= 7;

				// Break on the NMI interrupt.
				bBreak = TRUE;
			}
		}

		// If we are done with Vblank then we must update register
		// $2002 and reset the scanline counter.
		if (wScanline == NUM_SCANLINES_VBLANK+NUM_SCANLINES_SCREEN)
		{
			// Clear bit 7 and 6 of the PPU status register.
			CPU.Memory[0x2002] &= 0x3F;
			// Reset the scanline counter.
			wScanline = 0;
		}
	}

	return S_OK;
} // end CPU_DoCyclicTasks()


//------------------------------------------------------------------------------
// Name: CPU_Run()
// Desc: Runs the NES in run mode.
//------------------------------------------------------------------------------
DWORD WINAPI CPU_Run(LPVOID lpParam)
{
	DWORD dwBeginTime = 0; // The begining time of our frame.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -