doc | ||
.gitignore | ||
champ.rb | ||
empty | ||
p65c02.c | ||
pgif.c | ||
plot3d.yaml | ||
README.md |
Champ - N. Harold Cham's 65C02 Profiler
This is a 6502/65C02 emulator / profiler that enables you to really get to know your APPLE ][ HiRes Graphics Mode Demo.
Features
- full, cycle-accurate 65C02 emulation
- screen output as animated GIF with exact frame timing
- average frame rate
- see how much time is spent in which subroutine
- watch variables (single variables or pairs)
- no dependencies except Ruby, gcc and Merlin32
Usage
First, make sure you have gcc, ruby and Merlin32 installed. You need to prepare a YAML file to tell champ about all source and object files and their memory locations.
Take plot3d.yaml
for example (using Marc A. Golombeck's excellent 3D-Demo):
load:
0x6000: plot3d/plot3d242.s
0x1200: plot3d/multtab.s
0x8400: plot3d/ldrwtab.s
0x8900: plot3d/SINETABLE
0x8b00: plot3d/object2.s
0x9000: plot3d/object1.s
0x9500: plot3d/FONT
0xb600: plot3d/projtab.s
entry: ENTRY
instant_rts:
- LOAD1
We specified some source files (which will get compiled automatically) and some object files along with their locations in memory (load
). We also specified the entry point for our program (entry
), this can be a label or an address.
Furthermore, we can disable subroutines by replacing the first opcode with a RTS (instant_rts
). This is necessary in some cases because Champ does not emulate hardware and thus can not load data from disk, for example.
Defining watches
You can watch registers or variables at certain program counter addresses by inserting a champ directive in a comment after the respective code line in your assembler source code. All champ directives start with an at sign (@). Here's an example:
LDA #2 ; load 2 into accumulator
ASL ; multiply by two
STA SOMEWHERE ; store result @Au
With the @Au
directive, we tell champ to monitor the A register and interpret it as an unsigned 8 bit integer (likewise, @As
would treat the value as a signed 8 bit integer).
By default, all champ values get recorded before the operation has been executed. To get the value after the operation, you can write: @Au(post)
.
Running the profiler
To start champ, type:
$ ./champ.rb --max-frames 100 plot3d.yaml
This will run the emulator and write the HTML report to report.html
. If you do not specify the maximum number of frames, you can still cancel the emulator by pressing Ctrl+C at any time. If you need fast results and don't need the animated GIF of all frames, specify the --no-animation
flag, which will still give you all the information but without the animation.
Example report
Did you know?
By the way, there's a full-fledged, incremental, standalone, no-dependencies GIF encoder in pgif.c
that writes animated GIFs and uses some optimizations to further minimize space. It's stream-friendly and as you feed pixels in via stdin
, it dutifully writes GIF data to stdout
until stdin
gets closed.