📄 nestreme.cpp
字号:
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 + -