[stella] Stella conditional breakpoint support RFC

Subject: [stella] Stella conditional breakpoint support RFC
From: "B. Watson" <atari@xxxxxxxxxxxxxx>
Date: Mon, 11 Jul 2005 19:52:47 -0400
Apologies in advance for dumping this on you, but I really need help...

(Unlike an Internet RFC, this really is a Request for Comments :)

I'm working on adding conditional breakpoints to Stella now.. I've got
a design decision to make, and want to get some input from the list
(since you guys are our users...)

To make conditionals work, I have to implement a full-blown expression
parser (with yacc and either flex or a faster hand-rolled lexer).

Benchmarking the partial parser I've got so far, it's *slow*.

To make arbitrary conditionals work, I've got to do one of two things:

1. Re-parse the conditional(s) before each 6502 instruction executes, or..

2. Write a compiler for the conditionals (a "bytecode" type of compiler,
not a native one, since Stella is meant to be portable), and a bytecode
interpreter (hopefully one that's faster than the lexer/parser is!),
and interpret the compiled conditional(s) before each 6502 instruction.

Either way is going to slow the emulator down horribly. My incomplete
parser uses yacc and a hand-rolled (faster than flex) lexer, and
I've benched it at approx. 400,000 evaluations per second (using
medium-complexity expressions, on my Athlon 2100+ XP). The typical 2600
game executes approx.  350,000 6502 instructions/sec. When just one
conditional breakpoint is set, that means we'd have to emulate 350,000
6502 instructions *and* evaluate 350,000 conditional expressions per
second. I'm pretty sure this means you'll be able to run the emulator
at full speed if you've got a fairly fast box (something like an Athlon
2100+ XP, oddly enough). If you want two or more conditional breaks,
the emulator's going to slow down to a crawl.

If I can manage an expression compiler and an executor that's an order
of magnitude faster, it'll be worth it... but I have a dirty secret:

I've never actually written a compiler before :)

I have some good books on compiler theory (including the "dragon" book)
and I've done lots of "mini-language" stuff in perl (config file parsers,
CLI programs). And I *want* to learn to write a compiler... I'm sure I
could eventually write a lovely expression compiler, but I'm not so sure
the end result would run any faster than the current interpreted version,
unless I spend a year or two gaining experience at practical application
of the theory I only hazily understand...

However, there's an alternative.

What we've been talking about are arbitrary conditions: break if
accumulator = $FF and carry set and location $f0 contains a number
between $0f and $1f, for instance:

break if a==$ff && c && (*$f0 >= $0f || *$f0 <= $0f)

(yes, it's a very C-like syntax...)

Currently, Stella has a decently fast implementation of simple breakpoints
(it's O(1) in fact).  It barely adds any overhead at all to the 6502
emulation, even though it has to check for a breakpoint before every
instruction and a trap after every instruction (though if none are set,
it doesn't have to check for any).

What if I add conditional support to the existing breakpoints (and traps)?

You'd still set a breakpoint on a program address, or a trap on access to
an address... but you could also add a condition that must be met:

break myLabel if a==$ff && c && (*$f0 >= $0f || *$f0 <= $0f)

(this does the same thing as:

break if pc==myLabel && a==$ff && c && (*$f0 >= $0f || *$f0 <= $0f)

...which is a much more restricted condition).

If I do it this way, the 6502 emulation won't slow down much at all:
the existing, fast code could check for breakpoints/traps as it does
now, and only evaluate the condition (if there is one) once it's hit an
address where there actually is a break/trap. I wouldn't have to write
a compiler, and you'd have at least some form of conditional breaks in
weeks instead of sometime next year...

Actually, the first "break if" example could be rewritten as a "trap if":

trap $f0 if a==$ff && c && (*$f0 >= $0f || *$f0 <= $0f)

This means the same as the "break if", but will only be run when location
$f0 is actually accessed.

Of course, there's no reason I couldn't do both: arbitrary conditions that
come at the cost of slowing down the emulator (maybe a lot), or conditions
tied to an address that let the emulator run at (99% of) full speed.

What do you guys think?

(I could be missing something blindingly obvious, too...?)

Archives (includes files) at http://www.biglist.com/lists/stella/archives/
Unsub & more at http://stella.biglist.com

Current Thread