327 lines
20 KiB
HTML
327 lines
20 KiB
HTML
<html>
|
|
|
|
<head>
|
|
<title>Debugger</title>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
<meta name="generator" content="HelpNDoc Personal Edition 3.8.0.560">
|
|
<link type="text/css" rel="stylesheet" media="all" href="css/reset.css" />
|
|
<link type="text/css" rel="stylesheet" media="all" href="css/base.css" />
|
|
<link type="text/css" rel="stylesheet" media="all" href="css/hnd.css" />
|
|
<!--[if lte IE 8]>
|
|
<link type="text/css" rel="stylesheet" media="all" href="css/ielte8.css" />
|
|
<![endif]-->
|
|
<style type="text/css">
|
|
#topic_header
|
|
{
|
|
background-color: #EFEFEF;
|
|
}
|
|
</style>
|
|
<script type="text/javascript" src="js/jquery.min.js"></script>
|
|
<script type="text/javascript" src="js/hnd.js"></script>
|
|
<script type="text/javascript">
|
|
$(document).ready(function()
|
|
{
|
|
if (top.frames.length == 0)
|
|
{
|
|
var sTopicUrl = top.location.href.substring(top.location.href.lastIndexOf("/") + 1, top.location.href.length);
|
|
top.location.href = "fceux.html?" + sTopicUrl;
|
|
}
|
|
else if (top && top.FrameTOC && top.FrameTOC.SelectTocItem)
|
|
{
|
|
top.FrameTOC.SelectTocItem("Debugger");
|
|
}
|
|
});
|
|
</script>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div id="topic_header">
|
|
<div id="topic_header_content">
|
|
<h1>Debugger</h1>
|
|
|
|
<div id="topic_breadcrumb">
|
|
<a href="Debug.html">Debug</a> ›› </div>
|
|
</div>
|
|
<div id="topic_header_nav">
|
|
<a href="Debug.html"><img src="img/arrow_up.png" alt="Parent"/></a>
|
|
|
|
<a href="Debug.html"><img src="img/arrow_left.png" alt="Previous"/></a>
|
|
|
|
<a href="PPUViewer.html"><img src="img/arrow_right.png" alt="Next"/></a>
|
|
|
|
</div>
|
|
<div class="clear"></div>
|
|
</div>
|
|
<div id="topic_content">
|
|
|
|
<p></p>
|
|
<p><span class="rvts17">Debugger</span></p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><span class="rvts44">Introduction</span></p>
|
|
<p><br/></p>
|
|
<p>The debugger is a tool for inspecting and manipulating machine instructions and their execution. The debugger window has several components:</p>
|
|
<ul style="text-indent: 0px; margin-left: 24px; list-style-position: outside;">
|
|
<li>Execution - a small set of controls for controlling the execution of code.</li>
|
|
<li>CPU State - display of registers, flags, the execution stack, and the PPU state.</li>
|
|
<li>Memory disassembly - displays a disassembly of the bytes currently accessible by the CPU data bus.</li>
|
|
<li>Breakpoints - a list of breakpoints for debugging.</li>
|
|
<li>Bookmarks - a list of addresses for quick navigation.</li>
|
|
<li>Other - buttons for controlling symbolic debugging, rom patching, etc.</li>
|
|
</ul>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><span class="rvts44">Execution and CPU State</span></p>
|
|
<p><br/></p>
|
|
<p>Execution is controlled by a series of buttons at the top-middle of the window. These allow you to break (pause) execution and inspect the current state of the NES.</p>
|
|
<p><br/></p>
|
|
<p>When an NES ROM is opened, it will be normally be running. Most of the debugger window does not update while the game is running. To begin debugging you may click one of the buttons that will break (pause) execution, such as "Step Into", or add a breakpoint.</p>
|
|
<p><br/></p>
|
|
<ul style="text-indent: 0px; margin-left: 24px; list-style-position: outside;">
|
|
<li>Run - runs the program continuously until the next breakpoint is hit. The same effect can be achieved by pressing the Pause hotkey which will unpause emulator when it's paused.</li>
|
|
<li>Step Into - runs one instruction and then breaks.</li>
|
|
<li>Step Out - attempt to run until the current subroutine ends with an RTS; in some cases will behave the same as Run.</li>
|
|
<li>Step Over - runs one instruction, unless it is a JSR instruction, which will run until its RTS.</li>
|
|
<li>Run Line - runs one scanline before breaking.</li>
|
|
<li>128 Lines - runs 128 scanlines before breaking.</li>
|
|
</ul>
|
|
<p><br/></p>
|
|
<p>The Pause hotkey will break execution or resume it. The Frame Advance hotkey will run the emulator for one frame then break.</p>
|
|
<p><br/></p>
|
|
<p>When execution is paused, the disassembly view will begin with the memory at the current program counter location (PC) at the top of the window. You can scroll the disassembly up or down to observe the code. Then you can click "Seek PC" to return to the program counter at any time.</p>
|
|
<p><br/></p>
|
|
<p>You can also use "Seek To" button that will navigate to the specified address. Either type a hexadecimal address to the text field or simply left-click on any address in the Disassembly window.</p>
|
|
<p>When entering the address, these convenient strings may be used instead of the hexadecimal memory address:</p>
|
|
<p><br/></p>
|
|
<p>NES special addresses:</p>
|
|
<ul style="text-indent: 0px; margin-left: 24px; list-style-position: outside;">
|
|
<li>NMI/VBL - non-maskable interrupt vector (at FFFA)</li>
|
|
<li>RST - reset vector (at FFFC)</li>
|
|
<li>IRQ - interrupt vector (at FFFE)</li>
|
|
</ul>
|
|
<p><br/></p>
|
|
<p>FDS special addresses:</p>
|
|
<ul style="text-indent: 0px; margin-left: 24px; list-style-position: outside;">
|
|
<li>NMI1 - non-maskable interrupt vector (at DFF6)</li>
|
|
<li>NMI2 - non-maskable interrupt vector (at DFF8)</li>
|
|
<li>NMI3 - non-maskable interrupt vector (at DFFA)</li>
|
|
<li>RST - reset vector (at DFFC)</li>
|
|
<li>IRQ - interrupt vector (at DFFE)</li>
|
|
</ul>
|
|
<p><br/></p>
|
|
<p>NSF special addresses:</p>
|
|
<ul style="text-indent: 0px; margin-left: 24px; list-style-position: outside;">
|
|
<li>LOAD - NSF LOAD address</li>
|
|
<li>INIT - NSF INIT address</li>
|
|
<li>PLAY - NSF PLAY address</li>
|
|
</ul>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p>While execution is broken, the program counter (PC) can be edited, as well as the three registers A/X/Y, and the status flags. Normally they should be left as-is, but changing them at runtime can be useful for more advanced debugging.</p>
|
|
<p><br/></p>
|
|
<p>The contents of memory starting at the stack pointer (somewhere in the range $0100-01FF) is displayed in the Stack frame below the A/X/Y registers.</p>
|
|
<p><br/></p>
|
|
<p>The current PPU memory address, sprite memory address, scanline, and rendering pixel are displayed below the stack and status flags.</p>
|
|
<p><br/></p>
|
|
<p><span class="rvts15">Examples of Scanline number:</span> -1 it means Prerender time, 240 is Idle scanline, 0-239 are visible scanlines, 241-260/310 are VBlank scanlines.</p>
|
|
<p><br/></p>
|
|
<p>To the right from PPU section there's Cycles counter and Instructions counter that keep counting while the game is running. You can use the information for keeping statistics, for code profiling or writing PPU-synchronized code (e.g. raster effects). You can also make the debugger break automatically based on the counters values. The "Reset counters" button resets both counters to 0.</p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><span class="rvts44">Disassembly</span></p>
|
|
<p><br/></p>
|
|
<p>This large frame takes up the left side of the debugger window. It displays the current contents of memory accessible by the CPU with an automatic disassembly of that data into assembly instructions. The following memory ranges may contain useful data for inspection:</p>
|
|
<p><br/></p>
|
|
<ul style="text-indent: 0px; margin-left: 24px; list-style-position: outside;">
|
|
<li>0000-00FF - zero page (RAM)</li>
|
|
<li>0100-01FF - stack (RAM)</li>
|
|
<li>0200-07FF - RAM</li>
|
|
<li>4018-FFFF - mapper controlled (ROM or RAM, may be bankswitched)</li>
|
|
</ul>
|
|
<p><br/></p>
|
|
<p>Memory contents are displayed in this form:</p>
|
|
<p><br/></p>
|
|
<p><span class="rvts45">0F:C0A8:24 1F BIT $001F = #$80</span></p>
|
|
<p><span class="rvts45">bb:mmmm:dd dd dd iiiiiiiiiiiii...</span></p>
|
|
<p><br/></p>
|
|
<ul style="text-indent: 0px; margin-left: 24px; list-style-position: outside;">
|
|
<li>bb - 16k iNES bank, designates which 16k bank from the iNES file is mapped here. Note that the number may be not the same as mapper bankswitching current bank.</li>
|
|
<li>mmmm - physical address on the NES CPU data bus.</li>
|
|
<li>dd - data bytes belonging to the instruction beginning at this address.</li>
|
|
<li>iiii - assembly description of the instruction.</li>
|
|
</ul>
|
|
<p><br/></p>
|
|
<p>When debugging an NSF program, the bank designation will be a 4k NSF bank instead of the 16k iNES bank.</p>
|
|
<p><br/></p>
|
|
<p>A single instruction may be one to three bytes, and will all appear on the line before the assembly code description of that instruction. An instruction with "= #$xx" at the end indicates the value currently in memory at the address referenced by the instruction.</p>
|
|
<p><br/></p>
|
|
<p>Hovering the mouse over the disassembly will display at the bottom of the window more detailed information about the location of this code in the iNES file.</p>
|
|
<p><br/></p>
|
|
<p>There is an empty column to the left of the memory view. Left clicking on this column will open the Inline Assembler, which allows you to patch the ROM at runtime. Right clicking on this column will open the Hex Editor, which allows you to directly edit the ROM. Middle-clicking on this column will bring up the <a class="rvts18" href="GameGenieEncoderDecoder.html">Game Genie Encoder</a> at that address, so you can easily make Game Genie codes.</p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><span class="rvts44">Breakpoints</span></p>
|
|
<p><br/></p>
|
|
<p>Breakpoints will break execution when chosen conditions are met. To create a breakpoint, click the Add button in the BreakPoints frame in the upper right corner of the debugger.</p>
|
|
<p><br/></p>
|
|
<p>Each breakpoint has an address range to watch (use only the left address field if you wish to watch a single byte address). When entering the address of a breakpoint, you can also use convenient strings (such as IRQ) instead of hexadecimal memory addresses.</p>
|
|
<p><br/></p>
|
|
<p>Check one or more of the options to watch for Read, Write, or Execute at an address. Note that fetching of code from an address will not break as a Read; use the Execute box for this. Breakpoints can be given a name that will appear in the breakpoint window. The condition field can be used to break only on particular conditions; see "Conditional Breakpoints" below.</p>
|
|
<p><br/></p>
|
|
<p>Double click on a breakpoint in the BreakPoints list to quickly disable or enable it. So you don't have to delete breakpoints to stop them from causing the debugger to halt the game.</p>
|
|
<p><br/></p>
|
|
<p>A special kind of breakpoints with the Forbid option will prevent any breakpoints from occurring within the specified memory address range. This can be enabled and disabled like other breakpoints.</p>
|
|
<p><br/></p>
|
|
<p>A quicker way to add PC breakpoints is to double click on any address in the Disassembly when you want to set the breakpoint to that address. <span class="rvts15">Example:</span> when you need to quickly advance emulation to a given line of code, double-click on the address part of the line, and the "Add Execute breakpoint here" dialog will appear, click "OK" and then hit "Run", Debugger will break at this line of code.</p>
|
|
<p><br/></p>
|
|
<p>There is also an option to "Break on Bad Opcode" which will halt execution if a bad instruction opcode is reached.</p>
|
|
<p><br/></p>
|
|
<p>Finally, you can make the debugger break after certain number of instructions or CPU cycles.</p>
|
|
<p><br/></p>
|
|
<p>More advanced conditions and automation may be achieved through Lua script breakpoints. See the <a class="rvts18" href="LuaFunctionsList.html#LuaBreakpoints">Lua reference</a> for more information.</p>
|
|
<p><br/></p>
|
|
<p>Breakpoints are listed in the following form:</p>
|
|
<p><br/></p>
|
|
<p><span class="rvts45">aaaa: EmRWXF : nnnn</span></p>
|
|
<p><span class="rvts45">aaaa-aaaa: EmRWXF : nnnn</span></p>
|
|
<p><br/></p>
|
|
<ul style="text-indent: 0px; margin-left: 24px; list-style-position: outside;">
|
|
<li>aaaa - address of breakpoint</li>
|
|
<li>E - enabled</li>
|
|
<li>m - memory area: C = CPU, P = PPU, S = sprite</li>
|
|
<li>R - read</li>
|
|
<li>W - write</li>
|
|
<li>X - execute</li>
|
|
<li>F - Forbid</li>
|
|
<li>nnnn - name of breakpoint</li>
|
|
</ul>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><span class="rvts44">Conditional Breakpoints</span></p>
|
|
<p><br/></p>
|
|
<p>Breakpoints may also have a conditional statement that causes them to execute only if that statement evaluates to true. The conditional breakpoint grammar has this form:</p>
|
|
<p><br/></p>
|
|
<ul style="text-indent: 0px; margin-left: 24px; list-style-position: outside;">
|
|
<li>P -> Connect</li>
|
|
<li>Connect -> Compare { ('||' | '&&') Compare }</li>
|
|
<li>Compare -> Sum { ('==' | '!=' | '<=' | '>=' | '<' | '>') Sum }</li>
|
|
<li>Sum -> Product { ('+' | '-') Product }</li>
|
|
<li>Product -> Primitive { ('*' | '/') Primitive }</li>
|
|
<li>Primitive -> Number | Address | Register | Flag | PC Bank | '(' Connect ')'</li>
|
|
<li>Number -> '#' [1-9A-F]*</li>
|
|
<li>Address -> '$' [1-9A-F]* | '$' '[' Connect ']'</li>
|
|
<li>Register -> 'A' | 'X' | 'Y' | 'P'</li>
|
|
<li>Flag -> 'N' | 'C' | 'Z' | 'I' | 'B' | 'V'</li>
|
|
<li>PC Bank -> 'K'</li>
|
|
</ul>
|
|
<p><br/></p>
|
|
<p>The parser is very strict. All numbers are hexadecimal. Always prefix a number with # for an immediate value, or $ for a memory address. If a memory address needs to be calculated use $[] with the calculation inside the brackets.</p>
|
|
<p><br/></p>
|
|
<p>Registers A/X/Y are 8-bit unsigned values. Register P is the 16-bit program counter.</p>
|
|
<p><br/></p>
|
|
<p>Flags evaluate to 1 if set, 0 if clear.</p>
|
|
<p><br/></p>
|
|
<p>Connecting operators || or && combine boolean terms. Parentheses dictate order of operations.</p>
|
|
<p><br/></p>
|
|
<p><span class="rvts15">Example conditions:</span></p>
|
|
<p><br/></p>
|
|
<p>Break if register A is less than value at memory address $0005:</p>
|
|
<p><span class="rvts45">A < $0005</span></p>
|
|
<p><br/></p>
|
|
<p>Break if the program counter is 8123:</p>
|
|
<p><span class="rvts45">P == #8123</span></p>
|
|
<p><br/></p>
|
|
<p>Break if the value at the indirect address on zeropage $10 is not equal to FF:</p>
|
|
<p><span class="rvts45">#FF != $[$10+($11*#100)]</span></p>
|
|
<p><br/></p>
|
|
<p>Break if flag N is clear or A is not equal to 00:</p>
|
|
<p><span class="rvts45">(N==#0 || A!=#0)</span></p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><span class="rvts44">Bookmarks</span></p>
|
|
<p><br/></p>
|
|
<p>A list of bookmark addresses can be kept in the Address Bookmarks frame to make memory navigation easier. Simply type a hexadecimal address (or a convenient string, such as "NMI") and click "Add" to add it to your bookmarks. Alternatively, just click on any address in the Disassembly window, and the address will appear in the Bookmark Add field, so you don't have to type it.</p>
|
|
<p>Next time you wish to go to this address just double click on the bookmark.</p>
|
|
<p>You can also name bookmarks.</p>
|
|
<p>When you exit the emulator, bookmarks are saved in a .deb file. Next time you return to debugging the list of bookmarks will be automatically loaded from the file.</p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><span class="rvts44">Inline Assembler</span></p>
|
|
<p><br/></p>
|
|
<p>Open the inline assembler by left-clicking in the empty column to the left of the memory view.</p>
|
|
<p><br/></p>
|
|
<p>The starting memory address is displayed in the PC field at the top of the inline assembler window. Type a line of assembly to add in the empty field just below this, and hit enter. The assembled code of your patch will appear below as you enter each line.</p>
|
|
<p><br/></p>
|
|
<p>Click Apply to apply your patch to the ROM in memory. Click Undo to remove the last assembled line. After applying a patch, clicking Save will allow you to save this patch directly to the ROM file.</p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><span class="rvts44">Symbolic Debugging</span></p>
|
|
<p><br/></p>
|
|
<p>This feature makes it possible to rename addresses in the disassembly window (e.g. $C022) to easily understandable names (e.g. AddHealthpoints). You can also add comments to lines in the disassembly window. You can enable symbolic debugging by clicking the checkbox "Symbolic debug".</p>
|
|
<p><br/></p>
|
|
<p>To use this feature, create "name list files" in any text editor like Notepad (Filename should be like this: *.(bank).nl / *.ram.nl, <span class="rvts15">Example:</span> NES Test Cart (PD).nes.0.nl, NES Test Cart (PD).nes.ram.nl) which contain names and comments you wish to display in the disassembly window. These files are simple ASCII text files.</p>
|
|
<p><br/></p>
|
|
<p><span class="rvts15">Example of contents of a NL file:</span></p>
|
|
<p><br/></p>
|
|
<p>$C000#NewName1#Comment1</p>
|
|
<p>$C002##Comment2</p>
|
|
<p>$C004#NewName2#</p>
|
|
<p>$C006#NewName3#MultilineComment-Part1</p>
|
|
<p>\MultilineComment-Part2</p>
|
|
<p>\MultilineComment-Part3</p>
|
|
<p>$C008/10#NewName4#</p>
|
|
<p><br/></p>
|
|
<p>Every line contains two # characters which separate the three parts of one line: The first part (starting with a $ character) is the address to be renamed. Optionally you can add a "/number" part which marks the offsets as a beginning of an array of the given size (the size must be specified in hex form). The second (optional) part is the new name of that address. Whenever the line of that address is shown in the disassembly window an extra line saying "Name: NewName" is shown above it. Instructions referencing this address, for example JSR $C000 are also changed to JSR NewName1 (in that example). The third (optional) part is the comment that's also added above the disassembly line the comment refers to. It works exactly like the additional name line, only the prefix of that line is different. Comment lines start with "Comment: " rather than with "Name: ". Multi-lines comments are possible. Lines starting with a \ character are just appended to the comment of the preceding line. Multi-line comments are also shown in multiple lines in the disassembly window.</p>
|
|
<p><br/></p>
|
|
<p>In the example above, the first line contains all three parts. Using this NL file all references to the address $C000 are replaced with NewName1 and whenever line $C000 is shown in the disassembly window an additional comment is also visible right above the actual disassembled line. The second example line defines only a comment while the third line defines only a name. Following that there's a multi-line comment definition for address $C006. The last line defines an array called NewName4 of size 0x10 (= 16) bytes starting at offset $C008.</p>
|
|
<p><br/></p>
|
|
<p>NL files must follow a specific naming convention to account for bank swapping. Each bank needs its own NL file with a hexadecimal bank number. RAM can also be given its own NL file. For instance, an NES file named "mygame.nes" that has 4 banks would have these NL files:</p>
|
|
<p><br/></p>
|
|
<p>mygame.nes</p>
|
|
<p>mygame.nes.ram.nl</p>
|
|
<p>mygame.nes.0.nl</p>
|
|
<p>mygame.nes.1.nl</p>
|
|
<p>mygame.nes.2.nl</p>
|
|
<p>mygame.nes.3.nl</p>
|
|
<p><br/></p>
|
|
<p>All NL files must be in the same directory as the ROM file itself.</p>
|
|
<p><br/></p>
|
|
<p>In the *.ram.nl file you can name and comment RAM addresses instead of ROM addresses. In this case, you might use a line such as:</p>
|
|
<p><br/></p>
|
|
<p>$00A5#Mic Test OK#00=Not Passed, 01=Passed</p>
|
|
<p><br/></p>
|
|
<p>To reload the NL files of the currently active ROM file press the "Reload Symbols" button.</p>
|
|
<p><br/></p>
|
|
<p>When the "Symbolic debug" option is enabled, both the Debugger and Hex Editor will display symbolic names instead of specified addresses, so the debugging process becomes much easier.</p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><span class="rvts44">Other</span></p>
|
|
<p><br/></p>
|
|
<p>If the ".DEB files" checkbox in the lower right corner of the debugger window is checked, the emulator will automatically save debug settings such as breakpoints and bookmarks in a .deb file alongside the NES ROM, and load these settings next time you open the ROM.</p>
|
|
<p><br/></p>
|
|
<p>There is a "Rom Patcher" button that may be used to apply a small patch to a ROM.</p>
|
|
<p><br/></p>
|
|
<p>The "ROM offsets" option will display ROM offsets instead of CPU addresses in the Disassembly window.</p>
|
|
<p><br/></p>
|
|
<p>The "Restore Original Window Size" button will restore the original size of the debugger window if you resized it manually.</p>
|
|
<p><br/></p>
|
|
<p>The "Auto-open" checkbox causes the debugger window to open automatically whenever an NES ROM is opened.</p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p><br/></p>
|
|
<p></p>
|
|
<p class="rvps2"><span class="rvts13">Created with the Personal Edition of HelpNDoc: </span><a class="rvts14" href="http://www.helpndoc.com/create-epub-ebooks">Full-featured EBook editor</a></p>
|
|
</div>
|
|
|
|
<div id="topic_footer">
|
|
|
|
<div id="topic_footer_content">
|
|
2012</div>
|
|
</div>
|
|
</body>
|
|
|
|
</html>
|
|
|