| Games-NES-Emulator documentation | Contained in the Games-NES-Emulator distribution. |
Games::NES::Emulator::CPU - NES Central Processing Unit
Brian Cassidy <bricas@cpan.org>
Copyright 2007 by Brian Cassidy
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| Games-NES-Emulator documentation | Contained in the Games-NES-Emulator distribution. |
package Games::NES::Emulator::CPU; use strict; use warnings; use base qw( CPU::Emulator::6502 ); use Scalar::Util (); __PACKAGE__->mk_accessors( 'context' );
sub init { my $self = shift; my $emu = shift; Scalar::Util::weaken( $emu ); $self->SUPER::init( @_ ); $self->context( $emu ); $self->interrupt_line( 0 ); $self->memory( [ ( 0 ) x ( 0xFFFF + 1 ) ] ); my $reg = $self->registers; $reg->{ pc } = 0x8000; $reg->{ sp } = 0xFF; $self->toggle( 1 ); $self->cycle_counter( 0 ); $self->frame_counter( 0 ); }
sub RAM_read { my( $self, $addr ) = @_; my $c = $self->context; my $block = $addr >> 13; if( $block == 0 ) { return $self->SUPER::RAM_read( $addr & 0x7FF ); } elsif( $block == 1 ) { my $ppu_addr = ( $addr & 0x7 ) + 0x2000; my $ppu = $c->ppu; my $reg = $ppu->registers; if( $ppu_addr == 0x2002 ) { $self->toggle( 1 ); my $val = $reg->{ status }; $reg->{ status } &= 0x7f; return $val; } elsif( $ppu_addr == 0x2007 ) { return $ppu->VRAM->read( $reg->{ VRAM_addr }, 1 ); } } elsif( $block == 2 ) { if( ( $addr & 0x3F40 ) == 0 ) { if( $addr == 0x4016 ) { return $c->inputs->[ 0 ]->poll; } elsif( $addr == 0x4017 ) { return $c->inputs->[ 1 ]->poll; } return $c->apu->read( $addr ); } return $c->mapper->read( $addr ); } elsif( $block == 3 ) { return $self->SUPER::RAM_read( $addr ); } else { return $c->mapper->read( $addr ); } }
sub RAM_write { my( $self, $addr, $data ) = @_; my $c = $self->context; my $block = $addr >> 13; # TODO other cases if( $block == 0 ) { return $self->SUPER::RAM_write( ( $addr & 0x7FF ) => $data ); } elsif( $block == 1 ) { my $ppu_addr = ( $addr & 0x7 ) + 0x2000; my $ppu = $c->ppu; my $reg = $ppu->registers; if( $ppu_addr == 0x2000 ) { $reg->{ control1 } = $data; $reg->{ VRAM_temp_addr } = ( $reg->{ VRAM_temp_addr } & 0xF3FF) | ( $data & 0x3 ) << 10; $ppu->VRAM->increment( $data & 0x04 ? 32 : 1 ); } elsif( $ppu_addr == 0x2001 ) { $reg->{ control2 } = $data; } elsif( $ppu_addr == 0x2002 ) { } elsif( $ppu_addr == 0x2003 ) { $reg->{ SPRRAM_addr } = $data; } elsif( $ppu_addr == 0x2004 ) { $ppu->SPRRAM->[ $reg->{ SPRRAM_addr } ] = $data; } elsif( $ppu_addr == 0x2005 ) { if( $self->toggle ) { $reg->{ VRAM_temp_addr } = ( $reg->{ VRAM_temp_addr } & 0xffe0 ) | ( ( $data & 0xf8 ) >> 3 ); $ppu->draw->{ x_pixel_offset } = $data & 0x7; } else { $reg->{ VRAM_temp_addr } = ( $reg->{ VRAM_temp_addr } & 0xfc1f ) | ( ( $data & 0xf8 ) << 2 ); $reg->{ VRAM_temp_addr } = ( $reg->{ VRAM_temp_addr } & 0x8fff ) | ( ( $data & 0x7 ) << 12 ); } $self->toggle( !$self->toggle ); } elsif( $ppu_addr == 0x2006 ) { if( $self->toggle ) { $reg->{ VRAM_temp_addr } = ( $reg->{ VRAM_temp_addr } & 0xc0ff ) | ( ( $data & 0x3f ) << 8 ); $reg->{ VRAM_temp_addr } &= 0x3fff; } else { $reg->{ VRAM_temp_addr } = ( $reg->{ VRAM_temp_addr } & 0xff00 ) | $data; $reg->{ VRAM_addr } = $reg->{ VRAM_temp_addr }; } $self->toggle( !$self->toggle ); } elsif( $ppu_addr == 0x2007 ) { $ppu->VRAM->write( $reg->{ VRAM_addr }, $data, 1 ); } return; } elsif( $block == 2 ) { } elsif( $block == 3 ) { $self->SUPER::RAM_write( $addr => $data ); return $c->mapper->write( $addr => $data ); } else { return $c->mapper->write( $addr => $data ); } }
sub DMAT_transfer { my( $self, $page ) = @_; my $addr = $page * 0x100; my $ram = $self->context->ppu->SPRRAM; @$ram = map { $self->RAM_read( $addr + $_ ) } 0..0xFF; $self->cycle_counter( $self->cycle_counter + 512 ); }
1;