use strict;
use warnings;
use Test::More 'no_plan';
BEGIN {
use_ok( 'Acme::6502' );
}
my %test_lut = (
m => sub {
return shift->read_8( hex shift );
},
ps => sub {
return shift->get_p;
},
pc => sub {
return shift->get_pc;
},
sp => sub {
return shift->get_s;
},
acc => sub {
return shift->get_a;
},
ix => sub {
return shift->get_x;
},
iy => sub {
return shift->get_y;
},
s => sub {
return $_[0]->get_p & $_[0]->N ? 1 : 0;
},
v => sub {
return $_[0]->get_p & $_[0]->V ? 1 : 0;
},
b => sub {
return $_[0]->get_p & $_[0]->B ? 1 : 0;
},
d => sub {
return $_[0]->get_p & $_[0]->D ? 1 : 0;
},
i => sub {
return $_[0]->get_p & $_[0]->I ? 1 : 0;
},
z => sub {
return $_[0]->get_p & $_[0]->Z ? 1 : 0;
},
c => sub {
return $_[0]->get_p & $_[0]->C ? 1 : 0;
},
);
my %regset_lut = (
ps => sub {
shift->set_p( shift );
},
pc => sub {
shift->set_pc( shift );
},
sp => sub {
shift->set_s( shift );
},
acc => sub {
shift->set_a( shift );
},
ix => sub {
shift->set_x( shift );
},
iy => sub {
shift->set_y( shift );
},
s => sub {
$_[0]->set_p( $_[0]->get_p & ~$_[0]->N );
$_[0]->set_p( $_[0]->get_p | $_[0]->N ) if $_[1];
},
v => sub {
$_[0]->set_p( $_[0]->get_p & ~$_[0]->V );
$_[0]->set_p( $_[0]->get_p | $_[0]->V ) if $_[1];
},
b => sub {
$_[0]->set_p( $_[0]->get_p & ~$_[0]->B );
$_[0]->set_p( $_[0]->get_p | $_[0]->B ) if $_[1];
},
d => sub {
$_[0]->set_p( $_[0]->get_p & ~$_[0]->D );
$_[0]->set_p( $_[0]->get_p | $_[0]->D ) if $_[1];
},
i => sub {
$_[0]->set_p( $_[0]->get_p & ~$_[0]->I );
$_[0]->set_p( $_[0]->get_p | $_[0]->I ) if $_[1];
},
z => sub {
$_[0]->set_p( $_[0]->get_p & ~$_[0]->Z );
$_[0]->set_p( $_[0]->get_p | $_[0]->Z ) if $_[1];
},
c => sub {
$_[0]->set_p( $_[0]->get_p & ~$_[0]->C );
$_[0]->set_p( $_[0]->get_p | $_[0]->C ) if $_[1];
},
);
my $glob = $ENV{TEST_OP} || '*';
my @files = glob( "t/monkeynes/script_${glob}.txt" );
for my $file ( @files ) {
open( my $script, $file ) || die qq(cannot load test script "$file");
_diag( qq(Running script "$file") );
my @lines = <$script>;
chomp( @lines );
run_script( @lines );
close( $script );
}
sub run_script {
my $cpu;
for ( @_ ) {
chomp;
next if m{^\s*$};
next if m{^save};
if ( m{^# (.+)} ) {
_diag( $1 );
}
elsif ( $_ eq 'clear' ) {
next;
}
elsif ( $_ eq 'power on' ) {
$cpu = Acme::6502->new();
$cpu->set_s( 255 );
$cpu->set_p( $cpu->get_p | $cpu->R );
isa_ok( $cpu, 'Acme::6502' );
}
elsif ( $_ eq 'memclear' ) {
$cpu->poke_code( 0, ( 0 ) x 65536 );
_diag( 'Mem cleared' );
}
elsif ( $_ eq 'step' ) {
_diag( 'Running next instruction...' );
$cpu->run( 1 );
}
elsif ( m{^regset (.+) (.+)} ) {
$regset_lut{ lc $1 }->( $cpu, hex $2 );
_diag( "$1 set to $2" );
}
elsif ( m{^regs(?: (.+))?} ) {
diag_regs( $cpu, $1 );
}
elsif ( m{^memset (.+) (.+)} ) {
$cpu->write_8( hex $1, hex $2 );
is( $cpu->read_8( hex $1 ), hex $2, "Mem[$1] set to $2" );
}
elsif ( m{^test (.+) (.+) (.+)} ) {
my ( $op, @args ) = split( /:/, $1 );
my $cmp = $2;
$cmp = '==' if $cmp eq '=';
cmp_ok( $test_lut{ lc $op }->( $cpu, @args ),
$cmp, hex $3, "$1 $2 $3" );
}
elsif ( m{^op (.+)} ) {
my ( $op, $args_hex ) = split( ' ', $1 );
_diag( "OP: $1" );
$args_hex = '' unless defined $args_hex;
my @args = ( $args_hex =~ m{(..)}g );
my $pc = hex( 8000 );
$cpu->poke_code(
$pc,
map { hex( $_ || 0 ) } $op,
@args[ 0 .. 1 ]
);
$cpu->set_pc( $pc );
$cpu->run( 1 );
}
else {
use Data::Dumper;
warn Dumper $_;
}
}
}
sub diag_regs {
my $cpu = shift;
my $reg = uc shift;
_diag( 'CPU Registers' ) if !$reg;
_diag( sprintf ' PC: $%X', $cpu->get_pc )
if !$reg || $reg eq 'PC';
_diag( sprintf ' SP: $%X', $cpu->get_s ) if !$reg || $reg eq 'SP';
_diag( sprintf ' ACC: $%X', $cpu->get_a )
if !$reg || $reg eq 'ACC';
_diag( sprintf ' IX: $%X', $cpu->get_x ) if !$reg || $reg eq 'IX';
_diag( sprintf ' IY: $%X', $cpu->get_y ) if !$reg || $reg eq 'IY';
# this should be fixed to handle just one flag at a time
_diag( ' Flags S V - B D I Z C' )
if !$reg || $reg =~ m{^(PS|[SVBDIZC])$};
_diag(
sprintf ' PS: %d %d %d %d %d %d %d %d',
split( //, sprintf( '%08b', $cpu->get_p ) )
) if !$reg || $reg =~ m{^(PS|[SVBDIZC])$};
}
sub _diag {
return unless $ENV{DIAG_6502};
diag( @_ );
}