
20050717 bkw

This alpha build of Stella contains an incomplete version of the debugger.

What the debugger can do:
- Display registers and memory
- Dump state of TIA and RIOT, with things like joystick directions and
  NUSIZx decoded into English (more-or-less).
- Change registers/memory, including toggles for flags in P register
- Single step/trace
- Breakpoints - break running program and enter debugger when the
  Program Counter hits a predefined address. You can set as many
  breakpoints as you want.
- 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.
- Watches - View contents of a location/register before every
  debugger prompt.
- Traps - Like breakpoints, but break on read/write/any access to
  *any* memory location.
- Frame advance (automatic breakpoint at beginning of next frame)
  You can advance multiple frames with one command.
- Disassembly
- Support for DASM symbol files (created with DASM's -s option),
  including automatically loading symbol files if they're named
  romname.sym
- Built-in VCS.H symbols, if no symbol file is loaded
- Symbolic names in disassembly
- Symbolic names accepted as input
- Tab completion for commands and symbol names
- Graphical editor for RIOT RAM. Acts a lot like a spreadsheet.
  Input in hex, with displays for label/decimal/binary for
  currently-selected location.
- GUI CPU state window
- Cheat system (similar to MAME) (still needs a way to save/load cheats)
- Reset the 6502
- Input and output in hex, decimal, or binary
- Start emulator in debugger (via command-line option "-debug")
- Save CLI session to a text file.
- Supports hex, decimal, and binary input and output almost everywhere.
  (disassembly is still hex)
- Support for bank switching. You can see how many banks a cart has,
  and switch banks. There's still more to be done here though.
- Patching ROM in-place. Currently there's no way to save the patched
  ROM though.
- Registers/memory that get changed by the CPU during debugging are
  highlighted when they're displayed
- Scanline advance (like frame advance, break at beginning
  of next scanline).
- 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.

Planned features for Stella 2.0 release:
- Graphical TIA tab, with register names and GUI buttons for
  various bits (e.g. click ENAM0 to turn it on). This has been started
  on already.
- GUI Disassembly window, scrollable, with checkboxes for breakpoints
  (also perhaps 2 panes in this window so you can see 2 parts of the
  code at once)
- Bankswitch support in the debugger for the few remaining cart types
  that aren't supported.
- Patch ROM support for a few cart types doesn't work.
- Some way to control joystick/etc. input from within the debugger.
- Script (batch) file support, including auto-running a script file
  named after the ROM image.
- Saving the current debugger state to a script file (including
  breakpoints, traps, etc).
- Source-level debugging: if a DASM .lst file is available, we'll show
  the listing in the ROM tab instead of a disassembly.
- Save patched ROM.
- More "special variables" for the expression parser. Currently, you can
  use all the CPU registers and flags in expressions (e.g. "print a+1" does
  what you expect). Need to add TIA, RIOT registers too (so you can say
  "breakif _scanline==30" or such).

Future plans (post 2.0):
- Possibly a mini-assembler
- Support for extra RAM in Supercharger and other cart types.
- 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
- Graphics ROM view, so you can see your sprite data (it might still
  be upside-down though :)
- Various new GUI enhancements

How to use the debugger
-----------------------

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.  Pressing Ctrl-Tab cycles between tabs
from left to right, and Shift-Ctrl-Tab cycles from right to left.
Pressing Tab cycles between widgets in the current tab.

Tabs:

The top-level user interface uses tabs to select the current debugger
mode. Not all the tabs are implemented yet: those that aren't will just
show up as a blank tab.

The tabs that are implemented so far:

- Prompt tab

  This is a command-line interface, similar to the DOS DEBUG command
  or Supermon for the C=64. It shows you the current CPU state, including
  the disassembly of the instruction pointed to by the Program Counter.
  This instruction is the NEXT one that will execute, NOT the one that
  just executed!

  Editing keys work about like you'd expect them to: Home, End, Delete,
  arrows, etc. To scroll with the keyboard, use Shift-PageUp and
  Shift-PageDown or Shift-Up and Shift-Down arrow keys. You can also
  scroll with the mouse. Copy and paste is not (yet?) supported.

  To see the available commands, enter "help" or "?". Most commands can
  be abbreviated: instead of "clearbreaks", you can type "clear" or
  even just "cl". However, "c" by itself is the Toggle Carry command.

  Bash-style tab completion is supported for commands and labels (see below)

  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 by the time we release Stella 2.0. 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).

  - Status

    Before each prompt, you'll see a dump of the current processor
    status, plus any watches you've set (see section on watches
    below), plus a disassembly of the instruction at the current
    Program Counter.  This is the next instruction that will execute;
    it hasn't been executed yet.

    PC=f000 A=01 X=02 Y=03 S=ff P=21/nv-bdizC  Cycle 12345
      $f000  a2 ff    LDX #ff  ; 2
    >

    This is telling us a lot of information:

    - PC=f000 A=01 X=02 Y=03 S=ff
      The Program Counter, Accumulator, X, Y, and Stack Pointer registers.

    - P=21/nv-bdizC
      The Processor Status register. The "21" is the hex value of the
      register, and the "nv-bdizC" shows us the individual flags. Flags
      with capital letters are set, and lowercase means clear. In this
      example, only the Carry bit is set.

    - Cycle 12345
      This counter gets reset at the beginning of each frame, when
      VSYNC is strobed. In this example, we're 12345 (decimal) cycles
      past the beginning of the frame.

    - $f000  a2 ff    LDX #ff  ; 2
      Disassembly of the instruction the PC points to. This is the next
      instruction that will execute (when we exit the debugger, or via the
      "step" or "trace" commands). The "a2 ff" is the raw machine code
      for this instruction. The "; 2" is a cycle count: this instruction
      will take 2 cycles to execute.

      WARNING: the cycle count in the disassembly does NOT include extra
      cycles caused by crossing page boundaries. Also, Branch instructions
      are shown as 2 cycles, as though they're not going to be taken. A
      branch that's taken adds a cycle, of course.

   The ">" is where your commands will appear as you type them.


  - Tab completion

    While entering a command or label, 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 "print w" (but don't press Enter),
    then hit Tab. The "w" will change to "WSYNC" (since this is the only
    built-in label starting with a "w"). If there are multiple possible
    completions (try with "v" instead of "w"), you'll see a list of them,
    and your partial name will be completed as far as possible.

    Tab completion works on all labels: built-in, loaded from a symbol file,
    or set during debugging with the "define" command.

  - Expressions

    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.

    Like some programming languages, the debugger uses prefixed characters
    to change the meaning of an expression. The prefixes are:

    - Dereference prefixes:

    - "*"
      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++ programmer.

    - "@"
      Dereference a word pointer. 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).

    Only one or the other of the "*" and "@" prefixes is allowed in a
    given expression. If you're going to use a dereference, it needs
    to come first.

    - Hi/Lo Byte Prefixes

    - "<"
      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".

    - ">"
      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".

   Only one or the other of the "<" and ">" prefixes is allowed in
   the same expression. If you're going to use one, it needs to come
   after the dereference, if there is one. "*<myLabel" is legal,
   but "<*myLabel" is not (yet). These operators behave just like
   they do in DASM.

   - Number Base Prefixes

   - "#"
     Treat the input as a decimal number.

   - "$"
     Treat the input as a hex number.

   - "%"
     Treat the input as a binary number.

   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". You can only use one of the "#" "$" "%" prefixes per
   expression. When used, they must come immediately before the
   number in the expression, after any dereference or hi/lo byte
   operators.

   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.

   Remember, you can use arbitrarily complex expressions with any
   command that takes arguments (except the ones that take filenames,
   like "loadsym").

   (Future versions of the debugger may allow arithmetic and boolean
    operators in expressions)


  - Breakpoints, watches and traps, oh my!

    - Breakpoints

    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.

    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.

    Breakpoints happen *before* an instruction is executed: the
    instruction at the breakpoint location will be the "next"
    instruction.

    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.

    You could also use "clearbreaks" to remove all the breakpoints. Also,
    there is a "listbreaks" command that will list them all.

    - Watches

    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.

    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.

    The watches are numbered 1 to 10. 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 10). You can also
    delete them all with the "clearwatches" command.

    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 prompt status.

    - Traps

    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.

    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!)

    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...  so we don't have a way to run the emulated CPU
    in reverse.

    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" or "trapwrite". To remove a trap, you just attempt
    to set it again: the commands actually toggle the trap on & off. You
    can also get rid of all traps at once with the "cleartraps" command.


  - Prompt commands:

            a - Set Accumulator to value xx
         bank - Show # of banks (with no args), Switch to bank (with
                1 arg)
         base - Set default base (hex, dec, or bin)
        break - Set/clear breakpoint at address (default=pc)
            c - Carry Flag: set (to 0 or 1), or toggle (no arg)
  clearbreaks - Clear all breakpoints
   cleartraps - Clear all traps
 clearwatches - Clear all watches
    colortest - Color Test
            d - Decimal Flag: set (to 0 or 1), or toggle (no arg)
       define - Define label
     delwatch - Delete watch
       disasm - Disassemble from address (default=pc)
         dump - Dump 128 bytes of memory at address
        frame - Advance emulation by xx frames (default=1)
         help - This cruft
   listbreaks - List breakpoints
    listtraps - List traps
  listwatches - List watches
    loadstate - Load emulator state (0-9)
      loadsym - Load symbol file
            n - Negative Flag: set (to 0 or 1), or toggle (no arg)
           pc - Set Program Counter to address
        print - Evaluate and print expression in hex/dec/binary
          ram - Show RAM contents (no args), or set RAM address xx to
                value yy
       reload - Reload ROM and symbol file
        reset - Reset 6507 to init vector (does not reset TIA, RIOT)
         riot - Show RIOT timer/input status
          rom - Change ROM contents
          run - Exit debugger, return to emulator
            s - Set Stack Pointer to value xx
      saveses - Save console session to file
    savestate - Save emulator state (valid args 0-9)
      savesym - Save symbols to file
         step - Single step CPU (optionally, with count)
          tia - Show TIA state
        trace - Single step CPU (optionally, with count), subroutines
                count as one instruction
         trap - Trap read and write accesses to address
     trapread - Trap read accesses to address
    trapwrite - Trap write accesses to address
        undef - Undefine label (if defined)
            v - Overflow Flag: set (to 0 or 1), or toggle (no arg)
        watch - Print contents of address before every prompt
            x - Set X Register to value xx
            y - Set Y Register to value xx
            z - Zero Flag: set (to 0 or 1), or toggle (no arg)

    This list may be outdated: see the output of the "help" command
    for the current list. Commands marked with a * are unimplemented.
    Commands marked with a + are partially implemented. Most commands can
    be abbreviated just by typing a partial command: "st" for "step", or
    "f" for "frame". Where there are conflicts, generally the shortest
    name is chosen (so "tra" is "trap", not "trapread").


- CPU tab

(TODO: document this, when it's close enough to finished)


- RAM tab

  This is a spreadsheet-like GUI for inspecting and changing the contents
  of the 2600's RAM. All 128 bytes of RAM are visible on the screen at
  once. 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.

  On the right there are also some buttons to do various things to the
  currently-selected memory location. The buttons are:

  0 - Set the current location to zero.
  Inv - Invert the current location (toggle all its bits).
  Neg - Negate the current location (twos' complement negative).
  ++  - Increment the current location
  --  - Decrement the current location
  <<  - Shift the current location left. Any bits shifted off the left
        are lost (they will NOT end up in the Carry flag).
  >>  - Shift the current location right, like << above.

- Cheat tab

  This tab 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.

  Currently, input must be in decimal format, and search results will be
  shown in decimal (with hex addresses).

  Enter a byte value into the search editbox (0-255) and click 'Search'.
  All matching address/value pairs will be shown in the listbox to the
  right.  The values in the listbox may be changed using the normal
  editing operations, and the RAM will be immediately updated.  If
  'Search' is clicked and the inputbox is empty, all RAM locations
  are returned.

  The 'Compare' button is used to compare the given value using all
  addresses in the listbox.  This may be an absolute number (such as 2),
  or a comparitive number (such as -1).  Using a '+' or '-' operator
  means 'search addresses for values that have changed by that amount'.

  The following is an example of inspecting all addresses that have
  decreased by 1:

    Click 'Search' with an empty inputbox.  All 128 address/values are
    returned

    Exit debugger mode and lose a life, let your energy decrease, or
    do whatever it is you're trying to debug

    Enter debugger mode again, and enter a '-1' in the inputbox

    Click the 'Compare' button, which compares all addresses in the
    listbox with RAM, and finds all values that have decreased by 1
    (as compared to their value in the listbox).

    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

  The 'Restart' button restarts the whole procedure (ie, clear the
  input and listboxes, and allows another search.

  (TODO: need a way to name and save/load the cheats)

- TIA tab (TODO)

- ROM tab (TODO)

Global Buttons:

There are also buttons on the right that always show up no matter which
tab you're looking at. These are always active. They are: Step, Trace,
Frame+1, and Exit.

When you use these buttons, the prompt doesn't change. This means the
status lines with the registers and disassembly will be "stale". You
can update them just by pressing Enter in the prompt.

You can also use the Step, Trace and Frame+1 buttons from anywhere in
the GUI via the keyboard, with Alt-S, Alt-T and Alt-F.
