mirror of https://github.com/stella-emu/stella.git
1678 lines
79 KiB
HTML
1678 lines
79 KiB
HTML
<html>
|
|
<head>
|
|
<title>Stella Debugger</title>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<center><b><font size="7">Stella</font></b></center>
|
|
<center><h4><b>Release 5.1</b></h4></center>
|
|
<center><h1><b>Integrated Debugger</b></h1></center>
|
|
<center><h4><b>(a work in progress)</b></h4></center>
|
|
<br>
|
|
|
|
<h2>Contents</h2>
|
|
<ul>
|
|
<li><a href="#Features">Features</a></li>
|
|
<li><a href="#HowToDebugger">How to use the Debugger</a>
|
|
<ul>
|
|
<li><a href="#Startup">Startup</a></li>
|
|
<li><a href="#GlobalButtons">Global Buttons</a></li>
|
|
<li><a href="#ChangeTracking">Change Tracking</a></li>
|
|
<li><a href="#PromptTab">Prompt Tab</a>
|
|
<ul>
|
|
<li><a href="#TabCompletion">Tab Key Auto-Complete</a></li>
|
|
<li><a href="#Expressions">Expressions</a></li>
|
|
<ul>
|
|
<li><a href="#Prefixes">Prefixes</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#BreakpointsEtc">Breakpoints, watches and traps, oh my!</a>
|
|
<ul>
|
|
<li><a href="#Breakpoints">Breakpoints</a></li>
|
|
<li><a href="#ConditionalBreaks">Conditional Breaks</a></li>
|
|
<li><a href="#Functions">Functions</a></li>
|
|
<li><a href="#BuiltInFunctions">Built-in Functions</a></li>
|
|
<li><a href="#PseudoRegisters">Pseudo-Registers</a></li>
|
|
<li><a href="#Watches">Watches</a></li>
|
|
<li><a href="#Traps">Traps</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#SaveWork">Save your work!</a></li>
|
|
<li><a href="#PromptCommands">Prompt Commands</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#IOTab">I/O Tab</a></li>
|
|
<li><a href="#AudioTab">Audio Tab</a></li>
|
|
<li><a href="#TIADisplay">TIA Display</a></li>
|
|
<li><a href="#TIAInfo">TIA Information</a></li>
|
|
<li><a href="#TIAZoom">TIA Zoom</a></li>
|
|
<li><a href="#BreakpointStatus">Breakpoint/Trap Status</a></li>
|
|
<li><a href="#CPURegisters">CPU Registers</a></li>
|
|
<li><a href="#DataOpButtons">Data Operations Buttons</a></li>
|
|
<li><a href="#M6532">M6532/RIOT RAM</a></li>
|
|
<ul>
|
|
<li><a href="#M6532Search">M6532/RIOT RAM (search/compare mode)</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#Disassembly">ROM Disassembly</a>
|
|
<ul>
|
|
<li><a href="#DisassemblySettings">ROM Disassembly Settings</a></li>
|
|
<li><a href="#Limitations">Limitations</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#BankswitchInformation">Detailed Bankswitch Information</a></li>
|
|
<li><a href="#CartridgeRAMInformation">Detailed Cartridge Extended RAM Information</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#DistellaConfiguration">Distella Configuration Files</a></li>
|
|
<li><a href="#Howtohack">Tutorial: How to hack a ROM</a></li>
|
|
</li>
|
|
</ul>
|
|
<br>
|
|
|
|
<h1><a name="Features">Features</a></h1>
|
|
<p>The debugger in Stella may never be complete, as we're constantly
|
|
adding new features requested by homebrew developers. However, even in its
|
|
current form it's still quite powerful, and is able to boast at least one
|
|
feature that no other 2600 debugger has; it's <b>completely</b> cross-platform.</p>
|
|
|
|
<p>Here's a (non-comprehensive) list of what the debugger can do so far:</p>
|
|
<ul>
|
|
<li>Display registers and memory.</li>
|
|
|
|
<li>Dump state of TIA and RIOT, with things like joystick directions and
|
|
NUSIZx decoded into English (more-or-less).</li>
|
|
|
|
<li>Change registers/memory, including toggles for flags in P register.</li>
|
|
|
|
<li>Single step/trace.</li>
|
|
|
|
<li>Breakpoints - break running program and enter debugger when the
|
|
Program Counter hits a predefined address; you can set as many
|
|
breakpoints as you want.</li>
|
|
|
|
<li>Conditional breakpoints - Break running program when some arbitrary
|
|
condition is true (e.g. "breakif {a == $7f && c}" will break when the Accumulator value is $7f and the Carry flag is true, no matter where
|
|
in the program this happens). Unlike the cond breaks in PCAE, Stella's
|
|
are *fast*: the emulation will run at full speed unless you use lots
|
|
of breakif's at the same time, or have a slow CPU.</li>
|
|
|
|
<li>Watches - View contents of a location/register before every
|
|
debugger prompt.</li>
|
|
|
|
<li>Traps - Like breakpoints, but break on read/write/any access to
|
|
*any* memory location. Traps can also be combined with conditions to
|
|
become conditional traps.</li>
|
|
|
|
<li>Frame advance (automatic breakpoint at beginning of next frame)
|
|
You can advance multiple frames with one command.</li>
|
|
|
|
<li>Rewind previous advance operations and undo rewinds.</li>
|
|
|
|
<li>Supports Distella 'configuration directives', which may be used to
|
|
override automatic code/data determination in the disassembly. For now,
|
|
the following directives are supported: CODE, GFX, PGFX, DATA, ROW.
|
|
These directives can be entered at the debugger prompt, or (automatically)
|
|
loaded and saved in configuration files.</li>
|
|
|
|
<li>Extensive disassembly support, both from the emulation core and with help
|
|
from Distella. Where possible, the disassembly differentiates between code,
|
|
player graphics and playfield graphics (ie, addresses stored in GRPx and PFx)
|
|
and data (addresses used as an operand of a command). Code sections are also
|
|
differentiated between actual code, and 'tentative' code (ie, areas that may
|
|
represent code sections, but haven't actually been executed yet). Such
|
|
tentative code is marked with a '*' symbol.</li>
|
|
|
|
<li>Supports visual representation of the bitmap data of graphics areas,
|
|
as well as the ability to directly edit these areas in either hex or binary.</li>
|
|
|
|
<li>Support for DASM symbol files (created with DASM's -s option),
|
|
including automatically loading symbol files if they're named
|
|
romname.sym</li>
|
|
|
|
<li>Support for DASM list files (created with DASM's -l option),
|
|
including automatically loading list files if they're named
|
|
romname.lst</li>
|
|
|
|
<li>Built-in VCS.H symbols, if no symbol file is loaded.</li>
|
|
|
|
<li>Symbolic names in disassembly.</li>
|
|
|
|
<li>Symbolic names accepted as input.</li>
|
|
|
|
<li>Ability to generate DASM-compatible disassembly files (currently single-bank
|
|
only) with all the features mentioned above.</li>
|
|
|
|
<li>Tab completion for commands, symbol names and functions.</li>
|
|
<li>Graphical editor for RIOT and extended RAM. Acts a lot like a spreadsheet.
|
|
Input in hex, with displays for label/decimal/binary for
|
|
currently-selected location.</li>
|
|
<li>GUI CPU state window.</li>
|
|
<!--Cheat system (similar to MAME) (still needs a way to save/load cheats)-->
|
|
<li>Reset the 6502.</li>
|
|
<li>Start emulator in debugger (via command-line option "-debug").</li>
|
|
<li>Save CLI session to a text file.</li>
|
|
<li>Supports hex, decimal, and binary input and output almost everywhere.
|
|
(disassembly is still hex).</li>
|
|
<li>Support for bank switching. You can see how many banks a cart has and the
|
|
currently selected bank, and manually change banks.</li>
|
|
<li>Registers/memory that get changed by the CPU during debugging are
|
|
highlighted when they're displayed.</li>
|
|
<li>Data sources for the CPU SP/A/X/Y registers, showing the resolved/source
|
|
address of of load operands.</li>
|
|
<li>Scanline advance (like frame advance, break at beginning
|
|
of next scanline).</li>
|
|
<li>TIA display is updated during step/trace, so we can see our
|
|
scanlines being drawn as it happens. This isn't 100% perfect: unlike
|
|
a real TIA, the one in Stella only updates when it's written to.</li>
|
|
<li>Graphical TIA tab, with register names and GUI buttons for
|
|
various bits (e.g. click ENAM0 to turn it on).</li>
|
|
<li>GUI Disassembly window, scrollable, with checkboxes for breakpoints.</li>
|
|
<li>Script (batch) file support, including auto-running a script file
|
|
named after the ROM image.</li>
|
|
<li>Saving the current debugger state to a script file (including
|
|
breakpoints, traps, etc).</li>
|
|
<li>Built-in functions for use with "breakif", to support common conditions
|
|
(such as breaking when the user presses Game Select...)</li>
|
|
<li>Patching ROM in-place.</li>
|
|
<li>Save patched ROM</li>
|
|
</ul>
|
|
|
|
<h3>Future planned features:</h3>
|
|
|
|
<ul>
|
|
<li>GUI for cheat codes (Cheetah and normal codes).</li>
|
|
<li>Perhaps 2 panes in the disassembly window (so you can see 2 parts of the
|
|
code at once).</li>
|
|
<li>Add bookmark support to disassembly window.</li>
|
|
<li>More "special variables" for the expression parser.</li>
|
|
<li>Possibly a mini-assembler</li>
|
|
<li>Possibly support for recording and playing back input files, like
|
|
MAME. This isn't a debugger feature per se, but it'll make it easier
|
|
to reliably trigger a bug so you can debug it.</li>
|
|
<!--
|
|
<li>Graphics ROM view, so you can see your sprite data (it might still
|
|
be upside-down though :)</li> -->
|
|
<li>Various new GUI enhancements</li>
|
|
</ul>
|
|
|
|
</br>
|
|
<h1><a name="HowToDebugger">How to use the Debugger</a></h1>
|
|
|
|
<p>Pressing ` (aka backtick, backquote, grave accent) toggles the debugger on
|
|
& off. When you exit the debugger, the emulation resumes at the current
|
|
program counter, and continues until either a breakpoint/trap is hit,
|
|
or the ` key is pressed again.</p>
|
|
|
|
<p>The main debugger window will look similar to the following (note that the
|
|
letters here are for reference in the document below; they aren't actually
|
|
present in the debugger):</p>
|
|
<p><img src="graphics/debugger_main.png"></p>
|
|
|
|
<p>For space reasons, the Prompt, TIA, I/O and Audio displays are split into
|
|
4 tabs, only one of which is visible at a time. You can use the mouse or
|
|
keyboard to select which tab you want to view. Control/Cmd + Tab cycles between
|
|
tabs from left-to-right, Shift + Control/Cmd + Tab cycles right-to-left.
|
|
Pressing Tab (or Shift + Tab) cycles between widgets in the current tab (except
|
|
for in the Prompt Tab, where 'tab' is used for something else).</p>
|
|
|
|
<p>You can also enter the debugger at emulator startup by use the 'debug'
|
|
command on the command line, or alternatively within the ROM launcher in
|
|
'Power-on options':
|
|
<pre>
|
|
; will enter the debugger before any instructions run
|
|
stella -debug mygame.bin
|
|
|
|
; alternatively, you can use 'break' to accomplish the same thing
|
|
; $fffc is the 6502/6507 init vector. This command will break and enter the
|
|
; debugger before the first 6507 instruction runs, so you can debug the
|
|
; startup code:
|
|
stella -break "*($fffc)" mygame.bin
|
|
</pre>
|
|
</p>
|
|
|
|
<p>Using the ` key will always enter the debugger at the end of
|
|
the frame (for NTSC games usually scanline 262). This is because Stella only checks
|
|
for keystrokes once per frame. Once in the debugger, you can control
|
|
execution by stepping one instruction, scanline, or frame at a time
|
|
(or more than one at a time, using commands in the prompt). You can
|
|
also set breakpoints or traps, which will cause the emulator to enter
|
|
the debugger when they are triggered, even if it happens in mid-frame.</p>
|
|
</br>
|
|
|
|
<h2><a name="Startup">Startup</a></h2>
|
|
At startup, the debugger automatically tries load a number of files which
|
|
will provide additional information for the debugger and can make debugging
|
|
more convenient.
|
|
|
|
<ul>
|
|
<li>
|
|
"autoexec.script"</br>
|
|
Use this plain text file to define e.g. your own functions or settings.
|
|
They will be loaded at startup for each ROM.
|
|
|
|
<p>The location of this file will depend on the OS as follows:</p>
|
|
<p>
|
|
<table cellpadding=4 border="1">
|
|
<tr>
|
|
<td><b>Linux/Unix</b></td>
|
|
<td><i>~/.stella/autoexec.script</i></td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Macintosh</b></td>
|
|
<td><i>~/Library/Application Support/Stella/autoexec.script</i></td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Windows</b></td>
|
|
<td><i>%APPDATA%\Stella\autoexec.script</i>
|
|
<b>or</b><br>
|
|
<i>_BASEDIR_\autoexec.script</i>
|
|
(if a file named 'basedir.txt' exists in the application
|
|
directory containing the full pathname for _BASEDIR_)
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</p>
|
|
</li>
|
|
<li>
|
|
"<rom_filename>.script"</br>
|
|
In this ROM specific, plain text file you can store breaks, traps, watches
|
|
etc. for future re-use. Use the "save" command to create this file using
|
|
the current settings. You can also manually edit the file and add more commands.
|
|
</br></br>
|
|
</li>
|
|
<li>
|
|
"<rom_filename>.cfg"</br>
|
|
This file is described in <a href="#DistellaConfiguration">
|
|
<b>Distella Configuration Files</b></a>.
|
|
</br></br>
|
|
</li>
|
|
<li>
|
|
"<rom_filename>.lst"</br>
|
|
</li>
|
|
<li>
|
|
"<rom_filename>.sym"</br>
|
|
If you provied the -l and -s parameters DASM will create these two files during
|
|
assembly. Stella uses the file content to display the correct labels.
|
|
</li>
|
|
</ul>
|
|
<p>Note that all files are only accessed if you enter
|
|
the debugger at least once during a program run. This means you can create
|
|
these files, and not worry about slowing down emulation unless you're
|
|
actively using the debugger.</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="GlobalButtons">Global Buttons</a></h2>
|
|
|
|
<p>There are some buttons on the right top that always show up no matter which
|
|
tab you are looking at. This is because these are the ones that are most frequently
|
|
used.</p>
|
|
|
|
<ul>
|
|
<p><img src="graphics/debugger_globalbuttons.png"></p>
|
|
</ul>
|
|
|
|
<p>The larger button at the left top (labeled '<') performs the rewind operation,
|
|
which will undo the previous Step/Trace/Scan/Frame... advance, the smaller button at
|
|
the left bottom (labeled '>') performs the unwind operation, which will undo the
|
|
previous rewind operation. The rewind buffer is 100 levels deep by default, the
|
|
size can be configured e.g. in the
|
|
<b><a href="index.html#Debugger">Developer Settings</a> - Time Machine</b> dialog.<p>
|
|
|
|
<p>The other operations are Step, Trace, Scan+1, Frame+1 and Exit (debugger).</p>
|
|
|
|
<p>You can also use the buttons from anywhere in the GUI via hotkeys.</p>
|
|
<p>
|
|
<table BORDER=1 cellpadding=4>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Function</th>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-s</td>
|
|
<td>Step</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-t</td>
|
|
<td>Trace</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-L</td>
|
|
<td>Scan+1</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-f</td>
|
|
<td>Frame+1</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-r</td>
|
|
<td>Rewind 1</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-Shift-r</td>
|
|
<td>Rewind 10</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-Alt-r</td>
|
|
<td>Rewind all</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-y</td>
|
|
<td>Unwind 1</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-Shift-y</td>
|
|
<td>Unwind 10</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Control-Alt-y</td>
|
|
<td>Unwind all</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Backquote (`)</td>
|
|
<td>Exit</td>
|
|
</tr>
|
|
</table>
|
|
</p>
|
|
<p>To the left of the global buttons, you find the "Options..." button.</p>
|
|
<ul>
|
|
<p><img src="graphics/debugger_options.png"></p>
|
|
</ul>
|
|
<p>This opens the <a href="index.html#Options">Options Menu</a> which is described
|
|
in detail in the <a href="index.html">User's Guide</a>.</p>
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="ChangeTracking"></a>Change Tracking</h2>
|
|
|
|
<p>The debugger tracks changes to the CPU, TIA and RIOT registers and RAM by
|
|
displaying changed locations or registers with a red background after each step,
|
|
trace, scanline, or frame advance. This sounds simple, and it is, but
|
|
it's also amazingly useful.</p>
|
|
|
|
<p>One clarification about the change tracking: it only tracks when values
|
|
have <b>changed</b>. If the ROM writes a value into a RAM location that
|
|
already contained the same value, that's not considered a change (old
|
|
value was $whatever, new value is the same, so nothing got tracked). This
|
|
may change in a future version of Stella.</p>
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="PromptTab"><u>(A)</u> Prompt Tab</a></h2>
|
|
|
|
<p>This is a command-line interface, similar to the DOS DEBUG command
|
|
or Supermon for the C=64.</p>
|
|
|
|
<p>Editing keys work about like you'd expect them to in Windows, but many
|
|
Bash-style commands are also supported:</p>
|
|
|
|
<table border="1" cellpadding=4>
|
|
<tr><th>Key</th><th>Function</th></tr>
|
|
<tr><td>Home</td><td>Move cursor to beginning of line</td></tr>
|
|
<tr><td>End</td><td>Move cursor to end of line</td></tr>
|
|
<tr><td>Delete</td><td>Remove character to right of cursor</td></tr>
|
|
<tr><td>Backspace</td><td>Remove character to left of cursor</td></tr>
|
|
<tr><td>Control-a</td><td>Same function as 'Home'</td></tr>
|
|
<tr><td>Control-e</td><td>Same function as 'End'</td></tr>
|
|
<tr><td>Control-d</td><td>Same function as 'Delete'</td></tr>
|
|
<tr><td>Control-k</td><td>Remove all characters from cursor to end of line</td></tr>
|
|
<tr><td>Control-u</td><td>Remove all characters from cursor to beginning of line</td></tr>
|
|
<tr><td>Control-w</td><td>Remove entire word to left of cursor</td></tr>
|
|
<tr><td>Shift-PgUp</td><td>Scroll up through previous commands one screen/page</td></tr>
|
|
<tr><td>Shift-PgDown</td><td>Scroll down through previous commands one screen/page</td></tr>
|
|
<tr><td>Shift-Up</td><td>Scroll up through previous commands one line</td></tr>
|
|
<tr><td>Shift-Down</td><td>Scroll down through previous commands one line</td></tr>
|
|
<tr><td>Shift-Home</td><td>Scroll to beginning of commands</td></tr>
|
|
<tr><td>Shift-End</td><td>Scroll to end of commands</td></tr>
|
|
</table>
|
|
<p>You can also scroll with the mouse. Copy and paste is not yet supported.</p>
|
|
|
|
<p>To see the available commands, enter "help". For extended help, type "help cmd",
|
|
where 'cmd' is the command you wish to know about. The available commands are listed
|
|
in <a href="#PromptCommands"><b>Prompt Commands</b></a> at the end of this section. Bash-style tab completion is supported for commands,
|
|
labels and functions (see below).</p>
|
|
|
|
<p>For now, there are some functions that only exist in the prompt. We
|
|
intend to add GUI equivalents for all (or almost all?) of the prompt
|
|
commands in future releases. People who like command prompts will be able to
|
|
use the prompt, but people who hate them will have a fully functional
|
|
debugger without typing (or without typing much, anyway).</p>
|
|
|
|
<p>Note: unlike the rest of the UI, whatever is shown in the prompt will not
|
|
be updated during debugging and thus eventually become "stale". You can update it
|
|
just by re-running the relevant commands in the prompt.</p>
|
|
</br>
|
|
|
|
<h3><a name="TabCompletion">Tab Key Auto-Complete</a></h3>
|
|
|
|
<p>While entering a command, label or function, you can type a partial name and
|
|
press the Tab key to attempt to auto-complete it. If you've ever used
|
|
"bash", this will be immediately familiar. If not, try it: load up
|
|
a ROM, go to the debugger, type "g" (but don't press Enter),
|
|
then hit Tab. The "g" will change to "gfx" (since this is the only
|
|
built-in command starting with a "g"). If there are multiple possible
|
|
completions (try with "tr" instead of "g"), you'll see a list of them,
|
|
and your partial name will be completed as far as possible.
|
|
After the first character, the autocompletion considers all
|
|
characters in the right order as a match (e.g. "twf" will be completed to
|
|
"trapwriteif").</p>
|
|
|
|
<p>Tab completion works on all labels: built-in, loaded from a symbol file,
|
|
or set during debugging with the "define" command. It also works with
|
|
built-in functions and those defined with the "function" command,
|
|
but it doesn't (yet) work on filenames.</p>
|
|
</br>
|
|
|
|
<h3><a name="Expressions">Expressions</a></h3>
|
|
|
|
<p>Almost every command takes a value: the "a" command takes a
|
|
byte to stuff into the accumulator, the "break" command
|
|
takes an address to set/clear a breakpoint at. These values
|
|
can be as a hex constant ($ff, $1234), or as complex as
|
|
"the low byte of the 16-bit value located at the address
|
|
pointed to by the binary number 1010010110100101" (which
|
|
would be "@<\1010010110100101"). You can also use registers
|
|
and labels in expressions.</p>
|
|
|
|
<p>You can use arithmetic and boolean operators in expressions. The
|
|
syntax is very C-like. The operators supported are:</p>
|
|
|
|
<pre>
|
|
+ - * / (add, subtract, multiply, divide: 2+2 is 4)
|
|
% (modulus/remainder: 3%2 is 1)
|
|
& | ^ ~ (bitwise AND, OR, XOR, NOT: 2&3 is 2)
|
|
&& || ! (logical AND, OR, NOT: 2&&3 is 1, 2||0 is 0)
|
|
( ) (parentheses for grouping: (2+2)*3 is 12)
|
|
* @ (byte and word pointer dereference: *$80 is the byte stored
|
|
at location $80)
|
|
[ ] (array-style byte pointer dereference: $80[1] is the byte
|
|
stored at location ($80+1) or $81)
|
|
< > (prefix versions: low and high byte. <$abcd is $cd)
|
|
== < > <= >= !=
|
|
(comparison: equality, less-than, greater-than, less-or-equals,
|
|
greater-or-equals, not-equals)
|
|
<< >> (bit shifts, left and right: 1<<1 is 2, 2>>1 is 1)
|
|
</pre>
|
|
|
|
<p>Division by zero is not an error: it results in zero instead.</p>
|
|
|
|
<p>None of the operators change the values of their operands. There
|
|
are no variable-assignment or increment/decrement operators. This
|
|
may change in the future, which is why we used "==" for equality
|
|
instead of just "=".</p>
|
|
|
|
<p>The bitwise and logical boolean operators are different in that the
|
|
bitwise operators operate on all the bits of the operand (just like
|
|
AND, ORA, EOR in 6502 asm), while the logical operators treat their
|
|
operands as 0 for false, non-zero for true, and return either 0 or 1.
|
|
So $1234&$5678 results in $1230, whereas $1234&&$5678 results in 1.
|
|
This is just like C or C++...</p>
|
|
|
|
<h4><a name="Prefixes">Prefixes</a></h4>
|
|
|
|
<p>Like some programming languages, the debugger uses prefixed characters
|
|
to change the meaning of an expression. The prefixes are:</p>
|
|
|
|
<ul>
|
|
<li>Dereference prefixes:<br>
|
|
|
|
<p><pre>'*'</pre>
|
|
Dereference a byte pointer. "*a" means "the byte at the address that
|
|
the A register points to". If A is 255 (hex $ff), the result will be
|
|
the value currently stored in memory location 255. This operator
|
|
will be very familiar to you if you're a C or C++ developer. It's
|
|
equivalent to the PEEK() function in most 8-bit BASICs. Also, the
|
|
debugger supports array-like byte dereferences: *address can be
|
|
written as address[0]. *(address+1) can be written as address[1],
|
|
etc.</p>
|
|
|
|
<p><pre>'@'</pre>
|
|
Dereference a pointer to a word. This is just like the "*" byte deref,
|
|
except it refers to a 16-bit value, occupying 2 locations, in
|
|
low-byte-first format (standard for the 6507).</p>
|
|
|
|
<p>The following are equivalent:</p>
|
|
<pre>
|
|
@address
|
|
*address+$100**(address+1)
|
|
address[0]+#256*address[1]
|
|
</pre>
|
|
|
|
<p>(TODO: add (indirect),y and (indirect,x) syntax)</p>
|
|
</li>
|
|
|
|
<li>Hi/Lo Byte Prefixes:<br>
|
|
<p><pre>'<'</pre>
|
|
Take the low byte of a 16-bit value. This has no effect on an 8-bit
|
|
value: "a" is equal to "<a". However, "<$1234" equals "$34".</p>
|
|
|
|
<p><pre>'>'</pre>
|
|
Take the high byte of a 16-bit value. For 8-bit values such as
|
|
the Accumulator, this will always result in zero. For 16-bit values,
|
|
"<$1234" = "$12".</p>
|
|
</li>
|
|
|
|
<li>Number Base Prefixes:<br>
|
|
<p><pre>'#'</pre>
|
|
Treat the input as a decimal number.</p>
|
|
|
|
<p><pre>'$'</pre>
|
|
Treat the input as a hex number.</p>
|
|
|
|
<p><pre>'\'</pre>
|
|
Treat the input as a binary number.</p>
|
|
|
|
<p>These only have meaning when they come before a number, not a
|
|
label or a register. "\1010" means 10 decimal. So do "$0a" and
|
|
"#10". "a" by itself is always the Accumulator, no matter what
|
|
the default base is set to.</p>
|
|
|
|
<p>If you don't specify any number base prefix, the number is
|
|
assumed to be in the default base. When you first start Stella,
|
|
the default base is 16 (hexadecimal). You can change it with the
|
|
"base" command. If you want to change the default base to decimal permanently,
|
|
you can put a
|
|
<pre>
|
|
base #10
|
|
</pre>
|
|
command in your "autoexec.script" file (for details
|
|
see <a href="#Startup"><b>Startup</b></a>).</p>
|
|
</li>
|
|
</ul>
|
|
<p>Remember, you can use arbitrarily complex expressions with any
|
|
command that takes arguments.</p>
|
|
|
|
</br>
|
|
<h3><a name="BreakpointsEtc">Breakpoints, watches and traps, oh my!</a></h3>
|
|
|
|
<h4><a name="Breakpoints">Breakpoints</a></h4>
|
|
|
|
<p>A breakpoint is a "hotspot" in your program that causes the emulator
|
|
to stop emulating and jump into the debugger. You can set as many
|
|
breakpoints as you like. The command is "break xx" where xx is any
|
|
expression. If you've created a symbol file, you can use labels.</p>
|
|
|
|
<p>Example: you've got a label called "kernel". To break there,
|
|
the command is "break kernel". After you've set the breakpoint,
|
|
exit the debugger ("quit" or click the Exit button). The emulator
|
|
will run until it gets to the breakpoint, then it will enter the
|
|
debugger with the Program Counter pointing to the instruction
|
|
at the breakpoint.</p>
|
|
|
|
<p>Breakpoints happen *before* an instruction is executed: the
|
|
instruction at the breakpoint location will be the "next"
|
|
instruction.</p>
|
|
|
|
<p>To remove a breakpoint, you just run the same command you used to
|
|
set it. In the example, "break kernel" will remove the breakpoint.
|
|
The "break" command can be thought of as a *toggle*: it turns the
|
|
breakpoint on & off, like a light switch.</p>
|
|
|
|
<p>You could also use "clearbreaks" to remove all the breakpoints. Also,
|
|
there is a "listbreaks" command that will list them all.</p>
|
|
|
|
<h4><a name="ConditionalBreaks">Conditional Breaks</a></h4>
|
|
|
|
<p>A conditional breakpoint causes the emulator to enter the debugger when
|
|
some arbitrary condition becomes true. "True" means "not zero" here:
|
|
"2+2" is considered true because it's not zero. "2-2" is false, because
|
|
it evaluates to zero. This is exactly how things work in C and lots
|
|
of other languages, but it might take some getting used to if you've
|
|
never used such a language.</p>
|
|
|
|
<p>Suppose you want to enter the debugger when the Game Reset switch is
|
|
pressed. Looking at the Stella Programmers' Guide, we see that this
|
|
switch is read at bit 0 of SWCHB. This bit will be 0 if the switch is
|
|
pressed, or 1 otherwise.</p>
|
|
|
|
<p>To have an expression read the contents of an address, we use the
|
|
dereference operator "*". Since we're looking at SWCHB, we need
|
|
"*SWCHB".</p>
|
|
|
|
<p>We're only wanting to look at bit 0, so let's mask off all the other
|
|
bits: "*SWCHB&1". The expression now evaluates to bit 0 of SWCHB. We're
|
|
almost there: this will be 1 (true) if the switch is NOT pressed. We
|
|
want to break if it IS pressed...</p>
|
|
|
|
<p>So we invert the sense of the test with a logical NOT operator (which
|
|
is the "!" operator): !(*SWCHB&1). The parentheses are necessary as
|
|
we want to apply the ! to the result of the &, not just the first term
|
|
(the "*SWCHB").</p>
|
|
|
|
<p>"breakif !(*SWCHB&1)" will do the job for us. However, it's an ugly mess
|
|
of letters, numbers, and punctuation. We can do a little better:</p>
|
|
|
|
<p>"breakif { !(*SWCHB & 1 ) }" is a lot more readable, isn't it? If
|
|
you're going to use readable expressions with spaces in them,
|
|
enclose the entire expression in curly braces.</p>
|
|
|
|
<p>Remember that Stella only checks for input once per frame, so a break
|
|
condition that depends on input (like SWCHB) will always happen at the
|
|
end of a frame. This is different from how a real 2600 works, but most
|
|
ROMs only check for input once per frame anyway.</p>
|
|
|
|
<p>Conditional breaks appear in "listbreaks", numbered starting from
|
|
zero. You can remove a cond-break with "delbreakif number", where
|
|
the number comes from "listbreaks" or by entering the same conditional break again.</p>
|
|
|
|
<p>Any time the debugger is entered due to a trap, breakpoint, or
|
|
conditional break, the reason will be displayed in the
|
|
<a href="#BreakpointStatus"><b>Breakpoint/Trap Status</b></a> area.
|
|
|
|
<h4><a name="Functions">Functions</a></h4>
|
|
|
|
<p>There is one annoyance about complex expressions: once we
|
|
remove the conditional break with "delbreakif" or "clearbreaks",
|
|
we'd have to retype it (or search backwards with the up-arrow key)
|
|
if we wanted to use it again.</p>
|
|
|
|
<p>We can avoid this by defining the expression as a function, then using
|
|
"breakif function_name":</p>
|
|
|
|
<pre>
|
|
function gameReset { !(*SWCHB & 1 ) }
|
|
breakif gameReset
|
|
</pre>
|
|
|
|
<p>Now we have a meaningful name for the condition, so we can use it again.
|
|
Not only that: we can use the function as part of a bigger expression.
|
|
Suppose we've also defined a gameSelect function that evaluates to true
|
|
if the Game Select switch is pressed. We want to break when the user
|
|
presses both Select and Reset:</p>
|
|
|
|
<pre>
|
|
breakif { gameReset && gameSelect }
|
|
</pre>
|
|
|
|
<p>User-defined functions appear in "listfunctions", which shows the label
|
|
and expression for each function. Functions can be removed with
|
|
"delfunction label", where the labels come from "listfunctions".</p>
|
|
|
|
<h4><a name="BuiltInFunctions">Built-in Functions</a></h4>
|
|
|
|
<p>Stella has some pre-defined functions for use with the "breakif"
|
|
command. These allow you to break and enter the debugger on various
|
|
conditions without having to define the conditions yourself.</p>
|
|
|
|
<p>Built-in functions and pseudo-registers always start with an _
|
|
(underscore) character. It is suggested that you don't start labels in
|
|
your game's source with underscores, if you plan to use them with the
|
|
Stella debugger.</p>
|
|
|
|
<table border="1" cellpadding=4>
|
|
<tr><th>Function</th><th>Definition</th><th>Description</th></tr>
|
|
<tr><td> _joy0left</td><td> !(*SWCHA & $40)</td><td> Left joystick moved left</td></tr>
|
|
<tr><td> _joy0right</td><td> !(*SWCHA & $80)</td><td> Left joystick moved right</td></tr>
|
|
<tr><td> _joy0up</td><td> !(*SWCHA & $10)</td><td> Left joystick moved up</td></tr>
|
|
<tr><td> _joy0down</td><td> !(*SWCHA & $20)</td><td> Left joystick moved down</td></tr>
|
|
<tr><td> _joy0button</td><td> !(*INPT4 & $80)</td><td> Left joystick button pressed</td></tr>
|
|
<tr><td> _joy1left</td><td> !(*SWCHA & $04)</td><td> Right joystick moved left</td></tr>
|
|
<tr><td> _joy1right</td><td> !(*SWCHA & $08)</td><td> Right joystick moved right</td></tr>
|
|
<tr><td> _joy1up</td><td> !(*SWCHA & $01)</td><td> Right joystick moved up</td></tr>
|
|
<tr><td> _joy1down</td><td> !(*SWCHA & $02)</td><td> Right joystick moved down</td></tr>
|
|
<tr><td> _joy1button</td><td> !(*INPT5 & $80)</td><td> Right joystick button pressed</td></tr>
|
|
<tr><td> _select</td><td> !(*SWCHB & $02)</td><td> Game Select pressed</td></tr>
|
|
<tr><td> _reset</td><td> !(*SWCHB & $01)</td><td> Game Reset pressed</td></tr>
|
|
<tr><td> _color</td><td> *SWCHB & $08</td><td> Color/BW set to Color</td></tr>
|
|
<tr><td> _bw</td><td> !(*SWCHB & $08)</td><td> Color/BW set to BW</td></tr>
|
|
<tr><td> _diff0b</td><td> !(*SWCHB & $40)</td><td> Left difficulty set to B (easy)</td></tr>
|
|
<tr><td> _diff0a</td><td> *SWCHB & $40</td><td> Left difficulty set to A (hard)</td></tr>
|
|
<tr><td> _diff1b</td><td> !(*SWCHB & $80)</td><td> Right difficulty set to B (easy)</td></tr>
|
|
<tr><td> _diff1a</td><td> *SWCHB & $80</td><td> Right difficulty set to A (hard)</td></tr>
|
|
</table>
|
|
|
|
<p>Don't worry about memorizing them all: the Prompt "help" command
|
|
will show you a list of them.</p>
|
|
|
|
<h4><a name="PseudoRegisters">Pseudo-Registers</a></h4>
|
|
|
|
<p>These "registers" are provided for you to use in your conditional breaks.
|
|
They're not registers in the conventional sense, since they don't exist in
|
|
a real system. For example, while the debugger keeps track of the number
|
|
of scanlines in a frame, a real system would not (there is no register
|
|
that holds 'number of scanlines' on an actual console).</p>
|
|
<table border="1" cellpadding=4>
|
|
<tr><th>Function</th><th>Description</th></tr>
|
|
<tr><td> _bank</td><td> Currently selected bank</td></tr>
|
|
<tr><td> _cclocks</td><td> Color clocks on a scanline</td></tr>
|
|
<tr><td> _cycleshi</td><td> Higher 32 bits of number of cycles since emulation started</td></tr>
|
|
<tr><td> _cycleslo</td><td> Lower 32 bits of number of cycles since emulation started</td></tr>
|
|
<tr><td> _fcount</td><td> Number of frames since emulation started</td></tr>
|
|
<tr><td> _fcycles</td><td> Number of cycles since frame started</td></tr>
|
|
<tr><td> _icycles</td><td> Number of cycles of last instruction</td></tr>
|
|
<tr><td> _rwport</td><td> Last address to attempt a read from the cart write port</td></tr>
|
|
<tr><td> _scan</td><td> Current scanline count</td></tr>
|
|
<tr><td> _scycles</td><td> Number of cycles in current scanline</td></tr>
|
|
<tr><td> _vblank</td><td> Whether vertical blank is enabled (1 or 0)</td></tr>
|
|
<tr><td> _vsync</td><td> Whether vertical sync is enabled (1 or 0)</td></tr>
|
|
</table>
|
|
|
|
<p><b>_scan</b> always contains the current scanline count. You can use
|
|
this to break your program in the middle of your kernel. Example:</p>
|
|
<pre>
|
|
breakif _scan==#64
|
|
</pre>
|
|
<p>This will cause Stella to enter the debugger when the TIA reaches the
|
|
beginning of the 64th scanline.</p>
|
|
|
|
<p><b>_bank</b> always contains the currently selected bank. For 2K or 4K
|
|
(non-bankswitched) ROMs, it will always contain 0. One useful use is:</p>
|
|
|
|
<pre>
|
|
breakif { pc==myLabel && _bank==1 }
|
|
</pre>
|
|
|
|
<p>This is similar to setting a regular breakpoint, but it will only trigger
|
|
when bank 1 is selected.</p>
|
|
|
|
<h4><a name="Watches">Watches</a></h4>
|
|
|
|
<p>A watch is an expression that gets evaluated and printed before
|
|
every prompt. This is useful for e.g. tracking the contents of a
|
|
memory location while stepping through code that modifies it.</p>
|
|
|
|
<p>You can set up to 10 watches (in future the number will be unlimited).
|
|
Since the expression isn't evaluated until it's used, you can include
|
|
registers: "watch *y" will show you the contents of the location
|
|
pointed to by the Y register, even if the Y register changes.</p>
|
|
|
|
<p>The watches are numbered. The numbers are printed along with the
|
|
watches, so you can tell which is which. To delete a watch use the
|
|
"delwatch" command with the watch number (1 to whatever). You can
|
|
also delete them all with the "clearwatches" command.</p>
|
|
|
|
<p>Note that there's no real point in watching a label or CPU register
|
|
without dereferencing it: Labels are constants, and CPU registers
|
|
are already visible in the <a href="#CPURegisters"><b>CPU Registers</b></a> widget</p>
|
|
|
|
<h4><a name="Traps">Traps</a></h4>
|
|
|
|
<p>A trap is similar to a breakpoint, except that it catches
|
|
accesses to a memory address, rather than specific location in the
|
|
program. They're useful for finding code that modifies TIA registers
|
|
or memory.</p>
|
|
|
|
<p>Traps can also combined with a condition ("trapif"). If an access
|
|
to a memory address is caught, the condition is evaluated additionally.
|
|
Only if the condition is true too, the emulations stops. For details
|
|
about conditions see <a href="#ConditionalBreaks"><b>Conditional Breaks</b></a> described above.</p>
|
|
|
|
<p>An example: you are debugging a game, and you want to stop the
|
|
emulation and enter the debugger whenever RESP0 is strobed. You'd use
|
|
the command "trap RESP0" to set the trap, then exit the debugger. The
|
|
emulator will run until the next time RESP0 is accessed (either read
|
|
or write). Once the trap is hit, you can examine the TIA state to
|
|
see what the actual player 0 position is, in color clocks (or you
|
|
can in the future when we implement that feature in the TIA dump!)</p>
|
|
|
|
<p>Unlike breakpoints, traps stop the emulation *after* the instruction
|
|
that triggered the trap. The reason for this is simple: until the
|
|
instruction is executed, the emulator can't know it's going to hit a
|
|
trap. After the trap is hit, the instruction is done executing, and
|
|
whatever effects it may have had on e.g. the TIA state have already
|
|
happened... but we don't have a way to run the emulated VCS in reverse,
|
|
so the best we can do is stop before the next instruction runs.</p>
|
|
|
|
<p>Traps come in two varieties: read access traps and write access traps.
|
|
It is possible to set both types of trap on the same address (that's
|
|
what the plain "trap" command does). To set a read or write only trap,
|
|
use "trapread(if)" or "trapwrite(if)".
|
|
|
|
<p>All traps appear in "listtraps", numbered starting from zero. You
|
|
can remove a trap with "deltrap number", where the number comes from
|
|
"listtraps" or by entering the identical trap again. You can get rid of
|
|
all traps at once with the "cleartraps" command.</p></p>
|
|
|
|
</br>
|
|
<h3><a name="SaveWork">Save your work!</a></h3>
|
|
<p>Stella offers several commands to save your work inside the debugger for
|
|
later re-use.</p>
|
|
|
|
<ul>
|
|
<li>
|
|
<b><a name="savecmd">save</a></b>: If you've defined a lot of complex functions, you probably will
|
|
want to re-use them in future runs of the debugger. You can save all
|
|
your functions, breakpoints, conditional breaks, traps and watches with the
|
|
"save" command. If you name your saved file the same as the ROM filename
|
|
and place it in the ROM directory, it will be auto-loaded next time you
|
|
load the same ROM in Stella. The saved file is just a plain text file
|
|
called "<rom_filename>.script", so you can edit it and add new functions, etc.
|
|
<p>Note: While "save" is ROM specific, you can also create a file called
|
|
"autoexec.script" which will be loaded when the debugger starts, no matter
|
|
what ROM you have loaded.<p>
|
|
See <a href="#Startup"><b>Startup</b></a> for details.
|
|
</li>
|
|
<li>
|
|
<b>saveconfig</b>: The "saveconfig" command creates a
|
|
<a href="#DistellaConfiguration"><b>DiStella Configuration File</b></a> which is
|
|
based on Stella's dynamic and static analysis of the current ROM.
|
|
<p>This will be automatically loaded the next time your start the debugger.
|
|
From there on, you can continue analyzing the ROM and then use "saveconfig"
|
|
again to update the configuration. You can also use "loadconfig" to load it
|
|
manually.
|
|
<p>Note that this is not tested for multi-banked ROMs.</p>
|
|
</li>
|
|
<li>
|
|
<b>savedis</b>:
|
|
While your are playing or debugging a game, Stella will gather dynamic
|
|
information about the ROM. It can then use that information together with
|
|
a static analysis of the ROM and therefore create a better disassembly
|
|
than DiStella alone. "savedis" allows you to save that disassembly as the
|
|
result of this combined analysis.
|
|
<p>Note that this currently only works for single banked ROMs. For larger
|
|
ROMs, the created disassembly is incomplete.</p>
|
|
</li>
|
|
<li>
|
|
<p><b>saverom</b>:
|
|
If you have manipulated a ROM, you can save it with "saverom". The file is
|
|
named "<rom_filename>.a26".</p>
|
|
</li>
|
|
<li>
|
|
<p><b>saveses</b>:
|
|
The "saveses" command dumps the whole prompt session into a file named
|
|
"<YYYY-MM-DD_HH-mm-ss>.txt". So you can later lookup what you did exactly
|
|
when you were debugging at that time.</p>
|
|
</li>
|
|
<li>
|
|
<p><b>savestate</b>:
|
|
This command works identical to the save state hotkey (F9) during emulation.
|
|
Any previously saved state can be loaded with "loadstate" plus the slot
|
|
number (0-9).</p>
|
|
</li>
|
|
</ul>
|
|
</br>
|
|
|
|
<h3><a name="PromptCommands">Prompt Commands</a></h3>
|
|
|
|
<p>Type "help" to see this list in the debugger.<br/>
|
|
Type "help 'cmd'" to see extended information about the given command.</p>
|
|
|
|
<pre>
|
|
a - Set Accumulator to <value>
|
|
base - Set default number base to <base> (bin, dec, hex)
|
|
break - Set/clear breakpoint at <address>
|
|
breakif - Set/clear breakpoint on <condition>
|
|
c - Carry Flag: set (0 or 1), or toggle (no arg)
|
|
cheat - Use a cheat code (see manual for cheat types)
|
|
clearbreaks - Clear all breakpoints
|
|
clearconfig - Clear Distella config directives [bank xx]
|
|
clearsavestateifs - Clear all savestate points
|
|
cleartraps - Clear all traps
|
|
clearwatches - Clear all watches
|
|
cls - Clear prompt area of text
|
|
code - Mark 'CODE' range in disassembly
|
|
colortest - Show value xx as TIA color
|
|
d - Decimal Mode Flag: set (0 or 1), or toggle (no arg)
|
|
data - Mark 'DATA' range in disassembly
|
|
debugcolors - Show Fixed Debug Colors information
|
|
define - Define label xx for address yy
|
|
delbreakif - Delete conditional breakif <xx>
|
|
delfunction - Delete function with label xx
|
|
delsavestateif - Delete conditional savestate point <xx>
|
|
deltrap - Delete trap <xx>
|
|
delwatch - Delete watch <xx>
|
|
disasm - Disassemble address xx [yy lines] (default=PC)
|
|
dump - Dump data at address <xx> [to yy] [1: memory; 2: CPU state; 4: input regs]
|
|
exec - Execute script file <xx> [prefix]
|
|
exitrom - Exit emulator, return to ROM launcher
|
|
frame - Advance emulation by <xx> frames (default=1)
|
|
function - Define function name xx for expression yy
|
|
gfx - Mark 'GFX' range in disassembly
|
|
help - help <command>
|
|
joy0up - Set joystick 0 up direction to value <x> (0 or 1), or toggle (no arg)
|
|
joy0down - Set joystick 0 down direction to value <x> (0 or 1), or toggle (no arg)
|
|
joy0left - Set joystick 0 left direction to value <x> (0 or 1), or toggle (no arg)
|
|
joy0right - Set joystick 0 right direction to value <x> (0 or 1), or toggle (no arg)
|
|
joy0fire - Set joystick 0 fire button to value <x> (0 or 1), or toggle (no arg)
|
|
joy1up - Set joystick 1 up direction to value <x> (0 or 1), or toggle (no arg)
|
|
joy1down - Set joystick 1 down direction to value <x> (0 or 1), or toggle (no arg)
|
|
joy1left - Set joystick 1 left direction to value <x> (0 or 1), or toggle (no arg)
|
|
joy1right - Set joystick 1 right direction to value <x> (0 or 1), or toggle (no arg)
|
|
joy1fire - Set joystick 1 fire button to value <x> (0 or 1), or toggle (no arg)
|
|
jump - Scroll disassembly to address xx
|
|
listbreaks - List breakpoints
|
|
listconfig - List Distella config directives [bank xx]
|
|
listfunctions - List user-defined functions
|
|
listsavestateifs - List savestate points
|
|
listtraps - List traps
|
|
loadconfig - Load Distella config file
|
|
loadstate - Load emulator state xx (0-9)
|
|
n - Negative Flag: set (0 or 1), or toggle (no arg)
|
|
palette - Show current TIA palette
|
|
pc - Set Program Counter to address xx
|
|
pgfx - Mark 'PGFX' range in disassembly
|
|
print - Evaluate/print expression xx in hex/dec/binary
|
|
ram - Show ZP RAM, or set address xx to yy1 [yy2 ...]
|
|
reset - Reset system to power-on state
|
|
rewind - Rewind state by one or [xx] steps/traces/scanlines/frames...
|
|
riot - Show RIOT timer/input status
|
|
rom - Set ROM address xx to yy1 [yy2 ...]
|
|
row - Mark 'ROW' range in disassembly
|
|
run - Exit debugger, return to emulator
|
|
runto - Run until string xx in disassembly
|
|
runtopc - Run until PC is set to value xx
|
|
s - Set Stack Pointer to value xx
|
|
save - Save breaks, watches, traps and functions to file xx
|
|
saveconfig - Save Distella config file (with default name)
|
|
savedis - Save Distella disassembly (with default name)
|
|
saverom - Save (possibly patched) ROM (with default name)
|
|
saveses - Save console session (with default name)
|
|
savesnap - Save current TIA image to PNG file
|
|
savestate - Save emulator state xx (valid args 0-9)
|
|
savestateif - Create savestate on <condition>
|
|
scanline - Advance emulation by <xx> scanlines (default=1)
|
|
step - Single step CPU [with count xx]
|
|
stepwhile - Single step CPU while <condition> is true
|
|
tia - Show TIA state
|
|
trace - Single step CPU over subroutines [with count xx]
|
|
trap - Trap read/write access to address(es) xx [yy]
|
|
trapif - On <condition> trap R/W access to address(es) xx [yy]
|
|
trapread - Trap read access to address(es) xx [yy]
|
|
trapreadif - On <condition> trap read access to address(es) xx [yy]
|
|
trapwrite - Trap write access to address(es) xx [yy]
|
|
trapwriteif - On <condition> trap write access to address(es) xx [yy]
|
|
type - Show disassembly type for address xx [yy]
|
|
uhex - Toggle upper/lowercase HEX display
|
|
undef - Undefine label xx (if defined)
|
|
unwind - Unwind state state by one or [xx] steps/traces/scanlines/frames...
|
|
v - Overflow Flag: set (0 or 1), or toggle (no arg)
|
|
watch - Print contents of address xx before every prompt
|
|
x - Set X Register to value xx
|
|
y - Set Y Register to value xx
|
|
z - Zero Flag: set (0 or 1), or toggle (no arg)
|
|
</pre>
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="TIATab"></a><u>(B)</u> TIA Tab</a></h2>
|
|
|
|
<p>When selected, this tab shows detailed status of all the TIA registers
|
|
(except for audio; use the Audio tab for those).</p>
|
|
<p><img src="graphics/debugger_tiatab.png"></p>
|
|
|
|
<p>Most of the values on the TIA tab will be self-explanatory to a 2600
|
|
developer.<p>
|
|
|
|
<p>Many of the variables inside the TIA can only be written to by the
|
|
6502. The debugger lets you get inside the TIA and see the contents
|
|
of these variables. These include the color registers, player/missile
|
|
graphics and positions, and the playfield.</p>
|
|
|
|
<p>You can control almost every aspect of the TIA from here, too: most
|
|
of the displays are editable. You can even toggle individual bits in
|
|
the GRP0/1 and playfield registers (remember to double-click).</p>
|
|
|
|
<p>The group of buttons labelled "Strobes" allows you to write to any
|
|
of the strobe registers at any time.</p>
|
|
|
|
<p>The collision registers are displayed in decoded format, in a table.
|
|
You can see exactly which objects have hit what. These are read-only
|
|
displays; you can't toggle the bits in the current release of Stella. Of
|
|
course, you can clear all the collisions with the CXCLR Strobe button.</p>
|
|
|
|
<p>To the right of each color register, you'll see a small rectangle
|
|
drawn in the current color. Changing a color register will change the
|
|
color of this rectangle.</p>
|
|
|
|
<p>Both player graphics registers (GRP0 and GRP1) come in two versions:
|
|
a "new" and an "old" register. Writing GRP0 updates the value in the "new"
|
|
version of GRP0 and, at the same time, copies the value in the "new" GRP1
|
|
register into its "old" counterpart. Writing to GRP1 behaves the same way,
|
|
with the roles of GRP0 and GRP1 switched. The debugger shows both registers,
|
|
the "old" register being located below the "new" one. If VDEL is off, the
|
|
TIA displays the content of the "new" register, and the debugger tab reflects
|
|
this by crossing out the old register. If VDEL is enabled, the TIA displays
|
|
the "old" register, and the lines are removed in the tab.</p>
|
|
|
|
<p>The "enable ball" register (ENABL) also comes in a "new" and an "old"
|
|
version. The content of "new" is copied into "old" on writes to GRP1, and
|
|
VDELBL selects the register that is used to control the ball. This is
|
|
visualized in the debugger in the same way as the two copies of GRP0 and
|
|
GRP1</p>
|
|
|
|
<p>For many registers, writes don't take effect immediatelly as the
|
|
TIA takes some color clocks to change state. In Stella's TIA core, this
|
|
is implemented by queueing the writes, and the contents of this queue
|
|
are visualized in the debugger in the "Queued Writes" area of the TIA tab.</p>
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="IOTab"><u>(C)</u> I/O Tab</a></h2>
|
|
|
|
<p>When selected, this tab shows detailed status of the Input, Output,
|
|
and Timer portion of the RIOT/M6532 chip (the RAM portion is accessed
|
|
in another part of the debugger).</p>
|
|
<p><img src="graphics/debugger_iotab.png"></p>
|
|
|
|
<p>As with the TIA tab, most of the values here will be self-explanatory to a 2600
|
|
developer, and almost all can be modified. However, the SWCHx registers need
|
|
further explanation:<p>
|
|
<p>SWCHx(W) can be modified; here, the (W) stands for write. Similarly,
|
|
SWACNT/SWBCNT can be directly modified. However, the results of reading back from
|
|
the SWCHx register are influenced by SWACNT/SWBCNT, and SWCHx(R) is a read-only display
|
|
reflecting this result.</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="AudioTab"><u>(D)</u> Audio Tab</a></h2>
|
|
|
|
<p>This tab lets you view the contents of the TIA audio registers and the effective
|
|
volume resulting from the two channel volumes.</p>
|
|
|
|
<p><img src="graphics/debugger_audiotab.png"></p>
|
|
|
|
<p>This tab will grow some features in a future release.</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="TIADisplay"><u>(E)</u> TIA Display</a></h2>
|
|
|
|
<p>In the upper left of the debugger, you'll see the current frame of
|
|
video as generated by the TIA. If a complete frame hasn't been drawn,
|
|
the partial contents of the current frame will be displayed up to the
|
|
current scanline, with the contents of the old frame (in black &
|
|
white) filling the rest of the display. Note that if 'phosphor mode'
|
|
or TV effects are enabled, you won't see the effects here; this shows the
|
|
<b>raw</b> TIA image only.</p>
|
|
|
|
<p>To e.g. watch the TIA draw the frame one scanline at a time, you can
|
|
use the "Scan+1" button, the prompt "scan" command or the Control-L key.</p>
|
|
|
|
<p>You can also right-click anywhere in this window to show a context menu,
|
|
as illustrated:</p>
|
|
<p><img src="graphics/debugger_tiaoutcmenu.png"></p>
|
|
<p>The options are as follows:</p>
|
|
<ul>
|
|
<li><b>Fill to scanline</b>: If you've already started a partial frame
|
|
draw (ie, the frame is already partially 'greyed' out), selecting this
|
|
option will draw all scanlines up to the vertical position where the
|
|
mouse was clicked. Note that if you weren't in partial-frame mode,
|
|
this option will have no effect.</li>
|
|
<li><b>Toggle breakpoint</b>: Will toggle a conditional breakpoint at the
|
|
scanline where the mouse was clicked. You can also use
|
|
the Prompt Tab commands to list and turn off the breakpoint.</li>
|
|
<li><b>Set zoom position</b>: Influences what is shown in the TIA
|
|
zoom area (further described in <a href="#TIAZoom"><b>TIA Zoom</b></a>).
|
|
The zoom area will contain the area centered at the position where the
|
|
mouse was clicked.</li>
|
|
<li><b>Save snapshot</b>: Saves the TIA image currently shown,
|
|
including any current 'effects' (fixed debug colors, partial fill, etc).
|
|
</li>
|
|
</ul>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="TIAInfo"><u>(F)</u> TIA Information</a></h2>
|
|
<p>To the right of the <a href="#TIADisplay"><b>TIA Display</b></a> area, TIA information is displayed:</p>
|
|
<p><img src="graphics/debugger_tiainfo.png"></p>
|
|
<p>The indicators are as follows (note that all these are read-only):</p>
|
|
<ul>
|
|
<li><b>Frame Count</b>: The current frame number, since this ROM was loaded or reset.</li>
|
|
<li><b>Frame Cycle</b>: The number of CPU cycles that have been executed this frame, since
|
|
VSYNC was cleared at scanline 0.</li>
|
|
<li><b>Scanline</b>: The scanline that's currently being drawn, and the count from the
|
|
previous frame. Scanline 0 is the one on which VSYNC is cleared (after being set for 3 scanlines, as per the Stella
|
|
Programmer's Guide)</li>
|
|
<li><b>Scan Cycle</b>: The number of CPU cycles that have been executed since the beginning
|
|
of the current scanline.</li>
|
|
<li><b>VSYNC & VBLANK</b>: Self explanatory.</li>
|
|
<li><b>Pixel Pos</b>: The current number of visible color clocks that have been displayed on
|
|
the current scanline, starting from the beginning of the Horizontal Blank period.
|
|
During HBLANK, this value will be negative (representing the number of clocks
|
|
until the first visible one). Since there are 68 color clocks per HBLANK and
|
|
160 visible clocks per scanline, the Pixel Position will range from -68 to 159.</li>
|
|
<li><b>Color Clock</b>: The current number of total color clocks since the beginning of this
|
|
scanline's HBLANK. This counter starts at zero, but otherwise displays the same
|
|
information as the Pixel Position (so Color Clock will always be 68 more than Pixel
|
|
Position, and will range from 0 to 228).</li>
|
|
</ul>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="TIAZoom"><u>(G)</u> TIA Zoom</a></h2>
|
|
<p>Below the <a href="#TIAInfo"><b>TIA Information</b></a> is the TIA Zoom area. This allows you to enlarge
|
|
part of the TIA display, so you can see fine details. Like the
|
|
<a href="#TIADisplay"><b>TIA Display</b></a> area,
|
|
this one does generate frames as the real system would.</p>
|
|
<p>You can also right-click anywhere in this window to show a context menu,
|
|
as illustrated:</p>
|
|
<p><img src="graphics/debugger_tiazoomcmenu.png"></p>
|
|
<p>These options allow you to zoom in on the image for even greater detail.
|
|
If you click on the output window, you can scroll around using the cursor,
|
|
PageUp/Dn and Home/End keys. You can also select the zoom position from
|
|
a context menu in the <a href="#TIADisplay"><b>TIA Display</b></a>.</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="BreakpointStatus"></a><u>(H)</u> Breakpoint/Trap Status</a></h2>
|
|
<p>Below the <a href="#TIAZoom"><b>TIA Zoom</b></a> there is a status line that
|
|
shows the reason and the address the debugger was entered (if a breakpoint or
|
|
trap was hit), as shown:</p>
|
|
<p><img src="graphics/debugger_bpstatus.png"></p>
|
|
<p>The output here will generally be self-explanatory. Due to space concerns,
|
|
the reason will be shown as follows:
|
|
<ul>
|
|
<li>"CBP:" for conditional breakpoints</li>
|
|
<li>"BP:" for normal breakpoints</li>
|
|
<li>"RTrap:" for read traps</li>
|
|
<li>"WTrap:" for write traps</li>
|
|
<li>"RTrapIf:" for conditional read traps</li>
|
|
<li>"WTrapIf:" for conditional write traps</li>
|
|
</ul>
|
|
</p>
|
|
See the <a href="#BreakpointsEtc"><b>Breakpoints, watches and traps...</b></a>
|
|
section for details.</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="CPURegisters"><u>(I)</u> CPU Registers</a></h2>
|
|
<p>This displays the current CPU state, as shown:</p>
|
|
<p><img src="graphics/debugger_cpuregs.png"></p>
|
|
<p>All the registers and flags are displayed, and can be changed by
|
|
double-clicking on them (to the left). Flags are toggled on double-click.
|
|
Selected registers here can also be changed by using the "Data Operations" buttons,
|
|
further described in (J). All items are shown in hex. Any label defined for the
|
|
current PC value is shown to the right. Decimal and binary equivalents
|
|
are shown for SP/A/X/Y to the right (first decimal, then binary).</p>
|
|
<p>The column to the far right shows the 'source' of contents of the respective
|
|
registers. For example, consider the command 'LDA ($80),Y'. The operand of
|
|
the command resolves to some address, which isn't always easy to determine at
|
|
first glance. The 'Src Addr' area shows the actual resulting operand/address
|
|
being used with the given opcode.</p>
|
|
<p>There's not much else to say about the CPU Registers widget: if you know 6502
|
|
assembly, it's pretty self-explanatory. If you don't, well, you should
|
|
learn :)</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="DataOpButtons"><u>(J)</u> Data Operations Buttons</a></h2>
|
|
<p>These buttons can be used to change values in either
|
|
<a href="#CPURegisters"><b>CPU Registers</b></a>,
|
|
the <a href="#M6532"><b>M6532/RIOT RAM</b></a> or
|
|
<a href="#CartridgeRAMInformation"><b>Detailed Cartridge Extended RAM Information</b></a>,
|
|
depending on which of these widgets is currently in focus.
|
|
<p><img src="graphics/debugger_dataops.png"></p>
|
|
<p>Each of these buttons also have a keyboard shortcut (indicated in square
|
|
brackets). In fact, many of the inputboxes in various parts of the debugger
|
|
respond to these same keyboard shortcuts. If in doubt, give them a try.</p>
|
|
<table border="1" cellpadding=4>
|
|
<tr>
|
|
<th>Button</th><th>Shortut</th><th>Description</th>
|
|
</tr>
|
|
<tr>
|
|
<td>0</td><td><pre>'z'</pre></td><td>Set the current location/register to zero.</td>
|
|
</tr><tr>
|
|
<td>Inv</td><td><pre>'i' or '!'</pre></td><td>Invert the current location/register (toggle all its bits)</td>
|
|
</tr><tr>
|
|
<td>Neg</td><td><pre>'n'</pre></td><td>Negate the current location/register (twos' complement negative)</td>
|
|
</tr><tr>
|
|
<td>++</td><td><pre>'+' or '='</pre></td><td>Increment the current location/register.</td>
|
|
</tr><tr>
|
|
<td>--</td><td><pre>'-'</pre></td><td>Decrement the current location/register.</td>
|
|
</tr><tr>
|
|
<td><<</td><td><pre>'<' or ','</pre></td><td>Shift the current location/register left.</td>
|
|
</tr><tr>
|
|
<td>>></td><td><pre>'>' or '.'</pre></td><td>Shift the current location/register right.</td>
|
|
</tr>
|
|
</table>
|
|
<p>Any bits shifted out of the location/register with << or >>
|
|
are lost (they will NOT end up in the Carry flag).</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="M6532"><u>(K)</u> M6532/RIOT RAM</a></h2>
|
|
<p>This is a spreadsheet-like GUI for inspecting and changing the contents
|
|
of the 2600's zero-page RAM.</p>
|
|
<p>You can navigate with either the mouse or the keyboard arrow keys.
|
|
To change a RAM location, either double-click on it or press Enter while
|
|
it's highlighted. Enter the new value (hex only for now, sorry), then
|
|
press Enter to make the change. If you change your mind, press Escape
|
|
and the original value will be restored. The currently selected RAM cell
|
|
can also be changed by using the
|
|
<a href="#DataOpButtons"><b>Data Operations Buttons</b></a> or the associated
|
|
shortcut keys.</p>
|
|
<p><img src="graphics/debugger_ram.png"></p>
|
|
<p>The 'Undo' button in the upper right should be self-explanatory; it will
|
|
undo the most previous operation to one cell only. The 'Revert' button is
|
|
more comprehensive. It will undo <b>all</b> operations on <b>all</b> cells
|
|
since you first made a change.</p>
|
|
<p>The UI objects at the bottom refer to the currently selected RAM cell.
|
|
The 'Label' textbox shows the label attached to this RAM location (if any),
|
|
and the other two textboxes show the decimal and binary equivalent value.</p>
|
|
|
|
<p>The remaining buttons to the right are further explained in the next section.</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<h3><a name="M6532Search"><u>(L)</u> M6532/RIOT RAM (search/compare mode)</a></h3>
|
|
<p>The RAM widget also lets you search memory for values such as lives or remaining
|
|
energy, but it's also very useful when debugging to determine which
|
|
memory location holds which quantity.</p>
|
|
<p><img src="graphics/debugger_ramsearch.png"></p>
|
|
<p>To search the RAM, click 'Search...' and enter a byte value into the search editbox (0-255).
|
|
All matching values will be highlighted in the RAM widget. If no value was entered,
|
|
all RAM locations will be highlighted.</p>
|
|
|
|
<p>The 'Compare...' button is used to compare the given value using all
|
|
addresses currently highlighted. This may be an absolute number (such as 2),
|
|
or a comparative number (such as -1). Using a '+' or '-' operator
|
|
means 'search addresses for values that have changed by that amount'.</p>
|
|
<p>The 'Reset' button resets the entire operation; it clears the highlighted
|
|
addresses and allows another search.</p>
|
|
<p>The following is an example of inspecting all addresses that have
|
|
decreased by 1:</p>
|
|
<ul>
|
|
<li>Click 'Search...' and then 'OK' (no value entered). All address/values are highlighted</li>
|
|
<li>Exit debugger mode and lose a life, let your energy decrease, or
|
|
do whatever it is you're trying to debug</li>
|
|
<li>Enter debugger mode again, click 'Compare...' and and enter a '-1' for input.
|
|
This finds all values that have decreased by 1 (as compared to their current
|
|
values)</li>
|
|
<li>Repeatedly following these steps may help to narrow number of
|
|
addresses under consideration, and eventually you'll find the
|
|
memory address you're looking for</li>
|
|
<li>Click 'Reset' when you're finished</li>
|
|
</ul>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="Disassembly"></a><u>(M)</u> ROM Disassembly</a></h2>
|
|
<p>This area contains a disassembly of the current bank of ROM. If a symbol
|
|
file is loaded, the disassembly will have labels. Even without a symbol file, the standard TIA/RIOT labels will still be present.</p>
|
|
<p>The disassembly is often quite extensive, and whenever possible tries to automatically
|
|
differentiate between code, graphics, data and unused bytes. There are actually two
|
|
levels of disassembly in Stella. First, the emulation core tracks accesses as a game
|
|
is running, making for very accurate results. This is known as a <b>dynamic</b> analysis.
|
|
Second, the built-in Distella code does a <b>static</b> analysis, which tentatively fills
|
|
in sections that the dynamic disassembler missed (usually because the addresses haven't
|
|
been accessed at runtime yet).</p>
|
|
<p>As such, code can be marked in two ways (absolute, when done by the emulation core),
|
|
and tentative (when done by Distella, and the emulation core hasn't accessed it yet).
|
|
Such 'tentative' code is marked with the '*' symbol, indicating that it has the potential
|
|
to be accessed as code sometime during the program run. This gives very useful information,
|
|
since it can indicate areas toggled by an option in the game (ie, when a player dies,
|
|
when difficulty level changes, etc). It can also indicate whether blocks of code after
|
|
a relative jump are in fact code, or simply data.</p>
|
|
|
|
|
|
<p><img src="graphics/debugger_rom.png"></p>
|
|
|
|
<p>The <B>"Bank state"</B> is self-explanatory, and shows a summary of the current
|
|
bank information. For normal bankswitched ROMs, this will be the current bank number,
|
|
however more advanced schemes will show other types of information here. More detailed
|
|
information is available in <a href="#BankswitchInformation"><b>Detailed Bankswitch Information</b></a>.</p>
|
|
|
|
<p>Each line of disassembly has four fields:</p>
|
|
<ul>
|
|
<li><b>Breakpoint</b>: This is the area at the far left, to the left of the
|
|
labels. Normally there will be nothing there: this indicates that there's
|
|
no breakpoint set at that address. You can set and clear breakpoints by
|
|
clicking in this area. When a breakpoint is set, there will be a
|
|
red circle in this area. These are the same breakpoints as used
|
|
by the <a href="#Breakpoints">break</a> command, <b>not</b> the conditional "breakif" breakpoints
|
|
(which makes sense: conditional breaks can break on any condition, the Program
|
|
Counter isn't necessarily involved).</li>
|
|
<li><b>Labels</b>: Any labels assigned to the given address, either generated
|
|
automatically by Distella, read from a DASM symbol file or custom
|
|
labels created by the user. If 'Show PC addresses'
|
|
(see <a href="#DisassemblySettings"><b>ROM Disassembly Settings</b></a>) is enabled,
|
|
the address will be shown in grey.</li>
|
|
<li><b>Disassembled bytes</b>: This is either a standard 6502 mnemonic (possibly with operand),
|
|
or information about graphics and/or data. For instructions, the cycle count will be
|
|
included, separated by a semicolon. For graphics, a bitmap of the data, and the address
|
|
of the data is included. For actual data, only the address is included.</li>
|
|
<li><b>Hex bytes</b>: These are the raw machine bytes for the code/graphics/data.
|
|
Note that only code, graphics or data will show bytes and can be edited.</li>
|
|
</ul>
|
|
|
|
<p>At this point, we should explain the various 'types' that the disassembler
|
|
can use. These are known as 'directives', and partly correspond to configuration
|
|
options from the standalone Distella program. They are listed in order of
|
|
decreasing hierarchy:</p>
|
|
<table border="1" cellpadding=4>
|
|
<tr><td><b>CODE</b></td><td>Addresses which have appeared in the program counter, or
|
|
which tentatively can appear in the program counter. These can be edited in hex.</td></tr>
|
|
<tr><td><b>GFX</b></td><td>Addresses which contain data stored in the player graphics registers
|
|
(GRP0/GRP1). These addresses are shown with a bitmap of the graphics, which
|
|
can be edited in either hex or binary. The bitmap is shown as large blocks.</td></tr>
|
|
<tr><td><b>PGFX</b></td><td>Addresses which contain data stored in the playfield graphics registers
|
|
(PF0/PF1/PF2). These addresses are shown with a bitmap of the graphics, which
|
|
can be edited in either hex or binary. The bitmap is shown as small dashes.</td></tr>
|
|
<tr><td><b>DATA</b></td><td>Addresses used as an operand for some opcode. These can be edited
|
|
in hex.</td></tr>
|
|
<tr><td><b>ROW</b></td><td>Addresses not used as any of the above. These are shown up
|
|
to 8 per line, and cannot be edited.</td></tr>
|
|
</table>
|
|
|
|
|
|
<p>For code sections, the 6502 mnemonic will be UPPERCASE for all standard instructions,
|
|
or lowercase for "illegal" 6502 instructions (like "dcp"). If automatic resolving
|
|
of code sections has been disabled for any reason, you'll likely see a lot
|
|
of illegal opcodes if you scroll to a data table in ROM. This can also
|
|
occur if the disassembler hasn't yet encountered addresses in the PC.
|
|
If you step/trace/scanline/frame advance into such an area, the disassembler
|
|
will make note of it, and disassemble it correctly from that point on.</p>
|
|
|
|
<!-- TODO - is this true any longer?
|
|
<p>Beware: the cycle counts don't take into account any penalty cycles
|
|
for crossing page boundaries. All branches are shown as 2 cycles, which
|
|
is how long they take if the branch isn't taken. Branches that are
|
|
taken will actually take 3 cycles (plus a penalty cycle if they cross
|
|
page boundaries).</p> -->
|
|
|
|
<p>You can scroll through the disassembly with the mouse or keyboard. To
|
|
center the display on the current PC, press the Space bar.</p>
|
|
|
|
<p>Any time the Program Counter changes (due to a Step, Trace, Frame
|
|
or Scanline advance, or manually setting the PC), the disassembly will
|
|
scroll to the current PC location.</p>
|
|
|
|
<p>Even though ROM is supposed to be Read Only Memory, this is an
|
|
emulator: you can change ROM all you want within the debugger. The hex
|
|
bytes in the ROM Widget are editable. Double-click on them to edit
|
|
them. When you're done, press Enter to accept the changes (in which case
|
|
the cart will be re-disasembled) or Escape to cancel them.
|
|
Note that only instructions that have been fully disassembled
|
|
can be edited. In particular, blank lines or 'ROW' directives
|
|
cannot be edited. Also note that certain ROMs can have
|
|
sections of address space swapped in and out dynamically. As such, changing
|
|
the contents of a certain address will change the area pointed to <b>at
|
|
that time</b>. In particular, modifying an address that points to internal
|
|
RAM will change the RAM, not the underlying ROM. A future release may
|
|
graphically differentiate between RAM and ROM areas.</p>
|
|
|
|
<h3><a name="DisassemblySettings"></a>ROM Disassembly Settings</a></h3>
|
|
|
|
<p>The ROM Disassembly also contains a Settings dialog, accessible by right-clicking
|
|
anywhere in the listing:</p>
|
|
<p><img src="graphics/debugger_romcmenu.png"></p>
|
|
<p>The following options are available:</p>
|
|
<ul>
|
|
<li><b>Set PC @ current line</b>: Set the Program Counter to the address of the
|
|
disassembly line where the mouse was clicked (highlighted in green).</li>
|
|
|
|
<li><b>RunTo PC @ current line</b>: Single-step through code until the Program Counter
|
|
matches the address of the disassembly line where the mouse was clicked (highlighted in green)</li>
|
|
|
|
<li><b>Re-disassemble</b>: Self-explanatory; force the current bank to be
|
|
disassembled, regardless of whether anything has changed.</li>
|
|
|
|
<li><b>Show tentative code</b>: Allow Distella to do a static analysis/disassembly.</li>
|
|
|
|
<li><b>Show PC addresses</b>: Show Program Counter addresses as labels (where there
|
|
isn't already a defined label).</li>
|
|
|
|
<li><b>Show GFX as binary</b>: Switch between displaying/editing GFX and PGFX sections
|
|
in either binary or hexidecimal.</li>
|
|
|
|
<li><b>Use address relocation</b>: Corresponds to the Distella '-r' option
|
|
(Relocate calls out of address range).</li>
|
|
|
|
</ul>
|
|
|
|
<h3><a name="Limitations">Limitations</a></h3>
|
|
|
|
<ul>
|
|
|
|
<li>The ROM Widget only works on ROM or zero-page RAM separately. If your game runs
|
|
code from zero-page RAM, the disassembly will show addresses $80 to $FF (zero-page
|
|
RAM address space) only. Once your RAM routine returns, the ROM Widget will switch
|
|
back to ROM space ($1000 - $1FFF and mirrors). The same is true of the "disasm"
|
|
command; it will show either ROM or RAM space, not both at the same time.</li>
|
|
|
|
<li>The standard VCS memory map has the cartridge port at locations
|
|
$F000-$FFFF. However, not all the address lines exist on a 6507, so
|
|
the cartridge port is "mirrored" at every other 4K chunk of address
|
|
space ($1000, $3000, up to $D000). Some developers find it easier
|
|
to think of the different banks of ROM as being at different addresses
|
|
(such as: Bank 0 at $D000 and bank 1 at $F000). When such a ROM runs,
|
|
the Program Counter can point to one of the mirrors instead of the main
|
|
area at $F000. This is perfectly OK, and everything works just fine.
|
|
However, breakpoints are set on actual addresses. If there were a breakpoint
|
|
set at $F010, and the bank later switched to mirror $D000, the breakpoint
|
|
will not be shown, and will not break on $D010, even though it's technically
|
|
the same address.</li>
|
|
|
|
</ul>
|
|
|
|
<p>These limitations will be addressed in a future release of Stella.</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="BankswitchInformation"><u>(N)</u> Detailed Bankswitch Information</a></h2>
|
|
|
|
<p>This area shows a detailed breakdown of the bankswitching scheme. Since
|
|
the bankswitch schemes can greatly vary in operation, this tab will be
|
|
different for each scheme, but its specific functionality should be self-explanatory.
|
|
An example of both 4K (non-bankswitched) and DPC (Pitfall II) is as follows:</p>
|
|
|
|
<p><img src="graphics/debugger_banksimple.png"></p>
|
|
<p><img src="graphics/debugger_bankcomplex.png"></p>
|
|
|
|
<p>In many cases, quite a bit of the scheme functionality can be modified.
|
|
Go ahead and try to change something!</p>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h2><a name="CartridgeRAMInformation"><u>(O)</u> Detailed Cartridge Extended RAM Information</a></h2>
|
|
|
|
<p>If applicable, this area shows a detailed breakdown of any extra RAM supported by
|
|
the bankswitching scheme. Since the bankswitch schemes can greatly vary in operation,
|
|
this tab will be different for each scheme, but its specific functionality should be
|
|
self-explanatory. An example of both F8SC (8K Atari + ram) and DPC (Pitfall II) is
|
|
as follows:</p>
|
|
|
|
<p><img src="graphics/debugger_ram-f8sc.png"></p>
|
|
<p><img src="graphics/debugger_ram-dpc.png"></p>
|
|
|
|
<p>The RAM is shown in a grid similar to how zero-page RAM is shown in M6532/RIOT RAM
|
|
(K) and (L). See those sections for a description of usage.
|
|
|
|
<p>In the cases where RAM is always mapped into the same place in the cartridge
|
|
address space (such as Sara-chip), the RAM addresses are labeled as such. In other
|
|
cases, such as when the RAM is either quiescent (and mapped in at different places),
|
|
or not viewable by the 6507 at all, the RAM addresses are labeled as the cart sees them.
|
|
In the examples above, F8SC RAM is labeled starting at its read port, or $F080. However,
|
|
the RAM in the DPC scheme is not viewable by the 6507, so its addresses start from $0.
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h1><a name="DistellaConfiguration">Distella Configuration Files</a></h1>
|
|
<p>As mentioned in <a href="#Disassembly"><b>ROM Disassembly</b></a>, Stella supports the following directives:
|
|
CODE/GFX/PGFX/DATA/ROW. While the debugger will try to automatically mark address
|
|
space with the appropriate directive, there are times when it will fail. There are
|
|
several options in this case:</p>
|
|
<ol>
|
|
<li><b>Manually set the directives</b>: Directives can be set in the debugger
|
|
prompt with the code/gfx/pgfx/data/row commands. These accept an address range
|
|
for the given directive type. Setting a range with the same type a second time
|
|
will remove that directive from the range.</li>
|
|
<li><b>Use configuration files</b>: Configuration files can be used to automatically
|
|
load a list of directives when a ROM is loaded. These files can be generated with the
|
|
'saveconfig' command, and loaded with the 'loadconfig' command. There are also
|
|
'listconfig' and 'clearconfig' commands to show and erase (respectively) the current
|
|
directive listing. Upon opening the debugger for the first time, Stella attempts
|
|
to load a configuration file from several places. For this example, assume a ROM
|
|
named "rr.a26", with properties entry "River Raid". Attempts will be made as follows:
|
|
<ul>
|
|
<li>ROM dir based on properties entry name: <i>River Raid.cfg</i></li>
|
|
<li>ROM dir based on actual ROM name: <i>rr.cfg</i></li>
|
|
<li>CFG dir based on properties entry name: <i>configdir/River Raid.cfg</i></li>
|
|
</ul>
|
|
<p>The location of 'configdir' will depend on the OS as follows:</p>
|
|
|
|
<p><table cellpadding=4 border="1">
|
|
<tr>
|
|
<td><b>Linux/Unix</b></td>
|
|
<td><i>~/.stella/cfg/</i></td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Macintosh</b></td>
|
|
<td><i>~/Library/Application Support/Stella/cfg/</i></td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Windows</b></td>
|
|
<td><i>%APPDATA%\Stella\cfg\</i>
|
|
<b>OR</b><br>
|
|
<i>_BASEDIR_\cfg\</i>
|
|
(if a file named 'basedir.txt' exists in the application
|
|
directory containing the full pathname for _BASEDIR_)
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</table>
|
|
</li>
|
|
</ol>
|
|
|
|
|
|
<!-- ///////////////////////////////////////////////////////////////////////// -->
|
|
<br>
|
|
<h1><a name="HowToHack">Tutorial: How to hack a ROM</a></h1>
|
|
|
|
<p>Here is a step-by-step guide that shows you how to use the debugger to
|
|
actually do something useful. No experience with debuggers is necessary,
|
|
but it helps to know at least a little about 6502 programming.</p>
|
|
|
|
<ol>
|
|
<li>Get the Atari Battlezone ROM image. Make sure you've got the
|
|
regular NTSC version. Load it up in Stella and press TAB to get to
|
|
the main menu. From there, click on "Game Information". For "Name", it
|
|
should say "Battlezone (1983) (Atari)" and for MD5Sum it should say
|
|
"41f252a66c6301f1e8ab3612c19bc5d4". The rest of this tutorial assumes
|
|
you're using this version of the ROM; it may or may not work with the
|
|
PAL version, or with any of the various "hacked" versions floating around
|
|
on the 'net.</li>
|
|
|
|
<li>Start the game. You begin the game with 5 lives (count the tank
|
|
symbols at the bottom of the screen).</li>
|
|
|
|
<li>Enter the debugger by pressing the ` (backquote) key. Don't get
|
|
killed before you do this, though. You should still have all 5 lives.</li>
|
|
|
|
<li>In the RAM display, click the "Search" button and enter "5" for input.
|
|
This searches RAM for your value and highlights all addresses that match
|
|
the input. You should see two addresses highlighted: "00a5" and "00ba".
|
|
These are the only two addresses that currently have the value 5, so they're
|
|
the most likely candidates for "number of lives" counter. (However, some
|
|
games might actually store one less than the real number of lives, or
|
|
one more, so you might have to experiment a bit. Since this is a "rigged
|
|
demo", I already know Battlezone stores the actual number of lives.
|
|
Most games do, actually).</li>
|
|
|
|
<li>Exit the debugger by pressing ` (backquote) again. The game will
|
|
pick up where you left off.</li>
|
|
|
|
<li>Get killed! Ram an enemy tank, or let him shoot you. Wait for
|
|
the explosion to finish. You will now have 4 lives.</li>
|
|
|
|
<li>Enter the debugger again. Click the "Compare" button in RAM widget and enter
|
|
a value of 4. Now the RAM widget should only show one highlighted address:
|
|
"00ba". What we did was search within our previous results (the ones that
|
|
were 5 before) for the new value 4. Address $00ba used to have the value 5,
|
|
but now it has 4. This means that Battlezone (almost certainly) stores the
|
|
current number of lives at address $00ba.</li>
|
|
|
|
<li>Test your theory. Go to the RAM display and change address $ba to
|
|
some high number like $ff (you could use the Prompt instead: enter "ram
|
|
$ba $ff"). Exit the debugger again (or advance the frame). You should now see lots of lives
|
|
at the bottom of the screen (of course, there isn't room to display $ff
|
|
(255) of them!)... play the game, get killed a few times, notice that
|
|
you have lots of lives.</li>
|
|
|
|
<li>Now it's time to decide what sort of "ROM hack" we want to
|
|
accomplish. We've found the "lives" counter for the game, so we can
|
|
either have the game start with lots of lives, or change the game
|
|
code so we can't get killed (AKA immortality), or change the code
|
|
so we always have the same number of lives (so we never run out, AKA
|
|
infinite lives). Let's go for infinite lives: it's a little harder than
|
|
just starting with lots of lives, but not as difficult as immortality
|
|
(for that, we have to disable the collision checking code, which means
|
|
we have to find and understand it first!)</li>
|
|
|
|
<li>Set a Write Trap on the lives counter address: "trapwrite $ba"
|
|
in the Prompt. Exit the debugger and play until you get killed. When
|
|
you die, the trap will cause the emulator to enter the debugger with the
|
|
Program Counter pointing to the instruction *after* the one that wrote
|
|
to location $ba.</li>
|
|
|
|
<li>Once in the debugger, look at the ROM display. The PC should be at address
|
|
$f238, instruction "LDA $e1". You want to examine a few instructions before
|
|
the PC, so scroll up using the mouse or arrow keys. Do you see
|
|
the one that affects the lives counter? That's right, it's the "DEC $ba"
|
|
at location $f236.</li>
|
|
|
|
<li>Let's stop the DEC $ba from happening. We can't just delete the
|
|
instruction (it would mess up the addressing of everything afterwards,
|
|
if it were even possible), but we can replace it with some other
|
|
instruction(s).
|
|
|
|
<p>Since we just want to get rid of the instruction, we can replace it with
|
|
NOP (no operation). From looking at the disassembly, you can see that
|
|
"DEC $ba" is a 2-byte long instruction, so we will need two one-byte
|
|
NOP instructions to replace it. From reading the prompt help (the "help"
|
|
command), you can see that the "rom" command is what we use to patch ROM.
|
|
|
|
<p>Unfortunately, Stella doesn't contain an assembler, so we can't just
|
|
type NOP to put a NOP instruction in the code. We'll have to use the
|
|
hex opcode instead.
|
|
|
|
<p>Now crack open your 6502 reference manual and look up the NOP
|
|
instruction's opcode... OK, OK, I'll just tell you what it is: it's $EA
|
|
(234 decimal). We need two of them, so the bytes to insert will look like:
|
|
|
|
<pre> $ea $ea</pre>
|
|
|
|
<p>Select the line at address $f236 and enter 'ROM patch' mode. This is done
|
|
by either double-clicking the line, or pressing enter. Then delete the bytes
|
|
with backspace key and enter "ea ea". Another way to do this would have been
|
|
to enter "rom $f236 $ea $ea" in the Prompt widget.
|
|
</li>
|
|
|
|
<li>Test your patch. First, set location $ba to some number of
|
|
lives that can be displayed on the screen ("poke $ba 3" or enter directly into
|
|
the RAM display). Now exit the debugger and play the game. You should see 3
|
|
lives on the screen.</li>
|
|
|
|
<li>The crucial test: get killed again! After the explosion, you
|
|
will *still* see 3 lives: Success! We've hacked Battlezone to give us
|
|
infinite lives.</li>
|
|
|
|
<li>Save your work. In the prompt: "saverom". You now
|
|
have your very own infinite-lives version of Battlezone. The file will
|
|
be saved in your HOME directory (NOT your ROM directory), so you might
|
|
want to move it to your ROM directory if it isn't the current directory.
|
|
</li>
|
|
|
|
<li>Test the new ROM: exit Stella, and re-run it. Open your ROM
|
|
(or give its name on the command line) and play the game. You can play
|
|
forever! It worked.</li>
|
|
</ol>
|
|
|
|
<p>Now, try the same techniques on some other ROM image (try Pac-Man). Some
|
|
games store (lives+1) or (lives-1) instead of the actual number,
|
|
so try searching for those if you can't seem to make it work.</p>
|
|
|
|
<p>If you successfully patch a ROM in the debugger, but the saved version
|
|
won't work, or looks funny, you might need to add an entry to the
|
|
stella.pro file, to tell Stella what bankswitch and/or TV type to use.
|
|
That's outside the scope of this tutorial :)</p>
|
|
|
|
<p>Of course, the debugger is useful for a lot more than cheating and
|
|
hacking ROMs. Remember, with great power comes great responsibility,
|
|
so you have no excuse to avoid writing that game you've been thinking
|
|
about for so long now :)</p>
|