Nestreme.cpp
上传用户:luhy168
上传日期:2022-01-10
资源大小:240k
文件大小:62k
源码类别:

模拟服务器

开发平台:

Visual C++

  1. //------------------------------------------------------------------------------
  2. // Name: Nestreme.cpp
  3. // Desc: 
  4. //
  5. // Things still left to add in the debugger.
  6. // =========================================
  7. // 1.  Viewing all the memory, main, stack, prg-rom, chr-rom
  8. // 2.  Adding the memory registers to the register list view.
  9. // 3.  Better breakpoints, adding in dialog box, setting when dissassembling.
  10. // 4.  Step over.
  11. // 5.  View screen shot.
  12. // 6.  Editing memory.
  13. // 7.  
  14. //
  15. // Things to add to the CPU.
  16. // =========================
  17. // 1.  More acurate cpu cycle emulation.
  18. // 2.  Test all the instructions.
  19. // 3.  Update cpu bytes and cycles array.
  20. //
  21. // Things to add to the PPU.
  22. // =========================
  23. // 1.  Accurate emulation of registers 2005/2006.
  24. //------------------------------------------------------------------------------
  25. // Includes
  26. #include <windows.h>
  27. #include <commctrl.h>
  28. #include <afxres.h>
  29. #include <stdio.h>
  30. #include "resource.h"
  31. #include "Nestreme.h"
  32. #include "Cpu.h"
  33. #include "Io.h"
  34. #include "PrintInstrToString.h"
  35. #include "Gfx.h"
  36. #include "Sound.h"
  37. #include "NESData.h"
  38. //------------------------------------------------------------------------------
  39. // Name: WinMain()
  40. // Desc: Main entry point for a windows application.
  41. //------------------------------------------------------------------------------
  42. INT WINAPI WinMain(HINSTANCE hInstance,
  43.    HINSTANCE hPrevInst,
  44.    LPSTR lpCmdLine,
  45.    int nCmdShow)
  46. {
  47. MSG    msg;
  48. HACCEL hAccel;
  49. // Save the global variables
  50. g_hInstance = hInstance;
  51. // Initialize our classes.
  52. if (InitializeApp() != S_OK)
  53. return TRUE;
  54. // Create our main window
  55. hwndMain = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
  56.   strClassName,
  57.   strAppName,
  58.   WS_OVERLAPPEDWINDOW,
  59.   CW_USEDEFAULT,
  60.   CW_USEDEFAULT,
  61.   CW_USEDEFAULT,
  62.   CW_USEDEFAULT,
  63.   NULL,
  64.   NULL,
  65.   hInstance,
  66.   NULL);
  67. // Make sure we could create our window.
  68. if (hwndMain == NULL)
  69. return TRUE;
  70. // Load the keyboard accelerators.
  71. hAccel = LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_MAINACCEL));
  72. // Show and update our window.
  73. ShowWindow(hwndMain, SW_SHOWMAXIMIZED);
  74. UpdateWindow(hwndMain);
  75. // Now enter our main windows messaging loop.
  76. while (GetMessage(&msg, NULL, 0, 0))
  77. {
  78.     if (!TranslateAccelerator(hwndMain, hAccel, &msg))
  79. {
  80. TranslateMessage(&msg);
  81. DispatchMessage(&msg);
  82. }
  83. } // end windows messaging loop.
  84. // Make sure to delete everything we allocate.
  85. CleanUp();
  86. return msg.wParam;
  87. } // end WinMain()
  88. //------------------------------------------------------------------------------
  89. // Name: WndProcMain()
  90. // Desc: Our message handling procedure for windows messages.
  91. //------------------------------------------------------------------------------
  92. LRESULT CALLBACK WndProcMain(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  93. {
  94. HANDLE hThreadGo;    // Handle to the thread created when go is selected.
  95. DWORD  dwThreadIDGo; // Thread ID of the thread created when go is selected.
  96. switch (uMsg)
  97. {
  98. case WM_QUIT:
  99. case WM_DESTROY:
  100. PostQuitMessage(0);
  101. break;
  102. case WM_CREATE: 
  103. CreateMDIClientWindow(hwnd);
  104. break;
  105. case WM_COMMAND:
  106. switch (LOWORD(wParam))
  107. {
  108. case ID_DEBUG_BREAK:
  109. bBreak = TRUE;
  110. break;
  111. case ID_DEBUG_GO:
  112. hThreadGo = CreateThread(NULL, 0, CPU_RunUntilBreak, 
  113. NULL, NULL, &dwThreadIDGo);
  114. if (hThreadGo == NULL)
  115. FATAL(hwnd, "Couldn't create thread for execution.");
  116. break;
  117. case ID_DEBUG_RESTART:
  118. ResetNES();
  119. break;
  120. case ID_DEBUG_STEPINTO:
  121. CPU_Step();
  122. break;
  123. case ID_FILE_FREE:
  124. FreeNESFile();
  125. break;
  126. case ID_FILE_OPEN:
  127. LoadNESFile(hwnd);
  128. break;
  129. case ID_FILE_RUN:
  130. bRunning = TRUE;
  131. hThreadRun = CreateThread(NULL, 0, CPU_Run, 
  132. NULL, NULL, &dwThreadIDRun);
  133. if (hThreadRun == NULL)
  134. FATAL(hwnd, "Couldn't create thread for execution.");
  135. break;
  136. case ID_FILE_EXIT:
  137. PostQuitMessage(0);
  138. break;
  139. case ID_VIEW_MEMORY:
  140. CreateMemoryWindow(hwnd);
  141. break;
  142. case ID_VIEW_DISASSEMBLY:
  143. CreateDebugWindow(hwnd);
  144. DissassembleROM();
  145. UpdateDebugInfo();
  146. break;
  147. case ID_WINDOW_TILEHORIZONTALLY:
  148. SendMessage(hwndMDIClient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
  149. break;
  150. case ID_WINDOW_TILEVERTICALLY:
  151. SendMessage(hwndMDIClient, WM_MDITILE, MDITILE_VERTICAL, 0);
  152. break;
  153. }
  154. default:
  155. return DefFrameProc(hwnd, hwndMDIClient, uMsg, wParam, lParam);
  156. }
  157. return 0;
  158. } // end WndProcMain()
  159. //------------------------------------------------------------------------------
  160. // Name: WndProcDebug()
  161. // Desc: Our message handling procedure for windows messages.
  162. //------------------------------------------------------------------------------
  163. LRESULT CALLBACK WndProcDebug(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  164. {
  165. switch (uMsg)
  166. {
  167. case WM_NOTIFY:
  168. // Process notification messages.            
  169. switch (((LPNMHDR)lParam)->code) 
  170. case NM_DBLCLK:
  171. ToggleBreakpoint(((LPNMITEMACTIVATE)lParam)->iItem);
  172. break;
  173. }
  174. break;
  175. case WM_SIZE:
  176. ResizeDebugControls(hwndDebugWnd, hwndCodeLV, hwndRegsLV);
  177. default:
  178. return DefMDIChildProc(hwnd, uMsg, wParam, lParam);
  179. }
  180. return 0;
  181. } // end WndProcDebug()
  182. //------------------------------------------------------------------------------
  183. // Name: WndProcMemory()
  184. // Desc: Our message handling procedure for windows messages.
  185. //------------------------------------------------------------------------------
  186. LRESULT CALLBACK WndProcMemory(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  187. {
  188. static HWND hwndEdit = NULL;      // Handle to the edit control to hold the memory contents.
  189. static HWND hwndButton = NULL;    // Handle to the refresh button.
  190. static HWND hwndMemoryTab = NULL; // Handle to the memory tab control.
  191. switch (uMsg)
  192. {
  193. case WM_COMMAND:
  194. if (HIWORD(wParam) == BN_CLICKED) 
  195. {
  196. // Display the memory that corresponds, on what tab is selected.
  197. switch (TabCtrl_GetCurSel(hwndMemoryTab))
  198. {
  199. case 0:
  200. // Dump the main memory to the control.
  201. DisplayMainMemDump(hwndEdit);
  202. break;
  203. case 1:
  204. // Dump the stack memory to the control.
  205. DisplayStackMemDump(hwndEdit);
  206. break;
  207. case 2:
  208. // Dump the PRG-ROM memory to the control.
  209. DisplayPRGROMMemDump(hwndEdit);
  210. break;
  211. case 3:
  212. // Dump the PPU memory to the control.
  213. DisplayPPUMemDump(hwndEdit);
  214. break;
  215. case 4:
  216. // Dump the Sprite memory to the control.
  217. DisplaySpriteMemDump(hwndEdit);
  218. break;
  219. default:
  220. break;
  221. }
  222. }
  223. break;
  224. case WM_CREATE:
  225. // Create the tab control to hold all the different types of memory.
  226. if ((hwndMemoryTab = CreateTabControl(hwnd, IDS_MEMORY_MAIN, 5)) == NULL)
  227. FATAL(hwnd, "Couldn't create tab control.");
  228. // Create the edit control and initialize it.
  229. if (CreateMemoryDump(hwnd, &hwndEdit, &hwndButton) != S_OK)
  230. FATAL(hwnd, "Couldn't create the edit window.")
  231. // Dump the memory to the control.
  232. DisplayMainMemDump(hwndEdit);
  233. break;
  234. case WM_NOTIFY: 
  235. switch (((LPNMHDR) lParam)->code)
  236. case TCN_SELCHANGE: 
  237. {
  238. RECT rc;
  239. // Create the edit control and initialize it.
  240. if (CreateMemoryDump(hwnd, &hwndEdit, &hwndButton) != S_OK)
  241. FATAL(hwnd, "Couldn't create the edit window.")
  242. // Display the memory that corresponds, on what tab is selected.
  243. switch (TabCtrl_GetCurSel(hwndMemoryTab))
  244. {
  245. case 0:
  246. // Dump the main memory to the control.
  247. DisplayMainMemDump(hwndEdit);
  248. break;
  249. case 1:
  250. // Dump the stack memory to the control.
  251. DisplayStackMemDump(hwndEdit);
  252. break;
  253. case 2:
  254. // Dump the PRG-ROM memory to the control.
  255. DisplayPRGROMMemDump(hwndEdit);
  256. break;
  257. case 3:
  258. // Dump the PPU memory to the control.
  259. DisplayPPUMemDump(hwndEdit);
  260. break;
  261. case 4:
  262. // Dump the Sprite memory to the control.
  263. DisplaySpriteMemDump(hwndEdit);
  264. break;
  265. default:
  266. break;
  267. }
  268. // Update the window by calling sending the size message.
  269. GetClientRect(hwnd, &rc);
  270. SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(rc.right-rc.left, rc.bottom-rc.top));
  271. break; 
  272. }
  273. case TCN_SELCHANGING:
  274. DestroyWindow(hwndEdit);
  275. DestroyWindow(hwndButton);
  276. break;
  277. break; 
  278.         case WM_SIZE: 
  279.                 HDWP hdwp; 
  280.                 RECT rc; 
  281.  
  282.                 // Calculate the display rectangle, assuming the 
  283.                 // tab control is the size of the client area. 
  284.                 SetRect(&rc, 0, 0, LOWORD(lParam), HIWORD(lParam)); 
  285.                 TabCtrl_AdjustRect(hwndMemoryTab, FALSE, &rc); 
  286.  
  287.                 // Size the tab control to fit the client area. 
  288.                 hdwp = BeginDeferWindowPos(3); 
  289.                 DeferWindowPos(hdwp, hwndMemoryTab, NULL, 0, 0, 
  290.                     LOWORD(lParam), HIWORD(lParam), SWP_NOMOVE | SWP_NOZORDER); 
  291.  
  292.                 // Position and size the button control to fit at
  293. // the bottom of the tab control window.
  294.                 DeferWindowPos(hdwp, hwndButton, HWND_TOP, rc.left,
  295. rc.bottom-rc.top, 70, 20, 0); 
  296. // Position and size the edit control to fit the 
  297.                 // tab control's display area minus some room for
  298. // the button, and make sure the edit control is 
  299. // in front of the tab control. 
  300.                 DeferWindowPos(hdwp, hwndEdit, HWND_TOP, rc.left, rc.top, 
  301.                     rc.right-rc.left, rc.bottom-rc.top-30, 0); 
  302.                 EndDeferWindowPos(hdwp); 
  303. default:
  304. return DefMDIChildProc(hwnd, uMsg, wParam, lParam);
  305. }
  306. return 0;
  307. } // end WndProcMemory()
  308. //------------------------------------------------------------------------------
  309. // Name: WndProcRun()
  310. // Desc: Our message handling procedure for windows messages.
  311. //------------------------------------------------------------------------------
  312. LRESULT CALLBACK WndProcRun(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  313. {
  314. switch (uMsg)
  315. {
  316. case WM_DESTROY:
  317. case WM_CLOSE:
  318. // Tell the thread that runs the program to exit.
  319. bRunning = FALSE;
  320. break;
  321. case WM_KEYDOWN:
  322. switch (wParam)
  323. {
  324. case VK_ESCAPE:
  325. bRunning = FALSE;
  326. break;
  327. case 0x41: // A
  328. Joy1.SetButton(JOYSELECT);
  329. break;
  330. case 0x53: // S
  331. Joy1.SetButton(JOYSTART);
  332. break;
  333. case 0x5A: // Z
  334. Joy1.SetButton(JOYB);
  335. break;
  336. case 0x58: // X
  337. Joy1.SetButton(JOYA);
  338. break;
  339. case VK_UP:
  340. Joy1.SetButton(JOYUP);
  341. break;
  342. case VK_DOWN:
  343. Joy1.SetButton(JOYDOWN);
  344. break;
  345. case VK_LEFT:
  346. Joy1.SetButton(JOYLEFT);
  347. break;
  348. case VK_RIGHT:
  349. Joy1.SetButton(JOYRIGHT);
  350. break;
  351. case 0x31: // 1
  352. Options_bBackgroundEnabled ^= TRUE;
  353. break;
  354. case 0x32: // 2
  355. Options_bSpritesEnabled ^= TRUE;
  356. break;
  357. default:
  358. break;
  359. }
  360. break;
  361. case WM_KEYUP:
  362. switch (wParam)
  363. {
  364. case 0x41: // A
  365. Joy1.ClearButton(JOYSELECT);
  366. break;
  367. case 0x53: // S
  368. Joy1.ClearButton(JOYSTART);
  369. break;
  370. case 0x5A: // Z
  371. Joy1.ClearButton(JOYB);
  372. break;
  373. case 0x58: // X
  374. Joy1.ClearButton(JOYA);
  375. break;
  376. case VK_UP:
  377. Joy1.ClearButton(JOYUP);
  378. break;
  379. case VK_DOWN:
  380. Joy1.ClearButton(JOYDOWN);
  381. break;
  382. case VK_LEFT:
  383. Joy1.ClearButton(JOYLEFT);
  384. break;
  385. case VK_RIGHT:
  386. Joy1.ClearButton(JOYRIGHT);
  387. break;
  388. default:
  389. break;
  390. }
  391. break;
  392. case WM_SIZE:
  393. UpdateBounds(hwnd);
  394. default:
  395. return DefMDIChildProc(hwnd, uMsg, wParam, lParam);
  396. }
  397. return 0;
  398. } // end WndProcRun()
  399. //------------------------------------------------------------------------------
  400. // Name: AddBreakpoint()
  401. // Desc: Adds a breakpoint to the array of breakpoints.
  402. //------------------------------------------------------------------------------
  403. HRESULT AddBreakpoint(WORD wAddress, UINT uListViewItem)
  404. {
  405. // As long as we have room for another breakpoint add it to the array,
  406. // and check the list view check box.
  407. if (dwNumBreakpoints < OPTIONS_NUM_BREAKPOINTS)
  408. {
  409. awBreakpoints[dwNumBreakpoints++] = wAddress;
  410. ListView_Check(hwndCodeLV, uListViewItem);
  411. }
  412. else
  413. FATAL(hwndMain, "No more breakpoints allowed.");
  414. return S_OK;
  415. } // end AddBreakpoint()
  416. //------------------------------------------------------------------------------
  417. // Name: AddRegistersToListView()
  418. // Desc: Adds all the registers to the registers list view.
  419. //------------------------------------------------------------------------------
  420. HRESULT AddRegistersToListView()
  421. {
  422. char strText[128]; // Text to hold the info to be added.
  423. // Add the A register.
  424. ADD_VAR_TO_REG_LISTVIEW(0, "A", CPU.A);
  425. // Add the X register.
  426. ADD_VAR_TO_REG_LISTVIEW(1, "X", CPU.X);
  427. // Add the Y register.
  428. ADD_VAR_TO_REG_LISTVIEW(2, "Y", CPU.Y);
  429. // Add the SP register.
  430. ADD_VAR_TO_REG_LISTVIEW(3, "SP", CPU.S);
  431. // Add the PC register.
  432. ADD_VAR_TO_REG_LISTVIEW(4, "PC", CPU.P);
  433. // Add the flags register.
  434. ADD_VAR_TO_REG_LISTVIEW(5, "Flags", CPU.F);
  435. // Add a blank space in the list view.
  436. ADD_BLANK_TO_REG_LISTVIEW(6);
  437. // Add the Nintendo registers to the list view.
  438. ADD_VAR_TO_REG_LISTVIEW(7, "$2000", CPU.Memory[0x2000]);
  439. ADD_VAR_TO_REG_LISTVIEW(8, "$2001", CPU.Memory[0x2001]);
  440. ADD_VAR_TO_REG_LISTVIEW(9, "$2002", CPU.Memory[0x2002]);
  441. ADD_VAR_TO_REG_LISTVIEW(10, "$2003", CPU.Memory[0x2003]);
  442. ADD_VAR_TO_REG_LISTVIEW(11, "$2004", CPU.Memory[0x2004]);
  443. ADD_VAR_TO_REG_LISTVIEW(12, "$2005", CPU.Memory[0x2005]);
  444. ADD_VAR_TO_REG_LISTVIEW(13, "$2006", CPU.Memory[0x2006]);
  445. ADD_VAR_TO_REG_LISTVIEW(14, "$2007", CPU.Memory[0x2007]);
  446. ADD_VAR_TO_REG_LISTVIEW(15, "$4000", CPU.Memory[0x4000]);
  447. ADD_VAR_TO_REG_LISTVIEW(16, "$4001", CPU.Memory[0x4001]);
  448. ADD_VAR_TO_REG_LISTVIEW(17, "$4002", CPU.Memory[0x4002]);
  449. ADD_VAR_TO_REG_LISTVIEW(18, "$4003", CPU.Memory[0x4003]);
  450. ADD_VAR_TO_REG_LISTVIEW(19, "$4004", CPU.Memory[0x4004]);
  451. ADD_VAR_TO_REG_LISTVIEW(20, "$4005", CPU.Memory[0x4005]);
  452. ADD_VAR_TO_REG_LISTVIEW(21, "$4006", CPU.Memory[0x4006]);
  453. ADD_VAR_TO_REG_LISTVIEW(22, "$4007", CPU.Memory[0x4007]);
  454. ADD_VAR_TO_REG_LISTVIEW(23, "$4008", CPU.Memory[0x4008]);
  455. ADD_VAR_TO_REG_LISTVIEW(24, "$4009", CPU.Memory[0x4009]);
  456. ADD_VAR_TO_REG_LISTVIEW(25, "$4010", CPU.Memory[0x4010]);
  457. ADD_VAR_TO_REG_LISTVIEW(26, "$4011", CPU.Memory[0x4011]);
  458. ADD_VAR_TO_REG_LISTVIEW(17, "$4012", CPU.Memory[0x4012]);
  459. ADD_VAR_TO_REG_LISTVIEW(18, "$4013", CPU.Memory[0x4013]);
  460. ADD_VAR_TO_REG_LISTVIEW(19, "$4014", CPU.Memory[0x4014]);
  461. ADD_VAR_TO_REG_LISTVIEW(30, "$4015", CPU.Memory[0x4015]);
  462. ADD_VAR_TO_REG_LISTVIEW(31, "$4016", CPU.Memory[0x4016]);
  463. ADD_VAR_TO_REG_LISTVIEW(32, "$4017", CPU.Memory[0x4017]);
  464. // Add a blank space in the list view.
  465. ADD_BLANK_TO_REG_LISTVIEW(33);
  466. // Add the cycles to the list view.
  467. sprintf(strText, "Cycles");
  468. InsertListViewText(hwndRegsLV, 34, 0, strText);
  469. sprintf(strText, "%d", CPU.byCycles);
  470. InsertListViewText(hwndRegsLV, 34, 1, strText);
  471. // Add the scanlines to the list view.
  472. sprintf(strText, "Scanline");
  473. InsertListViewText(hwndRegsLV, 35, 0, strText);
  474. sprintf(strText, "%d", wScanline);
  475. InsertListViewText(hwndRegsLV, 35, 1, strText);
  476. // Add the VRAM address to the list view.
  477. ADD_VAR_TO_REG_LISTVIEW(36, "VRAM Address", wVRAMAddress);
  478. return S_OK;
  479. } // end AddRegistersToListView()
  480. //------------------------------------------------------------------------------
  481. // Name: CleanUp()
  482. // Desc: Deletes everthing that has been dynamically created.
  483. //------------------------------------------------------------------------------
  484. HRESULT CleanUp()
  485. {
  486. // Delete our rom that we allocated.
  487. FreeNESFile();
  488. return S_OK;
  489. } // end CleanUp()
  490. //------------------------------------------------------------------------------
  491. // Name: CreateMemoryDump()
  492. // Desc: Creates an edit control to hold our memory contents and
  493. //       a button for refreshing them.
  494. //------------------------------------------------------------------------------
  495. HRESULT CreateMemoryDump(HWND hwndParent, HWND* phwndEdit, HWND* phwndButton)
  496. {
  497. // Create the edit control.
  498. *phwndEdit = CreateWindow("EDIT", NULL,
  499. WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_READONLY, 
  500. 20, 20, 200, 200, hwndParent, NULL, g_hInstance, NULL);
  501. *phwndButton = CreateWindow( 
  502. "BUTTON",   // predefined class 
  503. "Refresh",       // button text 
  504. WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // styles 
  505. 0,         // starting x position 
  506. 0,         // starting y position 
  507. 40,        // button width 
  508. 20,        // button height 
  509. hwndParent, // parent window 
  510. NULL,       // No menu 
  511. g_hInstance, 
  512. NULL);      // pointer not needed 
  513. // Set the text to a fixed width font and the background color to white.
  514. SendMessage(*phwndEdit, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), TRUE);
  515. SendMessage(*phwndButton, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE);
  516. return S_OK;
  517. } // CreateMemoryDump()
  518. //------------------------------------------------------------------------------
  519. // Name: CreateDebugWindow()
  520. // Desc: Creates the window to hold our disassembly and register contents.
  521. //       This is a MDI child window with a splitter to separate the registers
  522. //       and the code disassembly.
  523. //------------------------------------------------------------------------------
  524. HRESULT CreateDebugWindow(HWND hwndParent)
  525. {
  526. // Create the child window for the code and registers.
  527. hwndDebugWnd = CreateWindowEx(WS_EX_MDICHILD,
  528.   strDebugClassName,
  529.   strDebugTitleName,
  530.   WS_CHILD,
  531.   CW_USEDEFAULT,
  532.   CW_USEDEFAULT,
  533.   CW_USEDEFAULT,
  534.   CW_USEDEFAULT,
  535.   hwndMDIClient,
  536.   NULL,
  537.   g_hInstance,
  538.   NULL);
  539. if (hwndDebugWnd == NULL)
  540. FATAL(hwndParent, "Couldn't Create the debug window");
  541. ShowWindow(hwndDebugWnd, SW_SHOWMAXIMIZED);
  542. // Create the code list view box
  543. hwndCodeLV = CreateListView(hwndDebugWnd, IDS_CODE_COLUMN0, 4);
  544. if (hwndCodeLV == NULL)
  545. FATAL(hwndParent, "Couldn't create the code list view. Make sure IE3 is installed.");
  546. // Create the registers list view.
  547. hwndRegsLV = CreateListView(hwndDebugWnd, IDS_REGS_COLUMN0, 2);
  548. if (hwndCodeLV == NULL)
  549. FATAL(hwndParent, "Couldn't create the registers list view. Make sure IE3 is installed.");
  550. // Set the font to a fixed width font
  551. SendMessage(hwndCodeLV, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), TRUE);
  552. SendMessage(hwndRegsLV, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), TRUE);
  553. // Set the extended styles for the list views.
  554. ListView_SetExtendedListViewStyle(hwndCodeLV, 
  555. LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_CHECKBOXES);
  556. // Create and resize the code and registers list boxes
  557. // to fit the child window.
  558. ResizeDebugControls(hwndDebugWnd, hwndCodeLV, hwndRegsLV);
  559. // Add the registers to the registers list view.
  560. AddRegistersToListView();
  561. // Create the tooltip window for viewing our memory contents.
  562. //CreateTooltip(hwndCodeLV, "Hello!");
  563. //SendMessage(hwndCodeLV, LVM_SETTOOLTIPS, 0, (LPARAM)hwndTT);
  564. return S_OK;
  565. } // end CreateDebugWindow()
  566. //------------------------------------------------------------------------------
  567. // Name: CreateListView()
  568. // Desc: Creates a list view control, initializes the columns, and
  569. //       returns the handle to the window if successful.
  570. //------------------------------------------------------------------------------
  571. HWND CreateListView(HWND hWndParent, UINT uFirstColRsc, UINT uNumColumns)
  572. {
  573. HWND      hwndListView;
  574. LV_COLUMN lvC;
  575. char      strCol[50];
  576. // Create a list view control in report view.
  577. hwndListView = CreateWindowEx(0L, WC_LISTVIEW, "", WS_VISIBLE | WS_CHILD |
  578.   WS_CHILD | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS,
  579.   0, 0, 0, 0, hWndParent, 
  580.   NULL, g_hInstance, NULL);
  581. if (hwndListView == NULL)
  582.   return NULL;
  583. // Create columns.
  584. lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  585. lvC.fmt = LVCFMT_LEFT;
  586. lvC.cx = 150;
  587. lvC.pszText = strCol;
  588. // Add the columns.
  589. for (UINT i = 0; i < uNumColumns; i++)
  590. {
  591. lvC.iSubItem = i;
  592. LoadString(g_hInstance, uFirstColRsc+i, strCol, sizeof(strCol));
  593. if (ListView_InsertColumn(hwndListView, i, &lvC) == -1)
  594. return NULL;
  595. }
  596. return hwndListView;
  597. } // end CreateListView()
  598. //------------------------------------------------------------------------------
  599. // Name: CreateMDIClientWindow()
  600. // Desc: Creates the client window for our frame window.
  601. //------------------------------------------------------------------------------
  602. HRESULT CreateMDIClientWindow(HWND hwndFrame)
  603. {
  604. CLIENTCREATESTRUCT ccs; 
  605. // Retrieve the handle to the window menu and assign the 
  606. // first child window identifier. 
  607. ccs.hWindowMenu = GetSubMenu(GetMenu(hwndFrame), WINDOWMENU);
  608. ccs.idFirstChild = IDM_WINDOWCHILD;
  609. // Create the MDI client window.
  610. hwndMDIClient = CreateWindow( "MDICLIENT", (LPCTSTR)NULL,
  611. WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
  612. 0, 0, 0, 0, hwndFrame, (HMENU)0xCAC, g_hInstance, (LPSTR)&ccs);
  613. ShowWindow(hwndMDIClient, SW_SHOW);
  614. return S_OK;
  615. } // end CreateMDIClientWindow()
  616. //------------------------------------------------------------------------------
  617. // Name: CreateMemoryWindow()
  618. // Desc: Creates the window hold our memory contents.
  619. //------------------------------------------------------------------------------
  620. HRESULT CreateMemoryWindow(HWND hwndParent)
  621. {
  622. // Create the child window for the code and registers.
  623. hwndMemoryWnd = CreateWindowEx(WS_EX_MDICHILD,
  624.    strMemoryClassName,
  625.    strMemoryTitleName,
  626.    WS_CHILD,
  627.    CW_USEDEFAULT,
  628.    CW_USEDEFAULT,
  629.    CW_USEDEFAULT,
  630.    CW_USEDEFAULT,
  631.    hwndMDIClient,
  632.    NULL,
  633.    g_hInstance,
  634.    NULL);
  635. if (hwndMemoryWnd == NULL)
  636. FATAL(hwndParent, "Couldn't Create the debug window");
  637. return S_OK;
  638. } // end CreateMemoryWindow()
  639. //------------------------------------------------------------------------------
  640. // Name: CreateTabControl()
  641. // Desc: Creates a tab control, sized to fit the specified parent window's 
  642. //       client area, and adds the number of tabs passed into the function. 
  643. //       All the strings in the string table must be in order.
  644. //------------------------------------------------------------------------------
  645. HWND WINAPI CreateTabControl(HWND hwndParent, UINT uTextResource, UINT uNumTabs)
  646. RECT   rcClient;     // Client rectangle coordinates.
  647. HWND   hwndTab;      // Window handle to the tab control.
  648. TCITEM tie;          // Tab item struction
  649. char   strText[128]; // Text to hold the string of the tab
  650. // Get the dimensions of the parent window's client area, and 
  651. // create a tab control child window of that size. 
  652. GetClientRect(hwndParent, &rcClient); 
  653. hwndTab = CreateWindow(WC_TABCONTROL, "", 
  654. WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 
  655. 0, 0, rcClient.right, rcClient.bottom, 
  656. hwndParent, NULL, g_hInstance, NULL); 
  657. if (hwndTab == NULL) 
  658. return NULL; 
  659. // Add the number of tabs passed to the function.
  660. tie.mask = TCIF_TEXT | TCIF_IMAGE; 
  661. tie.iImage = -1; 
  662. tie.pszText = strText; 
  663. for (UINT i = 0; i < uNumTabs; i++) 
  664. LoadString(g_hInstance, uTextResource+i, strText, sizeof(strText)); 
  665. if (TabCtrl_InsertItem(hwndTab, i, &tie) == -1) 
  666. DestroyWindow(hwndTab); 
  667. return NULL; 
  668. // Set the font of the control.
  669. SendMessage(hwndTab, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE);
  670. return hwndTab; 
  671. } // CreateTabControl()
  672. //------------------------------------------------------------------------------
  673. // Name: CreateTooltip()
  674. // Desc: Creates a tooltip window adding the string as the text.
  675. //------------------------------------------------------------------------------
  676. HRESULT CreateTooltip(HWND hwnd, LPSTR strToolTip)
  677. {
  678. TOOLINFO ti;      // struct specifying info about tool in tooltip control.
  679. UINT     uid = 0; // for ti initialization.
  680. RECT     rect;    // for client area coordinates.
  681. // Create the tooltip window.
  682. hwndTT = CreateWindowEx(WS_EX_TOPMOST,
  683. TOOLTIPS_CLASS,
  684. NULL,
  685. WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
  686. CW_USEDEFAULT,
  687. CW_USEDEFAULT,
  688. CW_USEDEFAULT,
  689. CW_USEDEFAULT,
  690. hwnd,
  691. NULL,
  692. g_hInstance,
  693. NULL);
  694. // Set the window position.
  695. SetWindowPos(hwndTT,
  696. HWND_TOPMOST,
  697. 0,
  698. 0,
  699. 0,
  700. 0,
  701. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  702. // Get coordinates of the main window.
  703. GetClientRect(hwnd, &rect);
  704. // Initialize the members fo the tool tip info structure.
  705. ti.cbSize = sizeof(TOOLINFO);
  706. ti.uFlags = TTF_SUBCLASS;
  707. ti.hwnd = hwnd;
  708. ti.hinst = g_hInstance;
  709. ti.uId = uid;
  710. ti.lpszText = strToolTip;
  711. // Tooltip control will cover the whole window
  712. ti.rect.left = rect.left;    
  713. ti.rect.top = rect.top;
  714. ti.rect.right = rect.right;
  715. ti.rect.bottom = rect.bottom;
  716. // Send a message to add the tooltip
  717. SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  718. SendMessage(hwndTT, TTM_ACTIVATE, (WPARAM)TRUE, 0);
  719. return S_OK;
  720. } // end CreateTooltip()
  721. //------------------------------------------------------------------------------
  722. // Name: DisableBreakpoint()
  723. // Desc: Disables a breakpoint by removing it from the breakpoint array.
  724. //------------------------------------------------------------------------------
  725. HRESULT DisableBreakpoint(UINT uBreakpointIndex, UINT uListViewItem)
  726. {
  727. // Remove the breakpoint from the array by setting it 0.
  728. awBreakpoints[uBreakpointIndex] = 0;
  729. // Copy all the old breakpoints into the old position.
  730. for (int i = uBreakpointIndex; i < OPTIONS_NUM_BREAKPOINTS-1; i++)
  731. awBreakpoints[i] = awBreakpoints[i+1];
  732. // Uncheck the breakpoint in the list view.
  733. ListView_UnCheck(hwndCodeLV, uListViewItem);
  734. // Finally decrement the number of breakpoints.
  735. dwNumBreakpoints--;
  736. return S_OK;
  737. } // end DisableBreakpoint()
  738. //------------------------------------------------------------------------------
  739. // Name: DisplayMainMemDump()
  740. // Desc: Dumps the main memory to a string and sets the text of an edit
  741. //       control to that string.
  742. //------------------------------------------------------------------------------
  743. HRESULT DisplayMainMemDump(HWND hwndEditCtrl)
  744. {
  745. char  strMemDump[0x1FFFF];       // String to hold the memory dump.
  746. char* pstrMemDump = strMemDump; // Pointer to the memory dump string.
  747. // Dump the first part of main memory to the string.
  748. sprintf(pstrMemDump, "Main Memory: %c%c%c%c%c%c", 13, 13, 10, 13, 13, 10);
  749. pstrMemDump += strlen(strMemDump);
  750. MemoryDumpToString(&pstrMemDump, &CPU.Memory[0], 0, 0x8000);
  751. // Set the text in the edit control.
  752. SendMessage(hwndEditCtrl, WM_SETTEXT, 0, (LPARAM) strMemDump);
  753. return S_OK;
  754. } // DisplayMainMemDump()
  755. //------------------------------------------------------------------------------
  756. // Name: DisplayPPUMemDump()
  757. // Desc: Dumps the PPU memory to a string and sets the text of an edit
  758. //       control to that string.
  759. //------------------------------------------------------------------------------
  760. HRESULT DisplayPPUMemDump(HWND hwndEditCtrl)
  761. {
  762. char  strMemDump[0x1FFFF];       // String to hold the memory dump.
  763. char* pstrMemDump = strMemDump; // Pointer to the memory dump string.
  764. // Dump the first bank of CHR-ROM to the string.
  765. sprintf(pstrMemDump, "CHR-ROM Bank 1: %c%c%c%c%c%c", 13, 13, 10, 13, 13, 10);
  766. pstrMemDump += 22;
  767. MemoryDumpToString(&pstrMemDump, &(*(PPU.apbyPatternTables[0])), 0, 0x1000);
  768. // Dump the second bank of CHR-ROM to the string.
  769. sprintf(pstrMemDump, "%c%c%c%c%c%cCHR-ROM Bank 2: %c%c%c%c%c%c", 
  770. 13, 13, 10, 13, 13, 10, 13, 13, 10, 13, 13, 10);
  771. pstrMemDump += 28;
  772. MemoryDumpToString(&pstrMemDump, &(*(PPU.apbyPatternTables[1])), 0, 0x1000);
  773. // Dump the first name and attribute table to the string.
  774. sprintf(pstrMemDump, "%c%c%c%c%c%cName Table 1: %c%c%c%c%c%c", 
  775. 13, 13, 10, 13, 13, 10, 13, 13, 10, 13, 13, 10);
  776. pstrMemDump += 26;
  777. MemoryDumpToString(&pstrMemDump, &(*(PPU.apbyNameTables[0])), 0, 0x400);
  778. // Dump the second name and attribute table to the string.
  779. sprintf(pstrMemDump, "%c%c%c%c%c%cName Table 2: %c%c%c%c%c%c", 
  780. 13, 13, 10, 13, 13, 10, 13, 13, 10, 13, 13, 10);
  781. pstrMemDump += 26;
  782. MemoryDumpToString(&pstrMemDump, &(*(PPU.apbyNameTables[1])), 0, 0x400);
  783. // Dump the third name and attribute table to the string.
  784. sprintf(pstrMemDump, "%c%c%c%c%c%cName Table 3: %c%c%c%c%c%c", 
  785. 13, 13, 10, 13, 13, 10, 13, 13, 10, 13, 13, 10);
  786. pstrMemDump += 26;
  787. MemoryDumpToString(&pstrMemDump, &(*(PPU.apbyNameTables[2])), 0, 0x400);
  788. // Dump the fourth name and attribute table to the string.
  789. sprintf(pstrMemDump, "%c%c%c%c%c%cName Table 4: %c%c%c%c%c%c", 
  790. 13, 13, 10, 13, 13, 10, 13, 13, 10, 13, 13, 10);
  791. pstrMemDump += 26;
  792. MemoryDumpToString(&pstrMemDump, &(*(PPU.apbyNameTables[3])), 0, 0x400);
  793. // Dump the palettes to the string.
  794. sprintf(pstrMemDump, "%c%c%c%c%c%cPalettes: %c%c%c%c%c%c", 
  795. 13, 13, 10, 13, 13, 10, 13, 13, 10, 13, 13, 10);
  796. pstrMemDump += 22;
  797. MemoryDumpToString(&pstrMemDump, &(PPU.abyPalettes[0]), 0, 0x20);
  798. // Set the text in the edit control.
  799. SendMessage(hwndEditCtrl, WM_SETTEXT, 0, (LPARAM) strMemDump);
  800. return S_OK;
  801. } // DisplayPPUMemDump()
  802. //------------------------------------------------------------------------------
  803. // Name: DisplayPRGROMMemDump()
  804. // Desc: Dumps the stack memory to a string and sets the text of an edit
  805. //       control to that string.
  806. //------------------------------------------------------------------------------
  807. HRESULT DisplayPRGROMMemDump(HWND hwndEditCtrl)
  808. {
  809. char  strMemDump[0x1FFFF];       // String to hold the memory dump.
  810. char* pstrMemDump = strMemDump; // Pointer to the memory dump string.
  811. // Dump the first bank of memory to the string.
  812. sprintf(pstrMemDump, "PRG-ROM Bank 1: %c%c%c%c%c%c", 13, 13, 10, 13, 13, 10);
  813. pstrMemDump += 22;
  814. MemoryDumpToString(&pstrMemDump, &(*CPU.pbyPRGROMBank1), 0, 0x4000);
  815. // Dump the second bank of memory to the string.
  816. sprintf(pstrMemDump, "%c%c%c%c%c%cPRG-ROM Bank 2: %c%c%c%c%c%c", 
  817. 13, 13, 10, 13, 13, 10, 13, 13, 10, 13, 13, 10);
  818. pstrMemDump += 28;
  819. MemoryDumpToString(&pstrMemDump, &(*CPU.pbyPRGROMBank2), 0, 0x4000);
  820. // Set the text in the edit control.
  821. SendMessage(hwndEditCtrl, WM_SETTEXT, 0, (LPARAM) strMemDump);
  822. return S_OK;
  823. } // DisplayPRGROMMemDump()
  824. //------------------------------------------------------------------------------
  825. // Name: DisplaySpriteMemDump()
  826. // Desc: Dumps the sprite memory to a string and sets the text of an edit
  827. //       control to that string.
  828. //------------------------------------------------------------------------------
  829. HRESULT DisplaySpriteMemDump(HWND hwndEditCtrl)
  830. {
  831. char  strMemDump[0xFFFF];       // String to hold the memory dump.
  832. char* pstrMemDump = strMemDump; // Pointer to the memory dump string.
  833. // Dump the sprite memory to the string.
  834. sprintf(pstrMemDump, "Sprite Memory: %c%c%c%c%c%c", 13, 13, 10, 13, 13, 10);
  835. pstrMemDump += strlen(strMemDump);
  836. MemoryDumpToString(&pstrMemDump, abySPRRAM, 0, 0x100);
  837. // Set the text in the edit control.
  838. SendMessage(hwndEditCtrl, WM_SETTEXT, 0, (LPARAM) strMemDump);
  839. return S_OK;
  840. } // DisplaySpriteMemDump()
  841. //------------------------------------------------------------------------------
  842. // Name: DisplayStackMemDump()
  843. // Desc: Dumps the stack memory to a string and sets the text of an edit
  844. //       control to that string.
  845. //------------------------------------------------------------------------------
  846. HRESULT DisplayStackMemDump(HWND hwndEditCtrl)
  847. {
  848. char  strMemDump[0xFFFF];       // String to hold the memory dump.
  849. char* pstrMemDump = strMemDump; // Pointer to the memory dump string.
  850. // Dump the stack memory to the string.
  851. sprintf(pstrMemDump, "Stack Memory: %c%c%c%c%c%c", 13, 13, 10, 13, 13, 10);
  852. pstrMemDump += strlen(strMemDump);
  853. MemoryDumpToString(&pstrMemDump, &CPU.Memory[0x100], 0, 0x100);
  854. // Set the text in the edit control.
  855. SendMessage(hwndEditCtrl, WM_SETTEXT, 0, (LPARAM) strMemDump);
  856. return S_OK;
  857. } // DisplayStackMemDump()
  858. //------------------------------------------------------------------------------
  859. // Name: DissassembleROM()
  860. // Desc: Dissassembles the rom from the current PC position and displays
  861. //       the results in the code dissassemble list box.
  862. //------------------------------------------------------------------------------
  863. HRESULT DissassembleROM()
  864. {
  865. char strInstr[128]; // String to hold the instruction output.
  866. WORD wPC = CPU.P;   // Temporary PC register.
  867. BYTE byNumBytes;    // Number of bytes to add to the temp PC register.
  868. // Clear the list view.
  869. ListView_DeleteAllItems(hwndCodeLV);
  870. // Display all the instructions.
  871. for (int i = 0; i < OPTIONS_NUM_DEBUGLINES; i++)
  872. {
  873. // Reset the instruction string.
  874. memset(strInstr, '', 128);
  875. // Print the instruction to our string.
  876. byNumBytes = PrintInstrToString(strInstr, wPC);
  877. // Add the strings to the code list view.
  878. InsertListViewText(hwndCodeLV, i, 0, &strInstr[DISASM_PC_OFFSET]);
  879. InsertListViewText(hwndCodeLV, i, 1, &strInstr[DISASM_MACHINECODE_OFFSET]);
  880. InsertListViewText(hwndCodeLV, i, 2, &strInstr[DISASM_INSTRUCTION_OFFSET]);
  881. InsertListViewText(hwndCodeLV, i, 3, &strInstr[DISASM_MEMCONTENTS_OFFSET]);
  882. // TODO: Make sure to add the breakpoint stuff here.
  883. // Move to the next instruction.
  884. wPC += byNumBytes;
  885. }
  886. return S_OK;
  887. } // end DissassembleROM()
  888. //------------------------------------------------------------------------------
  889. // Name: FreeNESFile()
  890. // Desc: Unloads the ROM that has been loaded so a new one can be loaded.
  891. //------------------------------------------------------------------------------
  892. HRESULT FreeNESFile()
  893. {
  894. // Free the mapper library.
  895. FreeLibrary(hinstMapperLib);
  896. // Delete our rom that we allocated.
  897. SAFE_DELETE_ARRAY(abyPRGROM);
  898. SAFE_DELETE_ARRAY(abyCHRROM);
  899. return S_OK;
  900. } // end FreeNESFile()
  901. //------------------------------------------------------------------------------
  902. // Name: InitializeApp()
  903. // Desc: Does any initialization that needs to be done before the
  904. //       main windows loop begins.
  905. //------------------------------------------------------------------------------
  906. HRESULT InitializeApp()
  907. {
  908. WNDCLASS wc;
  909. INITCOMMONCONTROLSEX icex;
  910. // Register the frame windows class.
  911. wc.cbClsExtra    = 0;
  912. wc.cbWndExtra    = 0;
  913. wc.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
  914. wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  915. wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  916. wc.hInstance     = g_hInstance;
  917. wc.lpfnWndProc   = (WNDPROC)WndProcMain;
  918. wc.lpszClassName = strClassName;
  919. wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MAINMENU);
  920. wc.style         = CS_HREDRAW | CS_VREDRAW;
  921. if (!RegisterClass(&wc))
  922. return ERROR;
  923. // Register the debug window class. 
  924.     wc.hIcon         = LoadIcon(g_hInstance, IDI_APPLICATION); 
  925.     wc.lpfnWndProc   = (WNDPROC)WndProcDebug; 
  926.     wc.lpszClassName = strDebugClassName; 
  927.     wc.lpszMenuName  = (LPSTR)NULL; 
  928.  
  929.     if (!RegisterClass(&wc)) 
  930.         return ERROR;
  931. // Register the memory window class.
  932.     wc.hIcon         = LoadIcon(g_hInstance, IDI_APPLICATION); 
  933.     wc.lpfnWndProc   = (WNDPROC)WndProcMemory; 
  934.     wc.lpszClassName = strMemoryClassName; 
  935.     wc.lpszMenuName  = (LPSTR)NULL; 
  936.     if (!RegisterClass(&wc)) 
  937.         return ERROR;
  938. // Register the run window class.
  939.     wc.hIcon         = LoadIcon(g_hInstance, IDI_APPLICATION); 
  940.     wc.lpfnWndProc   = (WNDPROC)WndProcRun; 
  941.     wc.lpszClassName = strRunClassName; 
  942.     wc.lpszMenuName  = (LPSTR)NULL; 
  943.     if (!RegisterClass(&wc)) 
  944.         return ERROR;
  945. // Ensure that the common control DLL is loaded. 
  946. icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
  947. icex.dwICC  = ICC_LISTVIEW_CLASSES;
  948. InitCommonControlsEx(&icex); 
  949. // Initialize the global variables
  950. abyPRGROM = NULL;
  951. abyCHRROM = NULL;
  952. return S_OK;
  953. } // InitializeApp()
  954. //------------------------------------------------------------------------------
  955. // Name: InsertListViewText()
  956. // Desc: Inserts text into an item of a list view control.
  957. //------------------------------------------------------------------------------
  958. HRESULT InsertListViewText(HWND hwndLV, UINT uRow, UINT uCol, LPSTR strText)
  959. {
  960. LV_ITEM lvI; // List view item structure.
  961. // Fill out the item structure.
  962. lvI.mask = LVIF_TEXT;
  963. lvI.iItem = uRow;
  964. lvI.iSubItem = uCol;
  965. lvI.pszText = strText;
  966. lvI.state = 0;
  967. lvI.stateMask = 0;
  968.     
  969. // Add the item. First try adding it as an item. If that doesn't work,
  970. // then try adding the item as a subitem. If no good, return an error.
  971. if (ListView_InsertItem(hwndLV, &lvI) == -1)
  972. if (ListView_SetItem(hwndLV, &lvI) == FALSE)
  973. return ERROR;
  974. return S_OK;
  975. } // end InsertListViewText()
  976. //------------------------------------------------------------------------------
  977. // Name: LoadNESFile()
  978. // Desc: Called when the user selected Open from the File menu. Loads
  979. //       the ROM into memory and sets the program up for debugging or
  980. //       runs the game depending on the option set.
  981. //------------------------------------------------------------------------------
  982. HRESULT LoadNESFile(HWND hwnd)
  983. {
  984. char strMapperLibName[512]; // FileName for the mapper .dll.
  985. // Popup the open file dialog box.
  986. PromptForFileOpen(hwnd);
  987. // Load the ROM into memory.
  988. LoadROMToMem((LPSTR)strFileName);
  989. // Save the number of program and character rom pages.
  990. wNumPRGROMPages = abyRomHeader[4];
  991. wNumCHRROMPages = abyRomHeader[5];
  992. // Some games only hold one (1) 16K bank of PRG-ROM, 
  993. // which should be loaded into both $C000 and $8000.
  994. // Otherwise initialize the banks to the first 2 pages.
  995. if (abyRomHeader[4] == 1)
  996. {
  997. CPU.pbyPRGROMBank1 = &abyPRGROM[0];
  998. CPU.pbyPRGROMBank2 = &abyPRGROM[0];
  999. }
  1000. else
  1001. {
  1002. CPU.pbyPRGROMBank1 = &abyPRGROM[0];
  1003. CPU.pbyPRGROMBank2 = &abyPRGROM[NES_PRGROM_PAGESIZE];
  1004. }
  1005. // Initialize the pattern tables to point to the first and
  1006. // second banks of CHR-ROM.
  1007. PPU.apbyPatternTables[0] = &abyCHRROM[0];
  1008. PPU.apbyPatternTables[1] = &abyCHRROM[NES_CHRROM_PAGESIZE];
  1009. // Initialize the pointers to the name tables depending on the
  1010. // mirroring of the ROM. Bit 1 of the rom header offset 6
  1011. // is 1 for vertical mirroring and 0 for horizontal mirroring.
  1012. if (abyRomHeader[6] & 1)
  1013. {
  1014. // For vertical mirroring, name tables 0 and 2 point to
  1015. // the first name table and name tables 1 and 3 point to
  1016. // the second name table.
  1017. PPU.apbyNameTables[0] = &PPU.abyNameTables[0];
  1018. PPU.apbyNameTables[1] = &PPU.abyNameTables[0x400];
  1019. PPU.apbyNameTables[2] = &PPU.abyNameTables[0];
  1020. PPU.apbyNameTables[3] = &PPU.abyNameTables[0x400];
  1021. }
  1022. else
  1023. {
  1024. // For horizontal mirroring, name tables 0 and 1 point to
  1025. // the first name table and name tables 2 and 3 point to
  1026. // the second name table.
  1027. PPU.apbyNameTables[0] = &PPU.abyNameTables[0];
  1028. PPU.apbyNameTables[1] = &PPU.abyNameTables[0];
  1029. PPU.apbyNameTables[2] = &PPU.abyNameTables[0x400];
  1030. PPU.apbyNameTables[3] = &PPU.abyNameTables[0x400];
  1031. }
  1032. // Get the string for the file of the mapper .dll
  1033. sprintf(strMapperLibName, ".\mappers\mapper%d.dll", (abyRomHeader[6]>>4)|(abyRomHeader[7]&0xF0));
  1034. // Load the mapper .dll
  1035. if ((hinstMapperLib = LoadLibrary(strMapperLibName)) == NULL)
  1036. FATAL(hwnd, "Could not find the .dll for the mapper.");
  1037. // Get all the function addresses and save them.
  1038. if (hinstMapperLib != NULL)
  1039. {
  1040. MapperOnLoad = (MAPPERLOAD)GetProcAddress(hinstMapperLib, "?OnLoad@@YAHPAUtagNESData@@@Z");
  1041. MapperOnRead = (MAPPERREAD)GetProcAddress(hinstMapperLib, "?OnRead@@YAEG@Z");
  1042. MapperOnWrite = (MAPPERWRITE)GetProcAddress(hinstMapperLib, "?OnWrite@@YAHGE@Z");
  1043. }
  1044. // Make a call to the mapper to do anything that may have to 
  1045. // happen during a load. This usually involves setting up the
  1046. // pointers to PRG-ROM and CHR-ROM. Also use this as a time to
  1047. // pass the pointers to our variables that are needed by the
  1048. // memory mapper.
  1049. NESDATA NesData;
  1050. NesData.pCPU = &CPU;
  1051. NesData.pPPU = &PPU;
  1052. NesData.pabyPRGROM = abyPRGROM;
  1053. NesData.wNumPRGROMPages = wNumPRGROMPages;
  1054. NesData.pabyCHRROM = abyCHRROM;
  1055. NesData.wNumCHRROMPages = wNumCHRROMPages;
  1056. MapperOnLoad(&NesData);
  1057. // Reset the NES.
  1058. ResetNES();
  1059. // Now if debug is set in the options then create the window 
  1060. // to hold our disassembly and register contents disassemble
  1061. // the rom starting at the current PC position.
  1062. CreateDebugWindow(hwnd);
  1063. // Dissassemble the beginning of the NES Rom.
  1064. DissassembleROM();
  1065. // Update the debugging information.
  1066. UpdateDebugInfo();
  1067. return S_OK;
  1068. } // end LoadNESFile()
  1069. //------------------------------------------------------------------------------
  1070. // Name: LoadROMToMem()
  1071. // Desc: Loads the ROM into memory for use. The .NES file is 
  1072. //       structure as follow (copied from Yoshi's nes doc.
  1073. //
  1074. //    +--------+------+------------------------------------------+
  1075. //    | Offset | Size | Content(s)                               |
  1076. //    +--------+------+------------------------------------------+
  1077. //    |   0    |  3   | 'NES'                                    |
  1078. //    |   3    |  1   | $1A                                      |
  1079. //    |   4    |  1   | 16K PRG-ROM page count                   |
  1080. //    |   5    |  1   | 8K CHR-ROM page count                    |
  1081. //    |   6    |  1   | ROM Control Byte #1                      |
  1082. //    |        |      |   %####vTsM                              |
  1083. //    |        |      |    |  ||||+- 0=Horizontal mirroring      |
  1084. //    |        |      |    |  ||||   1=Vertical mirroring        |
  1085. //    |        |      |    |  |||+-- 1=SRAM enabled              |
  1086. //    |        |      |    |  ||+--- 1=512-byte trainer present  |
  1087. //    |        |      |    |  |+---- 1=Four-screen mirroring     |
  1088. //    |        |      |    |  |                                  |
  1089. //    |        |      |    +--+----- Mapper # (lower 4-bits)     |
  1090. //    |   7    |  1   | ROM Control Byte #2                      |
  1091. //    |        |      |   %####0000                              |
  1092. //    |        |      |    |  |                                  |
  1093. //    |        |      |    +--+----- Mapper # (upper 4-bits)     |
  1094. //    |  8-15  |  8   | $00                                      |
  1095. //    | 16-..  |      | Actual 16K PRG-ROM pages (in linear      |
  1096. //    |  ...   |      | order). If a trainer exists, it precedes |
  1097. //    |  ...   |      | the first PRG-ROM page.                  |
  1098. //    | ..-EOF |      | CHR-ROM pages (in ascending order).      |
  1099. //    +--------+------+------------------------------------------+
  1100. //
  1101. //------------------------------------------------------------------------------
  1102. HRESULT LoadROMToMem(LPSTR strRomFileName)
  1103. {
  1104. FILE* pRomFile = NULL;  // File pointer for NES Rom.
  1105. // Open the .NES file for reading.
  1106. if ((pRomFile = fopen(strRomFileName, "rb")) == NULL)
  1107. FATAL(hwndMain, "Couldn't open nes file for reading.");
  1108. // Read the rom's header.
  1109. fread(abyRomHeader, 16, 1, pRomFile);
  1110. // For now skip the trainer if it exists.
  1111. if (abyRomHeader[6] & 0x04)
  1112. fseek(pRomFile, 512, SEEK_CUR);
  1113. // Allocate our memory for PRG-ROM and load the pages into memory.
  1114. abyPRGROM = new BYTE[abyRomHeader[4]*NES_PRGROM_PAGESIZE];
  1115. fread(abyPRGROM, NES_PRGROM_PAGESIZE, abyRomHeader[4], pRomFile);
  1116. // Allocate our memory for CHR-ROM and load the pages into memory.
  1117. abyCHRROM = new BYTE[abyRomHeader[5]*(NES_CHRROM_PAGESIZE*2)];
  1118. fread(abyCHRROM, (NES_CHRROM_PAGESIZE*2), abyRomHeader[5], pRomFile);
  1119. // Close the file.
  1120. fclose(pRomFile);
  1121. return S_OK;
  1122. } // end LoadROMToMem()
  1123. //------------------------------------------------------------------------------
  1124. // Name: MemoryDumpToString()
  1125. // Desc: Writes the memory dump of the passed variable in bytes. It places
  1126. //       the memory text in the string passed to the function.
  1127. //------------------------------------------------------------------------------
  1128. HRESULT MemoryDumpToString(char** pstrMemory, BYTE* pMemory, UINT uStartByte, 
  1129.    UINT uEndByte)
  1130. {
  1131. // Start at the beginning byte passed to this function and begin
  1132. // disassembling. Every 16 bytes put the address at the left corner.
  1133. for (UINT i = 0; i < uEndByte - uStartByte; i++)
  1134. {
  1135. // Display the current address.
  1136. if ((i % 16) == 0)
  1137. {
  1138. if (i != 0)
  1139. {
  1140. sprintf(*pstrMemory, "%c%c%c", 13, 13, 10);
  1141. *pstrMemory += 3;
  1142. }
  1143. sprintf(*pstrMemory, "%04X: ", i + uStartByte);
  1144. *pstrMemory += 6;
  1145. }
  1146. // Print the memory byte and move on in the string.
  1147. sprintf(*pstrMemory, "%02X ", pMemory[i+uStartByte]);
  1148. *pstrMemory += 3;
  1149. }
  1150. return S_OK;
  1151. } // MemoryDumpToString()
  1152. //------------------------------------------------------------------------------
  1153. // Name: PromptForFileOpen()
  1154. // Desc: Prompts the user with the open file common dialog and 
  1155. //       returns the file name and path of the file the user selected.
  1156. //       If the user presses cancel the this function returns FALSE.
  1157. //------------------------------------------------------------------------------
  1158. HRESULT PromptForFileOpen(HWND hwndOwner)
  1159. {
  1160. OPENFILENAME ofn;
  1161. // Initialize OPENFILENAME
  1162. ZeroMemory(&ofn, sizeof(OPENFILENAME));
  1163. ofn.lStructSize = sizeof(OPENFILENAME);
  1164. ofn.hwndOwner = hwndOwner;
  1165. ofn.hInstance = g_hInstance;
  1166. ofn.lpstrFile = (LPSTR)strFileName;
  1167. ofn.nMaxFile = 512;
  1168. ofn.lpstrFilter = "NES Files (*.nes)*.nes;";
  1169. ofn.nFilterIndex = 1;
  1170. ofn.lpstrFileTitle = NULL;
  1171. ofn.nMaxFileTitle = 0;
  1172. ofn.lpstrInitialDir = NULL;
  1173. ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER;
  1174. // Prompt the user to select the file.
  1175. if (GetOpenFileName(&ofn) == FALSE)
  1176. return FALSE;
  1177. // Save the filename string for the user.
  1178. strcpy((LPSTR)strFileName, ofn.lpstrFile);
  1179. return TRUE;
  1180. } // end PromptForFileOpen()
  1181. //------------------------------------------------------------------------------
  1182. // Name: ResetNES()
  1183. // Desc: Pretty self explainitory, resets the NES.
  1184. //------------------------------------------------------------------------------
  1185. HRESULT ResetNES()
  1186. {
  1187. // Initialize the registers to their values.
  1188. CPU.A = 0;
  1189. CPU.X = 0;
  1190. CPU.Y = 0;
  1191. CPU.S = 0;
  1192. CPU.F = 0;
  1193. CPU.P = MAKEWORD(GetMemoryByte(0xFFFC), GetMemoryByte(0xFFFD));
  1194. // Zero all the memory.
  1195. ZeroMemory(CPU.Memory, 0x8000);
  1196. // Reset the number of cpu cycles until the next scanline.
  1197. CPU.byCycles = NUM_CYCLES_PER_SCANLINE;
  1198. // Now display the updated information.
  1199. UpdateDebugInfo();
  1200. return S_OK;
  1201. } // end ResetNES()
  1202. //------------------------------------------------------------------------------
  1203. // Name: ResizeDebugControls()
  1204. // Desc: Resizes the code dissassembly list box as well as the 
  1205. //       registers list box to fit the child window.
  1206. //------------------------------------------------------------------------------
  1207. HRESULT ResizeDebugControls(HWND hwndChild, HWND hwndCodeLB, HWND hwndRegLB)
  1208. {
  1209. RECT rcChild;    // RECT for the window the controls are in.
  1210. // The the client area of the window these controls are to occupy
  1211. GetClientRect(hwndChild, &rcChild);
  1212. // Compute the coordinates and dimensions that the
  1213. // list boxes will ocupy in the child window. We 
  1214. // want them to fill the whole window.
  1215. int nWidth = rcChild.right - rcChild.left;
  1216. int nHeight = rcChild.bottom - rcChild.top;
  1217. // Now move and resize the listboxes.
  1218. MoveWindow(hwndCodeLB, 0, 0, nWidth-REGISTER_LISTBOX_WIDTH, nHeight, TRUE);
  1219. MoveWindow(hwndRegLB, nWidth-REGISTER_LISTBOX_WIDTH, 0, 
  1220. REGISTER_LISTBOX_WIDTH, nHeight, TRUE);
  1221. return S_OK;
  1222. } // end ResizeDebugControls()
  1223. //------------------------------------------------------------------------------
  1224. // Name: CPU_DoCyclicTasks()
  1225. // Desc: Does all the tasks that must be done at the end of a scanline.
  1226. //------------------------------------------------------------------------------
  1227. HRESULT CPU_DoCyclicTasks()
  1228. {
  1229. // If the cpu cycles are less than 0 then we must do
  1230. // everything that must happen at the end of a scanline.
  1231. if ((signed char)CPU.byCycles < 0)
  1232. {
  1233. // Add the number of cycles per scanline to the cycles.
  1234. CPU.byCycles += NUM_CYCLES_PER_SCANLINE;
  1235. // Increment which scanline are on.
  1236. wScanline++;
  1237. // Check for the sprite #0 hit.
  1238. DoSprite0();
  1239. // If we are passed the number of scanlines on the screen then
  1240. // we are in vblank and we must update register $2002 and 
  1241. // execute an NMI interrupt if bit 7 of $2000 is set.
  1242. if (wScanline == NUM_SCANLINES_SCREEN)
  1243. {
  1244. // Set bit 7 of the PPU status register.
  1245. CPU.Memory[0x2002] |= 0x80;
  1246. // Do the NMI if we need to.
  1247. if (CPU.Memory[0x2000] & 0x80)
  1248. {
  1249. // Push the PC onto the stack high byte first.
  1250. PUSH_BYTE((BYTE)(CPU.P >> 8));
  1251. PUSH_BYTE((BYTE)(CPU.P & 0xFF));
  1252. // Push the flags register onto the stack.
  1253. PUSH_BYTE(CPU.F);
  1254. // Set the interrupt flag.
  1255. CPU.F |= 0x04;
  1256. // Set the PC equal to the address specified in the 
  1257. // vector table for the NMI interrupt.
  1258. CPU.P = MAKEWORD(GetMemoryByte(0xFFFA), GetMemoryByte(0xFFFB));
  1259. // Subtract the interrupt latency from the CPU cycles.
  1260. CPU.byCycles -= 7;
  1261. // Break on the NMI interrupt.
  1262. bBreak = TRUE;
  1263. }
  1264. }
  1265. // If we are done with Vblank then we must update register
  1266. // $2002 and reset the scanline counter.
  1267. if (wScanline == NUM_SCANLINES_VBLANK+NUM_SCANLINES_SCREEN)
  1268. {
  1269. // Clear bit 7 and 6 of the PPU status register.
  1270. CPU.Memory[0x2002] &= 0x3F;
  1271. // Reset the scanline counter.
  1272. wScanline = 0;
  1273. }
  1274. }
  1275. return S_OK;
  1276. } // end CPU_DoCyclicTasks()
  1277. //------------------------------------------------------------------------------
  1278. // Name: CPU_Run()
  1279. // Desc: Runs the NES in run mode.
  1280. //------------------------------------------------------------------------------
  1281. DWORD WINAPI CPU_Run(LPVOID lpParam)
  1282. {
  1283. DWORD dwBeginTime = 0; // The begining time of our frame.
  1284. DWORD dwEndTime   = 0; // The ending time of our frame.
  1285. DWORD dwTotalTime = 0; // The total time of our frame.
  1286. DWORD dwFrames    = 0; // The number of frames drawn in a second.
  1287. MSG   msg;             // Message structure.
  1288. DWORD dwSoundFrameSkip = OPTIONS_NUM_AUDIOFRAMESKIP;
  1289. DWORD dwGfxFrameSkip = OPTIONS_NUM_GFXFRAMESKIP;
  1290. char strFrameCount[128] = {0};
  1291. // Create the child window for running the program.
  1292. hwndRunWnd = CreateWindowEx(WS_EX_MDICHILD,
  1293.     strRunClassName,
  1294. strRunTitleName,
  1295. WS_CHILD,
  1296. CW_USEDEFAULT,
  1297. CW_USEDEFAULT,
  1298. CW_USEDEFAULT,
  1299. CW_USEDEFAULT,
  1300. hwndMDIClient,
  1301. NULL,
  1302. g_hInstance,
  1303. NULL);
  1304. if (hwndRunWnd == NULL)
  1305. FATAL(hwndMDIClient, "Couldn't Create the run window");
  1306. // Create DirectDraw for the window.
  1307. CreateDirectDraw(hwndRunWnd);
  1308. // Create and initialize DirectSound.
  1309. CreateSound(hwndRunWnd);
  1310. // Keep running the program until the user has chosen to stop.
  1311. while (bRunning == TRUE)
  1312. {
  1313. // If there is a message for this window, then we need
  1314. // to process it. Otherwise, we are free to run then 
  1315. // NES ROM until the user has chosen to stop. (bRunning == FALSE)
  1316. if (PeekMessage(&msg, hwndRunWnd, 0, 0, PM_REMOVE))
  1317. {
  1318. DispatchMessage(&msg);
  1319. TranslateMessage(&msg);
  1320. }
  1321. else
  1322. {
  1323. // If we are back at the beginning of the screen
  1324. // then clear the screen to the background color.
  1325. if (wScanline == 0)
  1326. {
  1327. // Start our FPS counter.
  1328. dwBeginTime = GetTickCount();
  1329. // Initialize the surface to do our drawing.
  1330. if (!dwGfxFrameSkip)
  1331. {
  1332. BeginDrawing();
  1333. }
  1334. // Draw all the sprites behind the background.
  1335. if (!dwGfxFrameSkip)
  1336. {
  1337. if (Options_bSpritesEnabled)
  1338. DrawSprites(SPRITE_BEHIND);
  1339. }
  1340. }
  1341. // Single step the cpu.
  1342. RunCPU(RUNCPU_RUN);
  1343. // Do all the cyclic tasks.
  1344. CPU_DoCyclicTasks();
  1345. // Draw the scanline if its one of the scanlines on the screen.
  1346. // i.e Don't draw the scanlines in VBlank.
  1347. if (!dwGfxFrameSkip)
  1348. {
  1349. if ((wScanline < 240) && (CPU.Memory[0x2001] & 0x08) && Options_bBackgroundEnabled)
  1350. DrawScanline();
  1351. }
  1352. // Flip the surfaces if we are done drawing the frame.
  1353. if (wScanline == NUM_SCANLINES_SCREEN)
  1354. {
  1355. // Draw all the sprites in front of the background.
  1356. if (!dwGfxFrameSkip)
  1357. {
  1358. if (Options_bSpritesEnabled)
  1359. DrawSprites(SPRITE_INFRONT);
  1360. }
  1361. // Output the number of frames per second.
  1362. OutputText(strFrameCount, 0, 0, RGB(255, 255, 255), 0);
  1363. // Now that everything has been drawn.
  1364. if (!dwGfxFrameSkip)
  1365. {
  1366. EndDrawing();
  1367. }
  1368. // Update our sound for the frame.
  1369. if (!dwSoundFrameSkip)
  1370. APU_DoFrame();
  1371. if ((dwSoundFrameSkip--) == 0)
  1372. dwSoundFrameSkip = OPTIONS_NUM_AUDIOFRAMESKIP;
  1373. if ((dwGfxFrameSkip--) == 0)
  1374. dwGfxFrameSkip = OPTIONS_NUM_GFXFRAMESKIP;
  1375. }
  1376. else if (wScanline == (NUM_SCANLINES_VBLANK+NUM_SCANLINES_SCREEN-1))
  1377. {
  1378. // Wait so we only go 60 FPS.
  1379. //DWORD dwTemp = GetTickCount();
  1380. //if ((dwTemp - dwBeginTime) < 16)
  1381. // Wait(16 - (dwTemp - dwBeginTime));
  1382. // Calculate the FPS.
  1383. dwFrames++;
  1384. dwEndTime = GetTickCount();
  1385. dwTotalTime += dwEndTime - dwBeginTime;
  1386. // Display the FPS every 1 second.
  1387. if (dwTotalTime >= 1000)
  1388. {
  1389. sprintf(strFrameCount, "Fps = %d", dwFrames);
  1390. dwFrames = 0;
  1391. dwTotalTime = 0;
  1392. }
  1393. }
  1394. }
  1395. }
  1396. // Clean up DirectDraw, DirectSound and destroy the window.
  1397. DestroyDirectDraw();
  1398. DestroySound();
  1399. DestroyWindow(hwndRunWnd);
  1400. return 0;
  1401. } // end CPU_Run()
  1402. //------------------------------------------------------------------------------
  1403. // Name: CPU_RunUntilBreak()
  1404. // Desc: Runs the NES in debug mode until a breakpoint is encountered or
  1405. //       the user selects the break command.
  1406. //------------------------------------------------------------------------------
  1407. DWORD WINAPI CPU_RunUntilBreak(LPVOID lpParam)
  1408. {
  1409. // As long as the user hasn't pressed break or we havn't
  1410. // encountered a breakpoint, keep executing.
  1411. while (bBreak == FALSE)
  1412. {
  1413. // Single step the cpu.
  1414. RunCPU(RUNCPU_STEP);
  1415. // Do all the cyclic tasks.
  1416. CPU_DoCyclicTasks();
  1417. // Check for breakpoints and tell the loop to break
  1418. // if the PC is at a breakpoint.
  1419. for (int i = 0; i < OPTIONS_NUM_BREAKPOINTS; i++)
  1420. {
  1421. if (CPU.P == awBreakpoints[i])
  1422. {
  1423. bBreak = TRUE;
  1424. break;
  1425. }
  1426. }
  1427. }
  1428. // Update all the debugging information and reset the break command.
  1429. UpdateDebugInfo();
  1430. bBreak = FALSE;
  1431. return 0;
  1432. } // end CPU_RunUntilBreak()
  1433. //------------------------------------------------------------------------------
  1434. // Name: CPU_Step()
  1435. // Desc: Single steps through instructions in debug mode.
  1436. //------------------------------------------------------------------------------
  1437. HRESULT CPU_Step()
  1438. {
  1439. // Single step the cpu.
  1440. RunCPU(RUNCPU_STEP);
  1441. // Do all the cyclic tasks.
  1442. CPU_DoCyclicTasks();
  1443. // Update all the debugging information.
  1444. UpdateDebugInfo();
  1445. return S_OK;
  1446. } // end CPU_Step()
  1447. //------------------------------------------------------------------------------
  1448. // Name: SetListViewText()
  1449. // Desc: Sets the text of an item in a list view control.
  1450. //------------------------------------------------------------------------------
  1451. HRESULT SetListViewText(HWND hwndLV, UINT uRow, UINT uCol, LPSTR strText)
  1452. {
  1453. LV_ITEM lvI; // List view item structure.
  1454. // Fill out the item structure.
  1455. lvI.mask = LVIF_TEXT;
  1456. lvI.iItem = uRow;
  1457. lvI.iSubItem = uCol;
  1458. lvI.pszText = strText;
  1459. lvI.state = 0;
  1460. lvI.stateMask = 0;
  1461.     
  1462. // Set the text of the item
  1463. if (ListView_SetItem(hwndLV, &lvI) == FALSE)
  1464. return ERROR;
  1465. return S_OK;
  1466. } // end SetListViewText()
  1467. //------------------------------------------------------------------------------
  1468. // Name: ToggleBreakpoint()
  1469. // Desc: Turns a breakpoint on or off.
  1470. //------------------------------------------------------------------------------
  1471. HRESULT ToggleBreakpoint(UINT uItem)
  1472. {
  1473. char strPC[5];      // Text to hold the PC address of the item.
  1474. char strAddress[5]; // Text to hold the address of the breakpoint.
  1475. // Get the address that the user has selected to add a breakpoint.
  1476. ListView_GetItemText(hwndCodeLV, uItem, 0, strPC, 5);
  1477. // Loop through and see if a breakpoint is already added on this line.
  1478. for (int i = 0; i < OPTIONS_NUM_BREAKPOINTS; i++)
  1479. {
  1480. // Convert the breakpoint address to a string.
  1481. sprintf(strAddress, "%04X", awBreakpoints[i]);
  1482. // If the breakpoint is already on this line, then we
  1483. // need to disable it.
  1484. if (strcmp(strAddress, strPC) == 0)
  1485. return DisableBreakpoint(i, uItem);
  1486. }
  1487. // If the breakpoint does not exist on this line, 
  1488. // then we must add a breakpoint.
  1489. return AddBreakpoint((WORD)strtoul(strPC, NULL, 16), uItem);
  1490. } // end ToggleBreakpoint()
  1491. //------------------------------------------------------------------------------
  1492. // Name: UpdateInstrCursorPos()
  1493. // Desc: Places the selection marker of the code list view on the
  1494. //       current instruction.
  1495. //------------------------------------------------------------------------------
  1496. HRESULT UpdateInstrCursorPos()
  1497. {
  1498. LVFINDINFO lvfi;      // List view find item info.
  1499. DWORD      dwItem;    // Current item to be selected.
  1500. char       strPC[10]; // String of the PC's value.
  1501. // Convert the PC's value to a hexadecimal string
  1502. sprintf(strPC, "%04X:", CPU.P);
  1503. // Initialize the structure to find our PC string.
  1504. lvfi.flags = LVFI_STRING;
  1505. lvfi.psz   = strPC;
  1506. // Find the item in the list view.
  1507. dwItem = ListView_FindItem(hwndCodeLV, -1, &lvfi);
  1508. // If the item is not found then we need to dissassemble the rom
  1509. // again because the program counter is out of range, then select
  1510. // the first instruction.
  1511. if (dwItem == -1)
  1512. {
  1513. DissassembleROM();
  1514. dwItem = 0;
  1515. }
  1516. // Select the item.
  1517. SetFocus(hwndCodeLV);
  1518. ListView_SetItemState(hwndCodeLV, dwItem, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
  1519. return S_OK;
  1520. } // end UpdateInstrCursorPos()
  1521. //------------------------------------------------------------------------------
  1522. // Name: UpdateDebugInfo()
  1523. // Desc: Updates all the debugging information in the Registers List View.
  1524. //------------------------------------------------------------------------------
  1525. HRESULT UpdateDebugInfo()
  1526. {
  1527. // Update all the registers.
  1528. UpdateRegisters();
  1529. // Select the current instruction line.
  1530. UpdateInstrCursorPos();
  1531. return S_OK;
  1532. } // end UpdateDebugInfo()
  1533. //------------------------------------------------------------------------------
  1534. // Name: UpdateRegisters()
  1535. // Desc: Updates all the register values in the register list view.
  1536. //------------------------------------------------------------------------------
  1537. HRESULT UpdateRegisters()
  1538. {
  1539. char strText[128]; // Text to hold the info to be added.
  1540. // Update the A register.
  1541. UPDATE_VAR_IN_REG_LISTVIEW(0, "A", CPU.A);
  1542. // Update the X register.
  1543. UPDATE_VAR_IN_REG_LISTVIEW(1, "X", CPU.X);
  1544. // Update the Y register.
  1545. UPDATE_VAR_IN_REG_LISTVIEW(2, "Y", CPU.Y);
  1546. // Update the SP register.
  1547. UPDATE_VAR_IN_REG_LISTVIEW(3, "SP", CPU.S);
  1548. // Update the PC register.
  1549. UPDATE_VAR_IN_REG_LISTVIEW(4, "PC", CPU.P);
  1550. // Update the flags register.
  1551. sprintf(strText, "Flags");
  1552. SetListViewText(hwndRegsLV, 5, 0, strText);
  1553. sprintf(&strText[0], "%c", (CPU.F & 0x80) ? 'S' : 's');
  1554. sprintf(&strText[1], "%c", (CPU.F & 0x40) ? 'V' : 'v');
  1555. sprintf(&strText[2], "%c", ' ');
  1556. sprintf(&strText[3], "%c", (CPU.F & 0x10) ? 'B' : 'b');
  1557. sprintf(&strText[4], "%c", (CPU.F & 0x08) ? 'D' : 'd');
  1558. sprintf(&strText[5], "%c", (CPU.F & 0x04) ? 'I' : 'i');
  1559. sprintf(&strText[6], "%c", (CPU.F & 0x02) ? 'Z' : 'z');
  1560. sprintf(&strText[7], "%c", (CPU.F & 0x01) ? 'C' : 'c');
  1561. SetListViewText(hwndRegsLV, 5, 1, strText);
  1562. // Update the Nintendo registers.
  1563. UPDATE_VAR_IN_REG_LISTVIEW(7, "$2000", CPU.Memory[0x2000]);
  1564. UPDATE_VAR_IN_REG_LISTVIEW(8, "$2001", CPU.Memory[0x2001]);
  1565. UPDATE_VAR_IN_REG_LISTVIEW(9, "$2002", CPU.Memory[0x2002]);
  1566. UPDATE_VAR_IN_REG_LISTVIEW(10, "$2003", CPU.Memory[0x2003]);
  1567. UPDATE_VAR_IN_REG_LISTVIEW(11, "$2004", CPU.Memory[0x2004]);
  1568. UPDATE_VAR_IN_REG_LISTVIEW(12, "$2005", CPU.Memory[0x2005]);
  1569. UPDATE_VAR_IN_REG_LISTVIEW(13, "$2006", CPU.Memory[0x2006]);
  1570. UPDATE_VAR_IN_REG_LISTVIEW(14, "$2007", CPU.Memory[0x2007]);
  1571. UPDATE_VAR_IN_REG_LISTVIEW(15, "$4000", CPU.Memory[0x4000]);
  1572. UPDATE_VAR_IN_REG_LISTVIEW(16, "$4001", CPU.Memory[0x4001]);
  1573. UPDATE_VAR_IN_REG_LISTVIEW(17, "$4002", CPU.Memory[0x4002]);
  1574. UPDATE_VAR_IN_REG_LISTVIEW(18, "$4003", CPU.Memory[0x4003]);
  1575. UPDATE_VAR_IN_REG_LISTVIEW(19, "$4004", CPU.Memory[0x4004]);
  1576. UPDATE_VAR_IN_REG_LISTVIEW(20, "$4005", CPU.Memory[0x4005]);
  1577. UPDATE_VAR_IN_REG_LISTVIEW(21, "$4006", CPU.Memory[0x4006]);
  1578. UPDATE_VAR_IN_REG_LISTVIEW(22, "$4007", CPU.Memory[0x4007]);
  1579. UPDATE_VAR_IN_REG_LISTVIEW(23, "$4008", CPU.Memory[0x4008]);
  1580. UPDATE_VAR_IN_REG_LISTVIEW(24, "$4009", CPU.Memory[0x4009]);
  1581. UPDATE_VAR_IN_REG_LISTVIEW(25, "$4010", CPU.Memory[0x4010]);
  1582. UPDATE_VAR_IN_REG_LISTVIEW(26, "$4011", CPU.Memory[0x4011]);
  1583. UPDATE_VAR_IN_REG_LISTVIEW(27, "$4012", CPU.Memory[0x4012]);
  1584. UPDATE_VAR_IN_REG_LISTVIEW(28, "$4013", CPU.Memory[0x4013]);
  1585. UPDATE_VAR_IN_REG_LISTVIEW(29, "$4014", CPU.Memory[0x4014]);
  1586. UPDATE_VAR_IN_REG_LISTVIEW(30, "$4015", CPU.Memory[0x4015]);
  1587. UPDATE_VAR_IN_REG_LISTVIEW(31, "$4016", CPU.Memory[0x4016]);
  1588. UPDATE_VAR_IN_REG_LISTVIEW(32, "$4017", CPU.Memory[0x4017]);
  1589. // Update the number of cpu cycles.
  1590. sprintf(strText, "Cycles");
  1591. SetListViewText(hwndRegsLV, 34, 0, strText);
  1592. sprintf(strText, "%d", CPU.byCycles);
  1593. SetListViewText(hwndRegsLV, 34, 1, strText);
  1594. // Update the scanline number.
  1595. sprintf(strText, "Scanline");
  1596. SetListViewText(hwndRegsLV, 35, 0, strText);
  1597. sprintf(strText, "%d", wScanline);
  1598. SetListViewText(hwndRegsLV, 35, 1, strText);
  1599. // Update the VRAM Address.
  1600. UPDATE_VAR_IN_REG_LISTVIEW(36, "VRAM Address", wVRAMAddress);
  1601. return S_OK;
  1602. } // end UpdateRegisters()
  1603. //------------------------------------------------------------------------------
  1604. // Name: Wait()
  1605. // Desc: Waits the number of milliseconds passed into the function.
  1606. //------------------------------------------------------------------------------
  1607. __inline VOID Wait(DWORD dwTime)
  1608. {
  1609. DWORD dwBegin = GetTickCount();
  1610. while ((GetTickCount() - dwBegin) <= dwTime);
  1611. } // end Wait()