--- M6502/M6502.pm 2007/08/01 22:25:37 86 +++ M6502/M6502.pm 2007/08/02 13:04:29 94 @@ -15,9 +15,11 @@ M6502 - perl bindings for M6502 CPU emulator +=head1 FUNCTIONS + =cut -my $debug = 1; +our $debug = 0; our @mem = (0xff) x 0x10000; # 64M @@ -37,47 +39,43 @@ # Exec6502 cycles our $run_for = 0; -=head1 FUNCTIONS - =head2 init -Called before C - -=cut - -sub init { - my $self = shift; - warn "inside init low-level M6502 from $self\n"; -}; - -=head2 read - -Read from memory +Setup read and write memory hooks (to implement memory mapped devices) - $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 @@ -94,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 @@ -137,18 +135,84 @@ 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; +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 in humanly readable form @@ -167,6 +231,27 @@ 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 L is sample implementation using this module