practice
Doi: 10.1145/1735223.1735240
article development led by
queue.acm.org
An essential technique used in emulator
development is a useful addition to any
programmer’s toolbox.
BY PeteR PhiLLiPs
enhanced
Debugging
with traces
CreaTiNG aN eMULaTor to run old programs is a
difficult task. You need a thorough understanding
of the target hardware and the correct functioning
of the original programs that the emulator is to
execute. In addition to being functionally correct,
the emulator must hit a performance target of
running the programs at their original real-time
speed. Reaching these goals inevitably requires a
considerable amount of debugging. The bugs—
which for players of old arcade games are a vital
part of recreating the original experience—are often
subtle errors in the emulator itself but could also
be a misunderstanding of the target hardware or an
actual known bug in the original program. (It is also
possible the binary data for the original program
has become subtly corrupted or is not the version
expected.) Solving these problems requires some
unusual debugging techniques.
The debugger of the host system
can be used for the target (emulated)
system by setting a breakpoint in the
CPU do-instruction loop and examining the variables that hold the
contents of the emulated CPU registers and main memory. This works for
small and easily localized problems
such as failures in the self-test or ini-tialization code but is not of much use
for problems that happen at a later
stage in the execution. At that point
the organization of the program is not
apparent. No debug symbol or source
code is available to allow a high-level
approach to the problem. More powerful techniques are required and are
built into the emulator itself.
Obviously there is enormous variability in the kinds of bugs that may
present themselves. Ignoring major
execution path failures (for example,
infinite loop, the program counter
ending up in nonprogram memory),
a typical failure would be noticed in