--- M6502/M6502.pm 2007/07/31 13:56:50 55 +++ M6502/M6502.pm 2007/08/02 13:04:29 94 @@ -6,17 +6,20 @@ use Data::Dump qw/dump/; use Carp qw/confess/; use Exporter 'import'; -our @EXPORT = qw'@mem $PC $A $P $X $Y $S $IPeriod $run_for $debug'; +our @EXPORT = qw'dump_R @mem $PC $A $P $X $Y $S $IPeriod $ICount $IRequest $IAutoReset $TrapBadOps $Trap $Trace $run_for $debug'; +our $VERSION = '0.0.2'; +require XSLoader; +XSLoader::load('M6502', $VERSION); =head1 NAME M6502 - perl bindings for M6502 CPU emulator -=cut +=head1 FUNCTIONS -my $debug = 0; +=cut -our $VERSION = qw(0.0.1); +our $debug = 0; our @mem = (0xff) x 0x10000; # 64M @@ -24,52 +27,55 @@ our $PC = 0xbeef; # CPU registars our ( $A, $P, $X, $Y, $S ) = (0) x 5; -# Set IPeriod to number of CPU cycles between calls to Loop6502 -our $IPeriod = 1; + +our $IPeriod=1; # Set IPeriod to number of CPU cycles between calls to Loop6502 +our $ICount; +our $IRequest; # Set to the INT_IRQ when pending IRQ +our $IAutoReset; # Set to 1 to autom. reset IRequest +our $TrapBadOps=1; # Set to 1 to warn of illegal opcodes +our $Trap; # Set Trap to address to trace from +our $Trace; # Set Trace=1 to start tracing + # Exec6502 cycles our $run_for = 0; -=head1 FUNCTIONS - =head2 init -Called before C +Setup read and write memory hooks (to implement memory mapped devices) -=cut - -sub init { - my $self = shift; - warn "inside init low-level M6502 from $self\n"; -}; - -=head2 read - -Read from memory - - $byte = read( $address ); + $init->( + read => sub { + return $mem[$_[0]]; + }, + write => sub { + $mem[$_[0]] = $_[1]; + }, + ); =cut -sub read { - my ($addr) = @_; - my $byte = $mem[$addr]; - warn "## M6502::read(",dump(@_),") = ",dump( $byte ),"\n" if $debug; - return $byte; -} - -=head2 write - -Write into emory +our $_rw_hooks = { + read => sub { + warn sprintf("# callback read(%04x) not implemented\n", @_) if $debug; + return $mem[$_[0]]; + }, + write => sub { + warn sprintf("# callback write(%04x,%02x) not implemented", @_) if $debug; + $mem[$_[0]] = $_[1]; + }, +}; - write( $address, $byte ); +sub init { + my $self = shift; + my $args = {@_}; + warn "inside init low-level M6502 from ",ref($self),"\n"; -=cut + foreach my $p ( qw/read write/ ) { + confess "need $p argument as coderef" unless ( $args->{$p} && ref($args->{$p}) eq 'CODE' ); + $_rw_hooks->{$p} = $args->{$p}; + } -sub write { - warn "## M6502::write(",dump(@_),")\n" if $debug; - my ($addr,$byte) = @_; - $mem[$addr] = $byte; -} +}; =head2 poke_code @@ -86,7 +92,7 @@ warn sprintf("## M6502::poke_code(%04x,%s)\n", $addr, dump( @_ )) if $self->debug; #$mem[$addr++] = $_ foreach @_; # call low-level write - Arch::write($addr++, $_) foreach @_; + $_rw_hooks->{write}->( $addr++, $_ ) foreach @_; } =head2 ram @@ -129,28 +135,121 @@ return 1; } -=head2 push_R +=head1 XS Callbacks + +This functions are called from C + +=head2 _read + +Read from memory C callback + + $byte = M6502::_read( $address ); + +=cut + +sub _read { + return $_rw_hooks->{read}->( @_ ); +} + +=head2 _write + +Write into memory C callback + + M6502:_write( $address, $byte ); + +=cut + +sub _write { + return $_rw_hooks->{write}->( @_ ); +} + +=head2 _update_perl_R -called by C to push changes in registars back to perl variables +called by C to push changes in registars back to perl variables =cut -sub push_R { - warn "## M6502::push_R(",dump(@_),")\n" if $debug; - my ( $a, $p, $x, $y, $s, $pc ) = @_; - $PC = $pc; - $S=$s; $X=$x; $Y=$y; $P=$p; $A=$a; +sub _update_perl_R { + warn "## M6502::update_perl_R(",dump(@_),")\n" if $debug; + ( $A, $P, $X, $Y, $S, $PC, $IPeriod, $ICount, $IRequest, $IAutoReset, $TrapBadOps, $Trap, $Trace ) = @_; dump_R(); } +=head1 XS + +Following functions are implemented in C and exported to perl. + +=head2 set_debug + + M6502::set_debug( 0 ); + +=head2 get_debug + + my $debug = M6502::set_debug(); + +=head2 reset + +Reset 6502 CPU, reading PC from C<0xfffc> + + M6502::reset(); + +=head2 update_C_R + +Push perl notion of register values to CPU emulator + + M6502::update_C_R(); + +=head2 update_perl_R + +Update perl notion of register values + + M6502::update_perl_R(); + +=head2 exec + +Execute cpu for specified number of cycles + + my $cycles_left = M6502::exec( $execute_cpu_cycles ); + +=head1 Helpers + =head2 dump_R -helper function which dumps registers +helper function which dumps registers in humanly readable form + + my $dump = dump_R; =cut sub dump_R { - warn sprintf("## M6502::dump_R PC: %04x A:%02x P:%02x X:%02x Y:%02x S:%02x\n", $PC, $A, $P, $X, $Y, $S) if $debug; + my $dump = sprintf(" PC: %04x A:%02x P:%02x X:%02x Y:%02x S:%02x " + . "IPeriod:%d ICount:%d IRequest:%02x IAutoReset:%02x TrapBadOps:%d Trap:%d Trace:%d" + . "\n", + $PC, $A, $P, $X, $Y, $S, $IPeriod, $ICount, $IRequest, $IAutoReset, $TrapBadOps, $Trap, $Trace, + ); + warn "## M6502::dump_R $dump" if $debug; + return $dump; +} + +=head2 debug + +Turn perl and C-level debugging on/off + + $emu->debug( 0 ); + $emu->debug( 1 ); + print $emu->debug; + +=cut + +sub debug { + my $self = shift; + my $value = shift; + if (defined($value)) { + $debug = M6502::set_debug($value); + } else { + $debug = M6502::get_debug(); + } + return $debug; } =head1 SEE ALSO