Acme-6502
view release on metacpan or search on metacpan
lib/Acme/6502.pm view on Meta::CPAN
sub _jmp_i_bug {
my $a = shift;
# this should emulate a page boundary bug:
# JMP 0x80FF fetches from 0x80FF and 0x8000
# instead of 0x80FF and 0x8100
my $b = "($a & 0xFF00) | (($a + 1) & 0xFF)";
return '$pc = $mem[' . $a . '] | ($mem[' . $b . '] << 8);' . "\n";
}
sub _jmp {
return _jmp_i( '$pc' );
}
sub _jmpi {
return 'my $w = $mem[$pc] | ($mem[$pc + 1] << 8); '
. _jmp_i_bug( '$w' );
}
sub _jmpix {
return 'my $w = ($mem[$pc] | ($mem[$pc + 1] << 8)) + $x; '
. _jmp_i( '$w' );
}
sub _rti {
return
_pop( '$p' )
. '$p |= R;'
. 'my ($lo, $hi); '
. _pop( '$lo' )
. _pop( '$hi' )
. '$pc = $lo | ($hi << 8);' . "\n";
}
sub _rts {
return
'my ($lo, $hi); '
. _pop( '$lo' )
. _pop( '$hi' )
. '$pc = ($lo | ($hi << 8)) + 1;' . "\n";
}
1;
__END__
=head1 NAME
Acme::6502 - Pure Perl 65C02 simulator.
=head1 VERSION
This document describes Acme::6502 version 0.76
=head1 SYNOPSIS
use Acme::6502;
my $cpu = Acme::6502->new();
# Set start address
$cpu->set_pc(0x8000);
# Load ROM image
$cpu->load_rom('myrom.rom', 0x8000);
# Run for 1,000,000 instructions then return
$cpu->run(1_000_000);
=head1 DESCRIPTION
Imagine the nightmare scenario: your boss tells you about a legacy
system you have to support. How bad could it be? COBOL? Fortran? Worse:
it's an embedded 6502 system run by a family of squirrels (see Dilberts
passim). Fortunately there's a pure Perl 6502 emulator that works so
well the squirrels will never know the difference.
=head1 INTERFACE
=over
=item C<new>
Create a new 6502 CPU.
=item C<call_os( $vec_number )>
Subclass to provide OS entry points. OS vectors are installed by calling
C<make_vector>. When the vector is called C<call_os()> will be called
with the vector number.
=item C<get_a()>
Read the current value of the processor A register (accumulator).
=item C<get_p()>
Read the current value of the processor status register.
=item C<get_pc()>
Read the current value of the program counter.
=item C<get_s()>
Read the current value of the stack pointer.
=item C<get_x()>
Read the current value of the processor X index register.
=item C<get_y()>
Read the current value of the processor X index register.
=item C<get_xy()>
Read the value of X and Y as a sixteen bit number. X forms the lower 8
bits of the value and Y forms the upper 8 bits.
=item C<get_state()>
lib/Acme/6502.pm view on Meta::CPAN
Set the value of the processor A register (accumulator).
=item C<set_p( $value )>
Set the value of the processor status register.
=item C<set_pc( $value )>
Set the value of the program counter.
=item C<set_s( $value )>
Set the value of the stack pointer.
=item C<set_x( $value )>
Set the value of the X index register.
=item C<set_y( $value )>
Set the value of the Y index register.
=item C<set_xy( $value )>
Set the value of the X and Y registers to the specified sixteen bit
number. X gets the lower 8 bits, Y gets the upper 8 bits.
=item C<set_jumptab( $addr )>
Set the address of the block of memory that will be used to hold the
thunk blocks that correspond with vectored OS entry points. Each thunk
takes four bytes.
=item C<load_rom( $filename, $addr )>
Load a ROM image at the specified address.
=item C<make_vector( $jmp_addr, $vec_addr, $vec_number )>
Make a vectored entry point for an emulated OS. C<$jmp_addr> is the
address where an indirect JMP instruction (6C) will be placed,
C<$vec_addr> is the address of the vector and C<$vec_number> will be
passed to C<call_os> when the OS call is made.
=item C<poke_code( $addr, @bytes )>
Poke code directly at the specified address.
=item C<read_8( $addr )>
Read a byte at the specified address.
=item C<read_16( $addr )>
Read a sixteen bit (low, high) word at the specified address.
=item C<read_32( $addr )>
Read a 32 bit word at the specified address.
=item C<read_chunk( $start, $end )>
Read a chunk of data from C<$start> to C<$end> - 1 into a string.
=item C<read_str( $addr )>
Read a carriage return terminated (0x0D) string from the
specified address.
=item C<run( $count [, $callback ] )>
Execute the specified number of instructions and return. Optionally a
callback may be provided in which case it will be called before each
instruction is executed:
my $cb = sub {
my ($pc, $inst, $a, $x, $y, $s, $p) = @_;
# Maybe output trace info
}
$cpu->run(100, $cb);
=item C<write_8( $addr, $value )>
Write the byte at the specified address.
=item C<write_16( $addr, $value )>
Write a sixteen bit (low, high) value at the specified address.
=item C<write_32( $addr, $value )>
Write a 32 bit value at the specified address.
=item C<write_chunk( $addr, $string )>
Write a chunk of data to memory.
=back
=head1 DIAGNOSTICS
=over
=item C<< Bad instruction at %s (%s) >>
The emulator hit an illegal 6502 instruction.
=back
=head1 CONFIGURATION AND ENVIRONMENT
Acme::6502 requires no configuration files or environment variables.
=head1 DEPENDENCIES
None.
=head1 INCOMPATIBILITIES
None reported.
=head1 BUGS AND LIMITATIONS
( run in 0.603 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )