#!/usr/bin/perl -w # Performance tester for the Atari800 emulator # by Piotr Fusik use strict; # defaults my $test = ''; my $target = ''; my $cflags = ''; my $default_cflags = '-O2 -Wall'; my @features = (); my $reference_program = 'dc.xex'; # "Drunk Chessboard" my $frames = 3 * 60 * 50; # 3 Atari minutes # note that "Drunk Chessboard" ends after 3 minutes my $output = ''; # create 'benchmark' directory under 'src/' sub make_directory() { unless (-d 'benchmark') { print "'benchmark' directory does not exist, creating\n"; mkdir 'benchmark' or die "Failed to create 'benchmark' directory\n"; } } # runs a command (the output goes to the console) sub run_command(@) { print "Running: @_\n"; system @_ and die "$_[0] failed\n"; } # runs a command and captures its output sub pipe_command(@) { print "Running: @_\n"; my $result = `@_`; die "$_[0] failed\n" if $?; return $result; } # addresses of Atari hardware registers my @hwregs = ( 0xd000 .. 0xd01f, # GTIA 0xd200 .. 0xd21f, # POKEY (stereo) 0xd300 .. 0xd303, # PIA 0xd400 .. 0xd40f # ANTIC ); # built-in Atari programs my %programs = ( # yes, I really wrote them here directly in the machine language 'blank.xex' => "\xFF\xFF\x00\x06\x39\x06" . "\x78\xEE\x0E\xD4\xEE\x00\xD4" . "\x8D\x0A\xD4" x 16 . "\x4C\x07\x06" . "\xE0\x02\xE1\x02\x00\x06", 'incD01A.xex' => "\xFF\xFF\x00\x06\x39\x06" . "\x78\xEE\x0E\xD4\xEE\x00\xD4" . "\xEE\x1A\xD0" x 16 . "\x4C\x07\x06" . "\xE0\x02\xE1\x02\x00\x06", 'flash.xex' => "\xFF\xFF\x00\x06\x1B\x06" . "\x78\xA9\x00\x8D\x0E\xD4\x8D\x00\xD4" . "\x8D\x0A\xD4\x8D\x0A\xD4" . "\xAE\x0B\xD4\xD0\xF5" . "\x8D\x1A\xD0\x49\xA4" . "\x4C\x09\x06" . "\xE0\x02\xE1\x02\x00\x06", 'ramread.xex' => "\xFF\xFF\x00\x06" . pack('v', 0x060b + 3 * @hwregs) . "\x78\xA9\x00\x8D\x0E\xD4\x8D\x00\xD4" . "\xAD\xFF\x05" x @hwregs . "\x4C\x09\x06" . "\xE0\x02\xE1\x02\x00\x06", 'ramstore.xex' => "\xFF\xFF\x00\x06" . pack('v', 0x060b + 3 * @hwregs) . "\x78\xA9\x00\x8D\x0E\xD4\x8D\x00\xD4" . "\x8D\xFF\x05" x @hwregs . "\x4C\x09\x06" . "\xE0\x02\xE1\x02\x00\x06", 'hwread.xex' => "\xFF\xFF\x00\x06" . pack('v', 0x060b + 3 * @hwregs) . "\x78\xA9\x00\x8D\x0E\xD4\x8D\x00\xD4" . join('', map("\xAD" . pack('v', $_), @hwregs)) . "\x4C\x09\x06" . "\xE0\x02\xE1\x02\x00\x06", 'hwstore.xex' => "\xFF\xFF\x00\x06" . pack('v', 0x060b + 3 * @hwregs) . "\x78\xA9\x00\x8D\x0E\xD4\x8D\x00\xD4" . join('', map("\x8D" . pack('v', $_), @hwregs)) . "\x4C\x09\x06" . "\xE0\x02\xE1\x02\x00\x06" ); # write a built-in program to a file sub generate_program($) { my $program = shift; print "Generating $program\n"; open XEX, ">benchmark/$program" and binmode XEX and print XEX $programs{$program} and close XEX or die "$!\n"; } # we will be working in the src directory if (-e 'atari.c') { # ok } elsif (-e '../src/atari.c') { # script was run from the util/ directory chdir '../src' or die "Can't chdir to '../src'\n"; } else { die "atari.c not found\n"; } # supported targets my @targets = qw( default falcon windx x11 x11-motif x11-shm x11-xview x11-xview-shm motif shm xview xview-shm ); my $help_me = 0; # get command line options for (@ARGV) { if (/^--test=(.+)/) { $test = $1; } elsif (/^--target=(.+)/) { $target = $1; grep $target eq $_, @targets or die "$target is not a valid target\n"; } elsif (/^--cflags=(.+)/) { $cflags = $1; } elsif (/^--program=(.+)/) { $reference_program = $1; } elsif (/^--frames=(\d+)$/) { $frames = $1; } elsif (/^--output=(.+)/) { $output = $1; } elsif ($_ eq '--generate-programs') { make_directory(); generate_program($_) for sort keys %programs; exit; } elsif (/^-?-h(elp)?$/) { $help_me = 1; } else { push @features, $_; } } # gfx-generating target: windx on Win32, default otherwise my $gfx_target = 'default'; if ($^O =~ /win/i) { $gfx_target = 'windx'; } # must initialize this after parsing the command line # in order to fill in $reference_program my %tests = ( 'default' => { 'target' => $gfx_target, 'cflags' => '-D DONT_DISPLAY', 'run' => [ $reference_program, 'blank.xex' ] }, 'basic' => { 'target' => 'default', 'run' => [ $reference_program, 'blank.xex' ] }, 'monitorbreak' => { 'target' => 'default', 'config' => [ '--disable-monitorbreak', '--enable-monitorbreak' ], 'run' => [ $reference_program ], }, 'pagedattrib' => { 'target' => 'default', 'config' => [ '--disable-pagedattrib', '--enable-pagedattrib' ], 'run' => [ $reference_program, 'ramread.xex', 'ramstore.xex', 'hwread.xex', 'hwstore.xex' ], }, 'cycleexact' => { 'target' => $gfx_target, 'cflags' => '-D DONT_DISPLAY', 'config' => [ '--disable-newcycleexact', '--enable-newcycleexact' ], 'run' => [ $reference_program, 'incD01A.xex' ] }, 'display' => { 'target' => $gfx_target, 'run' => [ $reference_program, 'blank.xex', 'flash.xex' ] } ); if (@ARGV == 0 || $help_me) { # display help and exit print < Choose test (required) --target= Choose Atari800 target for the test --cflags="" Override CFLAGS (default: "-O2 -Wall" + test-specific) --program= Choose Atari program to be run (defaults to $reference_program) --frames= Set number of frames to be run (defaults to $frames) --output= Output the results to the specified file --generate-programs Just generate all the built-in Atari programs Any other options are passed to the configure script. Use it to affect the set of optional features and external libraries used during the test. Available tests: default Compare chosen Atari program with one that does nothing, using default configuration (may be modified with "--enable-", "--disable-") (default target: $gfx_target) basic Compare chosen Atari program with one that does nothing (default target: default) monitorbreak Compare configurations with/without MONITOR_BREAK (default target: default) pagedattrib Compare configurations with/without PAGED_ATTRIB (default target: default) cycleexact Compare configurations with/without NEW_CYCLE_EXACT (default target: $gfx_target) display Compare display performance with different Atari programs (default target: $gfx_target) Available Atari800 targets: @targets[0..5] @targets[6..11] EOF exit; } unless (exists $tests{$test}) { die "$test is not an available test\n"; } # autoconf stuff unless (-e 'config.h.in') { print "'config.h.in' not found\n"; run_command('autoheader'); } unless (-e 'configure') { print "'configure' not found\n"; run_command('autoconf'); } # create our directory under 'src/' make_directory(); # create Atari800 config file with ROM paths unless (-r 'benchmark/atari800.cfg') { print "'benchmark/atari800.cfg' does not exist, creating\n"; open CFG, '>benchmark/atari800.cfg' and print CFG <) { m!^(?:XL/XE|BASIC)_ROM=(.*?)\s*$! or next; my $romfile = $1; unless (-r $romfile) { die <$output" or die "$!\n"; print "Running test: $test\n"; print OUT "Test: $test\n"; # get test settings my $test_settings = $tests{$test}; $target ||= $test_settings->{'target'}; $cflags ||= exists($test_settings->{'cflags'}) ? $default_cflags . ' ' . $test_settings->{'cflags'} : $default_cflags; $cflags .= ' -D BENCHMARK=' . $frames; $ENV{'CFLAGS'} = $cflags; print "Using CFLAGS: $cflags\n"; print OUT "CFLAGS=$cflags\n"; # compile each configuration my @configs = $test_settings->{'config'} ? @{$test_settings->{'config'}} : (''); for my $config (@configs) { print '-' x 76, "\n"; # "@{[]}" trick in this print is to avoid two consecutive spaces when @features is empty print OUT "./configure --target=$target --without-sound @{[@features, $config]}\n"; run_command('sh', './configure', "--target=$target", '--without-sound', @features, split(' ', $config)); run_command('make', 'clean'); run_command('make'); # run each program for my $program (@{$test_settings->{'run'}}) { if (-r $program) { # ok } elsif (-r "benchmark/$program") { $program = "benchmark/$program"; } elsif (exists $programs{$program}) { generate_program($program); $program = "benchmark/$program"; } else { die "$program does not exist\n"; } my $result = pipe_command('./atari800', '-config', 'benchmark/atari800.cfg', $program); print $result; # parse result $result =~ /\d+ frames emulated in ([0-9.]+) seconds/ or die "Expected 'frames emulated in'\n"; my $speed_msg = "$1 seconds"; # avoid division by zero if ($1 != 0) { # assuming PAL, real Atari needs (0.02 * $frames) time $speed_msg .= sprintf ' (%d%% of real Atari speed)', 100 * 0.02 * $frames / $1; } print "$speed_msg\n\n"; printf OUT "./atari800 %-23s # %s\n", $program, $speed_msg; } } print OUT "Test complete.\n"; close OUT;