Tweaked ContextMenu so that an item is selected/committed only when the

left mouse button is pressed and released on the item (should fix problems
with accidentally selecting an item).  Also added 'Escape' key to mean
"cancel/remove context menu".

Updated '-help' commandline argument to recent additions.

Huge amount of work on the documentation.  It's probably not complete,
but it's turning into a project in itself.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@776 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2005-09-13 18:27:42 +00:00
parent 09a8f8f10d
commit 8aed4f81a9
12 changed files with 923 additions and 850 deletions

View File

@ -1,753 +0,0 @@
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, or have a slow CPU
- 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.
- 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).
- Built-in functions for use with "breakif", to support common conditions
(such as breaking when the user presses Game Select...)
- Save patched ROM ("saverom filename.bin").
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. Must fix.
- Some way to control joystick/etc. input from within the debugger.
- Source-level debugging: if a DASM .lst file is available, we'll show
the listing in the ROM tab instead of a disassembly. This is already
availabe in a very crude form ("loadlist" and "list" commands).
- 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), and the pseudo-variables "_scan" and "_bank" (which
evaluate the the current scanline and bank number). Need to add more TIA,
RIOT registers too. Also, more functions for the builtin function lib.
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.
You can also enter the debugger by giving a breakpoint on the command line:
; will enter the debugger the first time the instruction at "kernel" runs
stella -break kernel mygame.bin
; $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
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. However, it does not
yet work on functions defined with the "function" command, nor does it
work on filenames.
- 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.
You can use arithmetic and boolean operators in expressions. The
syntax is very C-like. The operators supported are:
+ - * / (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)
Division by zero is not an error: it results in zero instead.
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 "=".
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++...
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. 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.
- "@"
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).
The following are equivalent:
@address
*address+$100**(address+1)
address[0]+#256*address[1]
(TODO: add (indirect),y and (indirect,x) syntax)
- 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".
- 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". "a" by itself is always the Accumulator, no matter what
the default base is set to.
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").
- 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.
- Conditional Breaks
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.
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.
To have an expression read the contents of an address, we use the
dereference operator "*". Since we're looking at SWCHB, we need
"*SWCHB".
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...
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").
"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:
"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 {}.
There is one annoyance about this complex expression: 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.
We can avoid this by defining the expression as a function, then using
"breakif function_name":
function gameReset { !(*SWCHB & 1 ) }
breakif gameReset
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:
breakif { gameReset && gameSelect }
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, and watches
with the "save" command. If you name your saved file the same
as the ROM filename, it'll be auto-loaded next time you load the
same ROM in Stella. The save file is just a plain text file called
"filename.stella", so you can edit it and add new functions, etc.
Conditional breaks appear in "listbreaks", numbered starting from
zero. You can remove a cond. break with "delbreakif number", where
the number comes from "listbreaks".
- 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. 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.
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.
Use "listtraps" to see all enabled traps.
- 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.
Tutorial: How to hack a ROM
---------------------------
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.
Step 1: 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.
Step 2: Start the game. You begin the game with 5 lives (count the tank
symbols at the bottom of the screen).
Step 3: Enter the debugger by pressing the ` (backquote) key. Don't get
killed before you do this, though. You should still have all 5 lives.
Step 4: Go to the Cheat tab (click on Cheat). The Cheat tab will allow you
to search RAM for values such as the number of remaining lives.
Step 5: In the Cheat tab, enter "5" in the "Enter a value" box and click
on Search. This searches RAM for your value and displays the results in
the Address/Value list to the right. You should see two results: "00a5:
05" and "00ba: 05". 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).
Step 6: Exit the debugger by pressing ` (backquote) again. The game will
pick up where you left off.
Step 7: Get killed! Ram an enemy tank, or let him shoot you. Wait for
the explosion to finish. You will now have 4 lives.
Step 8: Enter the debugger again. The Cheat tab will still be showing.
In the "Enter a value" box, delete the 5 you entered earlier, and replace
it with a 4. Click "Compare". Now the Address/Value list should only
show one result: "00ba: 04". 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.
Step 9: Test your theory. Go to the RAM tab and change address $ba to
some high number like $ff (you could use the Prompt instead: enter "ram
$ba $ff"). Exit the debugger again. 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.
Step 10: 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!)
Step 11: 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.
Step 12: Once in the debugger, press Enter to refresh your prompt. The PC
should be at address $f238, instruction "LDA $e1". You want to disassemble
the ROM starting a few addresses before this to find the instruction
that actually caused the write trap, so enter the command "disasm pc-5"
(you may need to press Shift-PageUp afterwards to see the beginning of
the disassembly). Take a look at the first few instructions. Do you see
the one that affects the lives counter? That's right, it's the "DEC $ba"
at location $f236.
Step 13: 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).
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.
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.
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, starting at address $f236, so our
prompt command looks like:
rom $f236 $ea $ea
The debugger should respond with "changed 02 locations". If you run
"disasm pc-5" again, you should see the two NOPs at $f236 and $f237.
Step 14: Test your patch. First, set location $ba to some number of
lives that can be displayed on the screen ("poke $ba 3" will do). Now
exit the debugger and play the game. You should see 3 lives on the screen.
Step 15: 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.
Step 16: Save your work. In the prompt: "saverom bzhack.bin". You now
have your very own infinite-lives version of Battlezone. The file will
be saved in the current directory (NOT your ROM directory), so you might
want to move it to your ROM directory if it isn't the current directory.
Step 17: 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.
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. Also,
some cartridge types include their own RAM. The debugger doesn't (yet)
know how to access on-cartridge RAM, so you'll have to use the "bank" and
"ram" commands to manually search the address space for the value you're
looking for (future versions of the debugger will be smarter about this).
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 :)
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 :)

760
stella/docs/debugger.html Normal file
View File

@ -0,0 +1,760 @@
<html>
<head>
<title>Stella Debugger</title>
</head>
<body>
<h1>Stella Integrated Debugger (a work in progress)</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>
<h2>Here's a list of what the debugger can do so far:</h2>
<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.</li>
<li>Frame advance (automatic breakpoint at beginning of next frame)
You can advance multiple frames with one command.</li>
<li>Disassembly</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>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>Tab completion for commands and symbol names</li>
<li>Graphical editor for RIOT 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 switch banks. There's still more to be done here though.</li>
<li>Registers/memory that get changed by the CPU during debugging are
highlighted when they're displayed.<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 ("saverom filename.bin").</li>
</ul>
<h2>Future planned features (post 2.0):</h2>
<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>Bankswitch support in the debugger for the few remaining cart types
that aren't supported.</li>
<li>Patch ROM support for a few cart types doesn't work. Must fix.</li>
<li>Some way to control joystick/etc. input from within the debugger.</li>
<li>Source-level debugging: if a DASM .lst file is available, we'll show
the listing in the ROM tab instead of a disassembly. This is already
available in a very crude form ("loadlist" and "list" commands).</li>
<li>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), and the pseudo-variables "_scan" and "_bank" (which
evaluate the the current scanline and bank number). Need to add more TIA,
RIOT registers too. Also, more functions for the builtin function lib.</li>
<li>Possibly a mini-assembler</li>
<li>Support for extra RAM in Supercharger and other cart types.</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>
<h2>How to use the debugger</h2>
<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. 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.</p>
<p>You can also enter the debugger by giving a breakpoint on the command line:
<pre>
; will enter the debugger the first time the instruction at "kernel" runs
stella -break kernel mygame.bin
; $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>
<h2>Prompt tab</h2>
<p>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!</p>
<p>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.</p>
<p>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.</p>
<p>Bash-style tab completion is supported for commands and labels (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 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).</p>
<h4>Status</h4>
<p>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.</p>
<pre>
PC=f000 A=01 X=02 Y=03 S=ff P=21/nv-bdizC Cycle 12345
$f000 a2 ff LDX #ff ; 2
>
</pre>
<p>This is telling us a lot of information:
<pre>
- 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.
</pre>
<h4>Tab completion</h4>
<p>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.</p>
<p>Tab completion works on all labels: built-in, loaded from a symbol file,
or set during debugging with the "define" command. However, it does not
yet work on functions defined with the "function" command, nor does it
work on filenames.</p>
<h4>Expressions</h4>
<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>Prefixes</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>"*"<br>
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. 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>"@"<br>
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>"<"<br>
Take the low byte of a 16-bit value. This has no effect on an 8-bit
value: "a" is equal to "&lt;a". However, "<$1234" equals "$34".</p>
<p>">"<br>
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>"#"<br>
Treat the input as a decimal number.</p>
<p>"$"<br>
Treat the input as a hex number.</p>
<p>"\"<br>
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.</p>
<p>Remember, you can use arbitrarily complex expressions with any
command that takes arguments (except the ones that take filenames,
like "loadsym").</p>
</li>
</ul>
<h3>Breakpoints, watches and traps, oh my!</h3>
<h4>Breakpoints</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>Conditional Breaks</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>There is one annoyance about this complex expression: 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>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, and watches
with the "save" command. If you name your saved file the same
as the ROM filename, it'll be auto-loaded next time you load the
same ROM in Stella. The save file is just a plain text file called
"filename.stella", so you can edit it and add new functions, etc.</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".</p>
<h4>Watches</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 prompt status.</p>
<h4>Traps</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>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... so we don't have a way to run the emulated CPU
in reverse.</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" 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.</p>
<p>Use "listtraps" to see all enabled traps.</p>
<h3>Prompt commands:</h3>
<pre>
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)
</pre>
<p>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").</p>
<h2>CPU Widget</h2>
<p>FIXME: document this</p>
<h2>RAM Widget</h2>
<p>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.</p>
<p>Nearby there are also some buttons to do various things to the
currently-selected memory location. The buttons are:</p>
<pre>
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.
</pre>
<p>This 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>To search 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 'Search' is clicked
and input is empty, all RAM locations are searched.</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 comparitive number (such as -1). Using a '+' or '-' operator
means 'search addresses for values that have changed by that amount'.</p>
<p>The following is an example of inspecting all addresses that have
decreased by 1:
<ul>
<li>Click 'Search' with empty input. All 128 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>The 'Reset' button restarts the whole procedure (ie, clear the highlighted
addresses and allow another search</li>
</ul>
<h2>TIA Tab</h2>
<p>FIXME</p>
<h2>ROM Widget</h2>
<p>FIXME</p>
<h2>Global Buttons</h2>
<p>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, Scan+1 and Exit.</p>
<p>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.</p>
<p>You can also use the Step, Trace, Frame+1 and Scan+1 buttons from anywhere in
the GUI via the keyboard, with Alt-S, Alt-T, Alt-F and Alt-L.</p>
<h2>Tutorial: How to hack a ROM</h2>
<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 Widget, 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 widget and change address $ba to
some high number like $ff (you could use the Prompt instead: enter "ram
$ba $ff"). Exit the debugger again. 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 widget. 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 widget). 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 bzhack.bin". You now
have your very own infinite-lives version of Battlezone. The file will
be saved in the current directory (NOT your ROM directory), so you might
want to move it to your ROM directory if it isn't the current directory.
This can also be accomplished by right-clicking on the ROM widget and
selected 'Save ROM'.</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. Also,
some cartridge types include their own RAM. The debugger doesn't (yet)
know how to access on-cartridge RAM, so you'll have to use the "bank" and
"ram" commands to manually search the address space for the value you're
looking for (future versions of the debugger will be smarter about this).</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>

View File

@ -35,7 +35,7 @@
<br><br><br>
<center><b>February 1999 - (FIXME)February 2005</b></center>
<center><b>February 1999 - September 2005</b></center>
<center><b>The Stella Team</b></center>
<center><b><a href="http://stella.sourceforge.net">Stella Homepage</a></b></center>
@ -230,14 +230,22 @@
(the Windows and Linux frontends) have been discontinued. The OSX
port now uses the launcher as well.</li>
<li>FIXME - add info about integrated debugger</li>
<li>Added an integrated debugger for game developers. This is currently
the first version of a debugger in Stella, but it's already quite
usable.</li>
<li>Further consolidation and integration of SDL. This should lead to
faster operation and a more consistent look for all ports.</li>
<li>Added new sound subsystem, which is much faster and more accurate. Related
to this, added stereo sound output (used by some homebrew games).
<li>Added ZIP support. Stella can now open ROM's compressed in zip
format.</li>
<li>Reworked properties system to use both a system-wide 'stella.pro' and
a per-user 'user.pro' properties files. Changes made by the user
and stored in 'user.pro' are no longer erased when upgrading Stella.</li>
<li>Added support for cartridges with 3E bankswitching format.</li>
<li>Added <b>Alt/Shift-Cmd z, x, c, v, b, n</b> keys to enable/disable the
P0, P1, M0, M1, BL, PL bits in the TIA, respectively.</li>
@ -266,15 +274,22 @@
<li>Removed <b>mergeprops</b> commandline argument, since there are now
dedicated keys to do either save or merge game properties.</li>
<li>Removed <b>grabmouse</b> and <b>hidecursor</b> commandline arguments.
Stella will now automatically decide when to use these settings.
You can still use <b>Ctrl/Cmd g</b> to grab/ungrab the mouse.</li>
<li>Removed <b>hidecursor</b> commandline argument. Stella will now
automatically decide when to use this setting.</li>
<li>Fixed framerate when switching between NTSC and PAL modes. Stella
now uses the correct framerate based on the format of the ROM.</li>
now uses the correct framerate based on the format of the ROM,
in terms of both video <b>and</b> audio.</li>
<li>Added 'configure' support to the build process for both Linux and
Win32 (using MinGW). Developers can now use the familiar 'configure;
make; make install' commands to compile Stella.</li>
<li>Further consolidation and integration of SDL. This should lead to
faster operation and a more consistent look for all ports.</li>
<li>Fixed some 64-bit issues. Stella now compiles and runs correctly
on AMD64 and PPC64 systems.</li>
on AMD64 and PPC64 Linux systems.</li>
<li>Updated the Stella manual with pictures of the new integrated GUI.</li>
</ul>
@ -284,8 +299,8 @@
<ul>
<li>High speed emulation using optimized C++ code</li>
<li>Supports high quality sound emulation using Ron Fries' TIA Sound Emulation
library</li>
<li>Supports high quality sound emulation using code derived from Ron Fries'
TIA Sound Emulation library, including stereo sound support</li>
<li>Emulates the Atari 2600 Joystick Controllers using your computer's keyboard
or joysticks</li>
<li>Emulates the Atari 2600 Keyboard Controllers using your computer's keyboard</li>
@ -349,7 +364,7 @@
<ul>
<li>Linux Kernel 2.4.x, Linux Kernel 2.6.x is highly recommended</li>
<li>GNU C++ compiler version 2.95 and the make utility are required for compiling
the Stella source code; GNU C++ compiler version 3.2.x or later is highly
the Stella source code; GNU C++ compiler version 3.2.x/4.x or later is highly
recommended</li>
<li>Pentium class machine required (Stella <b>may</b> compile on other
architectures, but it hasn't been extensively tested)</li>
@ -364,6 +379,8 @@
<li>Mac OSX 10.1 or Above</li>
<li>500 MHz G4 PPC processor or above (Stella <b>may</b> work with a G3
processor, but this is still a work-in-progress)</li>
<li>OpenGL capable video card. Software rendering mode is still available,
but as of OSX 10.4 is substandard compared to OpenGL.</li>
</ul>
<p>
@ -402,15 +419,14 @@
<ol>
<li>Extract files from the distribution:
<pre> tar zxvf stella-release-src.tar.gz</pre></li>
<li>Change directories to the stella-<i>release</i>/src/build directory</li>
<li>Edit the <b>makefile</b> to meet your needs</li>
<li>Change directories to the stella-<i>release</i> directory</li>
<li>Configure the build with the following command:
<pre> ./configure (--help for list of options)</pre></li>
<li>Build the executable with the following command:
<pre> make linux (or make linux-gl)</pre></li>
<li>Copy the executable to a directory that's in your path:
<pre> cp stella /usr/local/bin</pre></li>
<li>Copy the supplied stella.pro properties file to the system-wide location:
<pre> cp stella.pro /etc</pre> or to your home directory:
<pre> mkdir ~/.stella<br> cp stella.pro ~/.stella</pre></li>
<pre> make</pre></li>
<li>Install the executable with the following command:
<pre> make install</pre></li>
</ol>
<li><b>Source RPM</b> (stella-<i>release</i>-1.src.rpm)</li>
<ol>
@ -442,19 +458,11 @@
<li>Extract files from the distribution using <b>Winzip</b>,
<b>Total Commander</b>, or some other archiving program that supports
gzipped tar files</li>
<li>If compiling the Stella executable using MinGW:</li>
<ul>
<li>Change directories to the stella-<i>release</i>/src/build
directory</li>
<li>Edit the <b>makefile</b> to meet your needs</li>
<li>Build the executable with the following command:
<pre> make win32 (or make win32-gl)</pre></li>
<li>Copy the <b>Stella</b> (commandline executable) and <b>stella.pro</b>
files to some directory</li>
</ul>
<li>If compiling the Stella executable using MinGW, use the same commands
as specified under <i>Linux/UNIX - Compressed tarball</i></li>
<li>If compiling using Visual C++ 7:</li>
<ul>
<li>Open the <b>stella-<i>release</i>/src/win32/Stella_Emulator.sln</b>
<li>Open the <b>stella-<i>release</i>/src/win32/Stella.sln</b>
file using Visual C++ 7</li>
<li>Build the 'Stella' solution</li>
<li>Copy the <b>Stella.exe</b> and <b>stella.pro</b> files
@ -562,8 +570,8 @@
<img src="graphics/rom_browser.png"></p>
<p>Once a ROM directory has been selected, clicking 'Reload' will update the
ROM listing. You can start emulation by selecting a ROM and pressing 'Enter',
clicking 'Play' or double-clicking a ROM.</p>
ROM listing. You can start emulation by selecting a ROM and pressing 'Enter'
or clicking 'Play', or double-clicking a ROM.</p>
<p>
<h2><b>Command Line</b></h2>
@ -589,12 +597,6 @@
<td>Use SDL software or OpenGL rendering mode.</td>
</tr>
<tr>
<td><pre>-video_driver &lt;windib|directx&gt;</pre></td>
<td>Use the specified video driver. Windows works best with 'windib'.
Currently, only Windows makes use of this argument.</td>
</tr>
<tr>
<td><pre>-gl_filter &lt;nearest|linear&gt;</pre></td>
<td>OpenGL mode only. Use GL_NEAREST or GL_LINEAR filtering.
@ -627,6 +629,11 @@
<td>Play the game in fullscreen mode.</td>
</tr>
<tr>
<td><pre>-center &lt;0|1&gt;</pre></td>
<td>Centers game window (if possible).</td>
</tr>
<tr>
<td><pre>-grabmouse &lt;0|1&gt;</pre></td>
<td>Keeps the mouse in the game window.</td>
@ -645,8 +652,13 @@
</tr>
<tr>
<td><pre>-sound &lt;0|1&gt;</pre></td>
<td>Disable or enable sound.</td>
<td><pre>-sound &lt;1|0&gt;</pre></td>
<td>Enable or disable sound generation.</td>
</tr>
<tr>
<td><pre>-channels &lt;1|2&gt;</pre></td>
<td>Enable mono or stereo sound.</td>
</tr>
<tr>
@ -670,12 +682,6 @@
<td>Shows some game info while Stella is running.</td>
</tr>
<tr>
<td><pre>-mergeprops &lt;0|1&gt;</pre></td>
<td>Save the current properties to a separate file in the users' home directory,
or merge them into the users' stella.pro file.</td>
</tr>
<tr>
<td><pre>-accurate &lt;0|1&gt;</pre></td>
<td>Linux only, may be removed in future versions. Use this when
@ -723,6 +729,31 @@
<th>Description</th>
</tr>
<tr>
<td><pre>-debugheight &lt;number&gt;</pre></td>
<td>Set height of debugger in lines of text (currently not 100% working)</td>
</tr>
<tr>
<td><pre>-debug</pre></td>
<td>Immediately jump to debugger mode when starting Stella.</td>
</tr>
<tr>
<td><pre>-holdreset</pre></td>
<td>Start the emulator with the Game Reset switch held down.</td>
</tr>
<tr>
<td><pre>-holdselect</pre></td>
<td>Start the emulator with the Game Select switch held down.</td>
</tr>
<tr>
<td><pre>-holdbutton0</pre></td>
<td>Start the emulator with the left joystick button held down.</td>
</tr>
<tr>
<td><pre>-pro &lt;props file&gt;</pre></td>
<td>Use the given properties file instead of stella.pro.</td>
@ -1483,7 +1514,7 @@
<tr>
<td>Toggle fullscreen/windowed mode</td>
<td>Alt + Enter</td>
<td>Shift-Cmd + Enter(FIXME)</td>
<td>Cmd + Enter</td>
</tr>
<tr>
@ -1585,7 +1616,9 @@
<p><h1>
<a name="Debugger">10. Integrated Debugger</a></h1>
<hr>
FIXME - add info and snapshots for debugger
<p>Have a look at <a href="debugger.html">this page</a> for integrated debugger
documentation.
<br><br><br>
<p><h1>
@ -1597,20 +1630,27 @@ FIXME - add info and snapshots for debugger
You must use the included <b>stella.pro</b> file. Otherwise, the emulator
will try to guess the best settings for the game.</p>
<p>As of Stella 2.0, the properties file handling has changed. Stella now uses
two properties files; a system-wide <b>stella.pro</b> and a per-user <b>user.pro</b>.
The system-wide file will not be modified by Stella, and is used as a default
database of properties. The per-user file will contain all properties 'merged' by
the user (by pressing Alt-s while a game is running). So this means that when
you upgrade Stella and stella.pro, your personal profile settings are preserved.</p>
<p>
<h2><b>Linux/Unix</b></h2>
<p>The Linux and Unix versions of Stella looks for the property file
in your <i>$HOME/.stella</i> directory. If this file is not found there,
Stella will look in the <i>/etc/</i> directory.
<p>The Linux and Unix versions of Stella looks for the system properties file
in <i>/etc/stella.pro</i> and your personal properties file in
<i>$HOME/.stella/user.pro</i>.
<p>
<h2><b>Macintosh</b></h2>
<p>The Mac version of Stella looks for the property file in the
<p>The Mac version of Stella looks for the properties file(s) in the
directory containing the application.</p>
<p>
<h2><b>Windows</b></h2>
<p>The Windows version of Stella looks for the property file in the same directory
<p>The Windows version of Stella looks for the properties file(s) in the directory
containing the application. Future versions of Stella for Windows may look in
user-specific locations (C:\Documents and Settings\...).</p>
@ -1698,6 +1738,14 @@ FIXME - add info and snapshots for debugger
Stella will probably allow the user to view this information.</td>
</tr>
<tr>
<td VALIGN="TOP"><i>Cartridge.Sound:</i></td>
<td>This property determines if the game should use 1 or 2 channels for
sound output. All original Atari 2600 machines supported 1 channel only,
but some homebrew games have been written to take advantage of stereo
sound. The value of this property must be Mono or Stereo.</td>
</tr>
<tr>
<td VALIGN="TOP"><i>Cartridge.Type:</i></td>
<td>This property indicates the bank-switching type for the game.
@ -2028,7 +2076,9 @@ FIXME - add info and snapshots for debugger
<td VALIGN="TOP">Brian&nbsp;Watson</td>
<td>Helped with getting the illegal CPU instruction support working with Stella.
Brian also submitted a number of other changes, such as debugger support, which
will be integrated into the 1.5 release of Stella.</td>
have been finally integrated into the 2.0 release of Stella. Without
a doubt, there would be no debugger support in Stella if not for the
tireless work of Brian.</td>
</tr>
<tr>

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferGL.cxx,v 1.41 2005-09-06 19:42:35 stephena Exp $
// $Id: FrameBufferGL.cxx,v 1.42 2005-09-13 18:27:42 stephena Exp $
//============================================================================
#ifdef DISPLAY_OPENGL
@ -254,8 +254,6 @@ void FrameBufferGL::preFrameUpdate()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::postFrameUpdate()
{
// Do the following twice, since OpenGL mode is double-buffered,
// and we need the contents placed in both buffers
if(theRedrawTIAIndicator || myDirtyFlag)
{
// Texturemap complete texture to surface so we have free scaling
@ -272,7 +270,7 @@ void FrameBufferGL::postFrameUpdate()
glTexCoord2f(myTexCoord[0], myTexCoord[3]); glVertex2i(0, h);
glEnd();
// Now show all changes made to the textures
// Now show all changes made to the texture
SDL_GL_SwapBuffers();
myDirtyFlag = false;

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: DebuggerParser.cxx,v 1.76 2005-08-20 18:19:52 urchlay Exp $
// $Id: DebuggerParser.cxx,v 1.77 2005-09-13 18:27:42 stephena Exp $
//============================================================================
#include "bspf.hxx"
@ -1534,7 +1534,7 @@ void DebuggerParser::executeRom() {
// "run"
void DebuggerParser::executeRun() {
debugger->saveOldState(); // FIXME - why is this here?
debugger->saveOldState();
debugger->quit();
commandResult = "exiting debugger";
}

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: ContextMenu.cxx,v 1.1 2005-08-31 19:15:10 stephena Exp $
// $Id: ContextMenu.cxx,v 1.2 2005-09-13 18:27:42 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -75,8 +75,15 @@ const string& ContextMenu::getSelectedString() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ContextMenu::handleMouseDown(int x, int y, int button, int clickCount)
{
// Only do a selection when the left button is in the dialog
if(button == 1)
sendSelection();
{
x += getAbsX(); y += getAbsY();
if(x >= _x && x <= _x+_w && y >= _y && y <= _y+_h)
sendSelection();
else
parent()->removeDialog();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -105,6 +112,9 @@ void ContextMenu::handleKeyDown(int ascii, int keycode, int modifiers)
{
switch(keycode)
{
case 27: // escape
parent()->removeDialog();
break;
case '\n': // enter/return
case '\r':
sendSelection();

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: RomWidget.cxx,v 1.3 2005-09-07 18:34:52 stephena Exp $
// $Id: RomWidget.cxx,v 1.4 2005-09-13 18:27:42 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -64,7 +64,7 @@ void RomWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
switch(cmd)
{
case kListScrolledCmd:
incrementalUpdate();
incrementalUpdate(data, myRomList->rows());
break;
case kListItemChecked:
@ -92,21 +92,25 @@ void RomWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::loadConfig()
{
cerr << " ==> RomWidget::loadConfig()\n";
Debugger& dbg = instance()->debugger();
// Only reload full bank when necessary
// if(myFirstLoad || myCurrentBank != instance()->debugger().getBank())
// FIXME - always do a full reload for now, will optimize later
if(true)
if(myFirstLoad || myCurrentBank != instance()->debugger().getBank())
{
initialUpdate();
myFirstLoad = false;
}
else // only reload what's in current view
{
incrementalUpdate();
incrementalUpdate(myRomList->currentPos(), myRomList->rows());
}
myCurrentBank = dbg.getBank();
// Update romlist to point to current PC
int pc = instance()->debugger().cpuDebug().pc();
int pc = dbg.cpuDebug().pc();
AddrToLine::iterator iter = myLineList.find(pc);
if(iter != myLineList.end())
myRomList->setHighlighted(iter->second);
@ -117,8 +121,6 @@ void RomWidget::initialUpdate()
{
Debugger& dbg = instance()->debugger();
myCurrentBank = dbg.getBank();
// Fill romlist the current bank of source or disassembly
if(mySourceAvailable)
; // FIXME
@ -131,7 +133,7 @@ void RomWidget::initialUpdate()
StringList label, data, disasm;
BoolArray state;
// Disassemble entire bank (up to 4096 lines)
// Disassemble entire bank (up to 4096 lines) and invalidate all lines
dbg.disassemble(myAddrList, label, data, disasm, 0xf000, 4096);
for(unsigned int i = 0; i < data.size(); ++i)
state.push_back(false);
@ -146,7 +148,7 @@ void RomWidget::initialUpdate()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomWidget::incrementalUpdate()
void RomWidget::incrementalUpdate(int line, int rows)
{
}

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: RomWidget.hxx,v 1.2 2005-09-07 18:34:52 stephena Exp $
// $Id: RomWidget.hxx,v 1.3 2005-09-13 18:27:42 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -47,7 +47,7 @@ class RomWidget : public Widget, public CommandSender
private:
void initialUpdate();
void incrementalUpdate();
void incrementalUpdate(int line, int rows);
void setBreak(int data);
void setPC(int data);
@ -63,6 +63,10 @@ class RomWidget : public Widget, public CommandSender
/** List of line numbers indexed by address */
AddrToLine myLineList;
/** Indicates whether a given line is valid or not;
Invalid lines need to be disassembled again */
BoolArray myLineValid;
bool myFirstLoad;
bool mySourceAvailable;
int myCurrentBank;

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Settings.cxx,v 1.59 2005-09-11 15:44:51 stephena Exp $
// $Id: Settings.cxx,v 1.60 2005-09-13 18:27:42 stephena Exp $
//============================================================================
#include <cassert>
@ -39,7 +39,6 @@ Settings::Settings(OSystem* osystem)
// Now fill it with options that are common to all versions of Stella
set("video", "soft");
set("video_driver", "");
set("gl_filter", "nearest");
set("gl_aspect", "2.0");
@ -47,8 +46,8 @@ Settings::Settings(OSystem* osystem)
set("zoom", "2");
set("fullscreen", "false");
set("grabmouse", "false");
set("center", "true");
set("grabmouse", "false");
set("palette", "standard");
set("debugheight", "0");
@ -255,7 +254,6 @@ void Settings::usage()
<< " soft SDL software mode\n"
#ifdef DISPLAY_OPENGL
<< " gl SDL OpenGL mode\n"
<< " -video_driver <type> SDL Video driver (see manual).\n"
<< endl
<< " -gl_filter <type> Type is one of the following:\n"
<< " nearest Normal scaling (GL_NEAREST)\n"
@ -266,22 +264,19 @@ void Settings::usage()
#endif
<< " -zoom <size> Makes window be 'size' times normal\n"
<< " -fullscreen <1|0> Play the game in fullscreen mode\n"
<< " -grabmouse <1|0> Keeps the mouse in the game window\n"
<< " -center <1|0> Centers game window (if possible)\n"
<< " -grabmouse <1|0> Keeps the mouse in the game window\n"
<< " -palette <original| Use the specified color palette\n"
<< " standard|\n"
<< " z26>\n"
<< " -framerate <number> Display the given number of frames per second\n"
<< " -debug <1|0> Start in the debugger\n"
<< " -debugheight <number> Set height of debugger in lines of text (NOT pixels)\n"
<< " -holdreset Start the emulator with the Game Reset switch held down\n"
<< " -holdselect Start the emulator with the Game Select switch held down\n"
<< " -holdbutton0 Start the emulator with the left joystick button held down\n"
<< endl
#ifdef SOUND_SUPPORT
<< " -sound <1|0> Enable sound generation\n"
<< " -channels <1|2> Enable mono or stereo sound\n"
<< " -fragsize <number> The size of sound fragments (must be a power of two)\n"
<< " -volume <number> Set the volume (0 - 100)\n"
<< endl
#endif
<< " -paddle <0|1|2|3> Indicates which paddle the mouse should emulate\n"
<< " -showinfo <1|0> Shows some game info\n"
@ -292,6 +287,7 @@ void Settings::usage()
<< " -ssdir <path> The directory to save snapshot files to\n"
<< " -ssname <name> How to name the snapshot (romname or md5sum)\n"
<< " -sssingle <1|0> Generate single snapshot instead of many\n"
<< endl
#endif
<< " -listrominfo Display contents of stella.pro, one line per ROM entry\n"
<< " -help Show the text you're now reading\n"
@ -299,6 +295,12 @@ void Settings::usage()
<< " The following options are meant for developers\n"
<< " Arguments are more fully explained in the manual\n"
<< endl
<< " -debugheight <number> Set height of debugger in lines of text (NOT pixels)\n"
<< " -debug Start in debugger mode\n"
<< " -holdreset Start the emulator with the Game Reset switch held down\n"
<< " -holdselect Start the emulator with the Game Select switch held down\n"
<< " -holdbutton0 Start the emulator with the left joystick button held down\n"
<< endl
<< " -pro <props file> Use the given properties file instead of stella.pro\n"
<< " -type <arg> Sets the 'Cartridge.Type' property\n"
<< " -ld <arg> Sets the 'Console.LeftDifficulty' property\n"

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: ListWidget.cxx,v 1.30 2005-09-07 18:34:52 stephena Exp $
// $Id: ListWidget.cxx,v 1.31 2005-09-13 18:27:42 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -147,6 +147,7 @@ void ListWidget::scrollBarRecalc()
_scrollBar->recalc();
setDirty(); draw();
sendCommand(kListScrolledCmd, _currentPos, _id);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -366,20 +367,16 @@ void ListWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ListWidget::scrollToCurrent(int item)
{
bool scrolled = false;
// Only do something if the current item is not in our view port
if (item < _currentPos)
{
// it's above our view
_currentPos = item;
scrolled = true;
}
else if (item >= _currentPos + _rows )
{
// it's below our view
_currentPos = item - _rows + 1;
scrolled = true;
}
if (_currentPos < 0 || _rows > (int)_list.size())
@ -387,12 +384,13 @@ void ListWidget::scrollToCurrent(int item)
else if (_currentPos + _rows > (int)_list.size())
_currentPos = _list.size() - _rows;
int oldScrollPos = _scrollBar->_currentPos;
_scrollBar->_currentPos = _currentPos;
_scrollBar->recalc();
setDirty(); draw();
if(scrolled)
if(oldScrollPos != _currentPos)
sendCommand(kListScrolledCmd, _currentPos, _id);
}

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: ListWidget.hxx,v 1.12 2005-08-26 16:44:17 stephena Exp $
// $Id: ListWidget.hxx,v 1.13 2005-09-13 18:27:42 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -48,6 +48,9 @@ class ListWidget : public EditableWidget
int x, int y, int w, int h);
virtual ~ListWidget();
int rows() const { return _rows; }
int currentPos() const { return _currentPos; }
int getSelected() const { return _selectedItem; }
void setSelected(int item);

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SettingsWin32.cxx,v 1.17 2005-08-24 01:07:37 stephena Exp $
// $Id: SettingsWin32.cxx,v 1.18 2005-09-13 18:27:42 stephena Exp $
//============================================================================
#include <sstream>
@ -28,9 +28,8 @@
SettingsWin32::SettingsWin32(OSystem* osystem)
: Settings(osystem)
{
set("video_driver", "directx"); // This seems to be much faster than DirectX
set("fragsize", "2048"); // Anything less than this usually causes sound skipping
set("video", "hard"); // Use software mode with hardware surface
set("fragsize", "2048"); // Anything less than this usually causes sound skipping
set("video", "hard"); // Use software mode with hardware surface
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -