diff --git a/output/dll/libquicknes.dll b/output/dll/libquicknes.dll index 632c80874c..a1e53a83de 100644 Binary files a/output/dll/libquicknes.dll and b/output/dll/libquicknes.dll differ diff --git a/quicknes/bizinterface.cpp b/quicknes/bizinterface.cpp index 66b8f193d2..fd8018d939 100644 --- a/quicknes/bizinterface.cpp +++ b/quicknes/bizinterface.cpp @@ -284,5 +284,7 @@ EXPORT const char *qn_get_mapper(Nes_Emu *e, int *number) case 66: return "gnrom"; case 87: return "mapper_87"; case 232: return "quattro"; + case 9: return "mmc2"; + case 10: return "mmc4"; } } diff --git a/quicknes/libquicknes/libquicknes.vcxproj b/quicknes/libquicknes/libquicknes.vcxproj index bc4afff8f0..5ac37479d7 100644 --- a/quicknes/libquicknes/libquicknes.vcxproj +++ b/quicknes/libquicknes/libquicknes.vcxproj @@ -24,6 +24,7 @@ + @@ -64,6 +65,7 @@ + diff --git a/quicknes/libquicknes/libquicknes.vcxproj.filters b/quicknes/libquicknes/libquicknes.vcxproj.filters index cbe833eda0..f8b33fa5e1 100644 --- a/quicknes/libquicknes/libquicknes.vcxproj.filters +++ b/quicknes/libquicknes/libquicknes.vcxproj.filters @@ -135,6 +135,9 @@ Source Files + + Source Files\nes_emu + @@ -257,5 +260,8 @@ Header Files\fex + + Header Files\nes_emu + \ No newline at end of file diff --git a/quicknes/mingw/Makefile b/quicknes/mingw/Makefile index 0868cc53a0..1f845a80b4 100644 --- a/quicknes/mingw/Makefile +++ b/quicknes/mingw/Makefile @@ -38,6 +38,7 @@ SRCS = \ ../nes_emu/Nes_State.cpp \ ../nes_emu/nes_util.cpp \ ../nes_emu/Nes_Vrc6_Apu.cpp \ + ../nes_emu/Mmc24.cpp \ ../bizinterface.cpp \ ../fex/Data_Reader.cpp \ ../fex/blargg_errors.cpp \ diff --git a/quicknes/nes_emu/Mapper_Namco106.cpp b/quicknes/nes_emu/Mapper_Namco106.cpp index 5039e54539..3c2c32aad3 100644 --- a/quicknes/nes_emu/Mapper_Namco106.cpp +++ b/quicknes/nes_emu/Mapper_Namco106.cpp @@ -209,5 +209,8 @@ void register_optional_mappers() extern void register_namco106_mapper(); register_namco106_mapper(); + + extern void register_mmc24(); + register_mmc24(); } diff --git a/quicknes/nes_emu/Mmc24.cpp b/quicknes/nes_emu/Mmc24.cpp new file mode 100644 index 0000000000..13a7fba32f --- /dev/null +++ b/quicknes/nes_emu/Mmc24.cpp @@ -0,0 +1,117 @@ +#include + +#include "Nes_Mapper.h" +#include "blargg_source.h" + +#include "Mmc24.h" + + +class MMC2: public Nes_Mapper +{ + byte regs[6]; // A,B,C,D,E,F + + void mirror(byte val) + { + if (val & 1) + mirror_horiz(); + else + mirror_vert(); + } + +public: + MMC2() + { + register_state(regs, sizeof(regs)); + } + + virtual void reset_state() + { + std::memset(regs, 0, sizeof(regs)); + } + + virtual void apply_mapping() + { + mirror(regs[5]); + set_prg_bank(0x8000, bank_8k, regs[0]); + set_prg_bank(0xa000, bank_8k, 13); + set_prg_bank(0xc000, bank_8k, 14); + set_prg_bank(0xe000, bank_8k, 15); + + set_chr_bank(0x0000, bank_4k, regs[1]); + set_chr_bank(0x1000, bank_4k, regs[3]); + + set_chr_bank_ex(0x0000, bank_4k, regs[2]); + set_chr_bank_ex(0x1000, bank_4k, regs[4]); + } + + virtual void write(nes_time_t, nes_addr_t addr, int data) + { + switch (addr >> 12) + { + case 0xa: regs[0] = data; set_prg_bank(0x8000, bank_8k, data); break; + case 0xb: regs[1] = data; set_chr_bank(0x0000, bank_4k, data); break; + case 0xc: regs[2] = data; set_chr_bank_ex(0x0000, bank_4k, data); break; + case 0xd: regs[3] = data; set_chr_bank(0x1000, bank_4k, data); break; + case 0xe: regs[4] = data; set_chr_bank_ex(0x1000, bank_4k, data); break; + case 0xf: regs[5] = data; mirror(data); break; + } + } +}; + +class MMC4: public Nes_Mapper +{ + byte regs[6]; // A,B,C,D,E,F + + void mirror(byte val) + { + if (val & 1) + mirror_horiz(); + else + mirror_vert(); + } + +public: + MMC4() + { + register_state(regs, sizeof(regs)); + } + + virtual void reset_state() + { + std::memset(regs, 0, sizeof(regs)); + } + + virtual void apply_mapping() + { + enable_sram(); + + mirror(regs[5]); + set_prg_bank(0x8000, bank_16k, regs[0]); + + set_chr_bank(0x0000, bank_4k, regs[1]); + set_chr_bank(0x1000, bank_4k, regs[3]); + + set_chr_bank_ex(0x0000, bank_4k, regs[2]); + set_chr_bank_ex(0x1000, bank_4k, regs[4]); + } + + virtual void write(nes_time_t, nes_addr_t addr, int data) + { + switch (addr >> 12) + { + case 0xa: regs[0] = data; set_prg_bank(0x8000, bank_16k, data); break; + case 0xb: regs[1] = data; set_chr_bank(0x0000, bank_4k, data); break; + case 0xc: regs[2] = data; set_chr_bank_ex(0x0000, bank_4k, data); break; + case 0xd: regs[3] = data; set_chr_bank(0x1000, bank_4k, data); break; + case 0xe: regs[4] = data; set_chr_bank_ex(0x1000, bank_4k, data); break; + case 0xf: regs[5] = data; mirror(data); break; + } + } +}; + + +void register_mmc24() +{ + register_mapper(9); + register_mapper(10); +} diff --git a/quicknes/nes_emu/Mmc24.h b/quicknes/nes_emu/Mmc24.h new file mode 100644 index 0000000000..a7400ef0c9 --- /dev/null +++ b/quicknes/nes_emu/Mmc24.h @@ -0,0 +1,7 @@ +#ifndef MMC24_H +#define MMC24_H + + +void register_mmc24(); + +#endif diff --git a/quicknes/nes_emu/Nes_Mapper.cpp b/quicknes/nes_emu/Nes_Mapper.cpp index a55f02397c..eaef1c99bd 100644 --- a/quicknes/nes_emu/Nes_Mapper.cpp +++ b/quicknes/nes_emu/Nes_Mapper.cpp @@ -148,6 +148,12 @@ void Nes_Mapper::set_chr_bank( nes_addr_t addr, bank_size_t bs, int bank ) emu().ppu.set_chr_bank( addr, 1 << bs, bank << bs ); } +void Nes_Mapper::set_chr_bank_ex( nes_addr_t addr, bank_size_t bs, int bank ) +{ + emu().ppu.render_until( emu().clock() ); + emu().ppu.set_chr_bank_ex( addr, 1 << bs, bank << bs ); +} + void Nes_Mapper::mirror_manual( int page0, int page1, int page2, int page3 ) { emu().ppu.render_bg_until( emu().clock() ); diff --git a/quicknes/nes_emu/Nes_Mapper.h b/quicknes/nes_emu/Nes_Mapper.h index 59a258a571..f7206b254c 100644 --- a/quicknes/nes_emu/Nes_Mapper.h +++ b/quicknes/nes_emu/Nes_Mapper.h @@ -121,6 +121,7 @@ protected: // Map 'size' bytes from 'CHR + bank * size' to PPU address space starting at 'addr' void set_chr_bank( nes_addr_t addr, bank_size_t size, int bank ); + void set_chr_bank_ex( nes_addr_t addr, bank_size_t size, int bank ); // Set PPU mirroring. All mappings implemented using mirror_manual(). void mirror_manual( int page0, int page1, int page2, int page3 ); diff --git a/quicknes/nes_emu/Nes_Ppu.cpp b/quicknes/nes_emu/Nes_Ppu.cpp index 22affb2a3c..a3ff00aad6 100644 --- a/quicknes/nes_emu/Nes_Ppu.cpp +++ b/quicknes/nes_emu/Nes_Ppu.cpp @@ -226,7 +226,7 @@ inline void Nes_Ppu::invalidate_sprite_max( nes_time_t t ) // Sprite 0 hit -inline int Nes_Ppu_Impl::first_opaque_sprite_line() const +inline int Nes_Ppu_Impl::first_opaque_sprite_line() /*const*/ { // advance earliest time if sprite has blank lines at beginning byte const* p = map_chr( sprite_tile_index( spr_ram ) * 16 ); diff --git a/quicknes/nes_emu/Nes_Ppu_Bg.h b/quicknes/nes_emu/Nes_Ppu_Bg.h index 3bcfde4a65..e1ca5f2fbf 100644 --- a/quicknes/nes_emu/Nes_Ppu_Bg.h +++ b/quicknes/nes_emu/Nes_Ppu_Bg.h @@ -66,4 +66,3 @@ while ( true ) if ( !count ) break; } - diff --git a/quicknes/nes_emu/Nes_Ppu_Impl.cpp b/quicknes/nes_emu/Nes_Ppu_Impl.cpp index 7bb694b1c2..e9aae96e45 100644 --- a/quicknes/nes_emu/Nes_Ppu_Impl.cpp +++ b/quicknes/nes_emu/Nes_Ppu_Impl.cpp @@ -33,6 +33,10 @@ Nes_Ppu_Impl::Nes_Ppu_Impl() max_palette_size = 0; tile_cache_mem = NULL; ppu_state_t::unused = 0; + + mmc24_enabled = false; + mmc24_latched[0] = 0; + mmc24_latched[1] = 0; #ifndef NDEBUG // verify that unaligned accesses work @@ -126,6 +130,29 @@ void Nes_Ppu_Impl::set_chr_bank( int addr, int size, long data ) } } +void Nes_Ppu_Impl::set_chr_bank_ex( int addr, int size, long data ) +{ + mmc24_enabled = true; + + check( !chr_is_writable || addr == data ); // to do: is CHR RAM ever bank-switched? + //dprintf( "Tried to set CHR RAM bank at %04X to CHR+%04X\n", addr, data ); + + if ( data + size > chr_size ) + data %= chr_size; + + int count = (unsigned) size / chr_page_size; + assert( chr_page_size * count == size ); + assert( addr + size <= chr_addr_size ); + + int page = (unsigned) addr / chr_page_size; + while ( count-- ) + { + chr_pages_ex [page] = data - page * chr_page_size; + page++; + data += chr_page_size; + } +} + void Nes_Ppu_Impl::save_state( Nes_State_* out ) const { *out->ppu = *this; diff --git a/quicknes/nes_emu/Nes_Ppu_Impl.h b/quicknes/nes_emu/Nes_Ppu_Impl.h index 3eb8bdf6f2..c3965ffed9 100644 --- a/quicknes/nes_emu/Nes_Ppu_Impl.h +++ b/quicknes/nes_emu/Nes_Ppu_Impl.h @@ -1,4 +1,3 @@ - // NES PPU misc functions and setup // Nes_Emu 0.7.0 @@ -45,6 +44,7 @@ public: enum { vaddr_clock_mask = 0x1000 }; void set_nt_banks( int bank0, int bank1, int bank2, int bank3 ); void set_chr_bank( int addr, int size, long data ); + void set_chr_bank_ex( int addr, int size, long data ); // Nametable and CHR RAM enum { nt_ram_size = 0x1000 }; @@ -77,7 +77,7 @@ protected: //friend class Nes_Ppu; private: enum { last_sprite_max_scanline = 240 }; long recalc_sprite_max( int scanline ); - int first_opaque_sprite_line() const; + int first_opaque_sprite_line() /*const*/; protected: //friend class Nes_Ppu_Rendering; private: @@ -91,8 +91,8 @@ protected: //friend class Nes_Ppu_Rendering; private: typedef uint32_t cache_t; typedef cache_t cached_tile_t [4]; - cached_tile_t const& get_bg_tile( int index ) const; - cached_tile_t const& get_sprite_tile( byte const* sprite ) const; + cached_tile_t const& get_bg_tile( int index ) /*const*/; + cached_tile_t const& get_sprite_tile( byte const* sprite ) /*const*/; byte* get_nametable( int addr ) { return nt_banks [addr >> 10 & 3]; }; private: @@ -103,14 +103,37 @@ private: // Mapping enum { chr_page_size = 0x400 }; long chr_pages [chr_addr_size / chr_page_size]; - long map_chr_addr( unsigned a ) const { return chr_pages [a / chr_page_size] + a; } + long chr_pages_ex [chr_addr_size / chr_page_size]; + long map_chr_addr( unsigned a ) /*const*/ + { + if (!mmc24_enabled) + return chr_pages [a / chr_page_size] + a; + + int page = a >> 12 & 1; + int newval0 = (a & 0xff0) != 0xfd0; + int newval1 = (a & 0xff0) == 0xfe0; + + long ret; + if (mmc24_latched[page]) + ret = chr_pages_ex [a / chr_page_size] + a; + else + ret = chr_pages [a / chr_page_size] + a; + + mmc24_latched[page] &= newval0; + mmc24_latched[page] |= newval1; + + return ret; + } byte* nt_banks [4]; + bool mmc24_enabled; + byte mmc24_latched [2]; + // CHR data byte const* chr_data; // points to chr ram when there is no read-only data byte* chr_ram; // always points to impl->chr_ram; makes write_2007() faster long chr_size; - byte const* map_chr( int addr ) const { return &chr_data [map_chr_addr( addr )]; } + byte const* map_chr( int addr ) /*const*/ { return &chr_data [map_chr_addr( addr )]; } // CHR cache cached_tile_t* tile_cache; diff --git a/quicknes/nes_emu/Nes_Ppu_Rendering.cpp b/quicknes/nes_emu/Nes_Ppu_Rendering.cpp index 7cc726feb3..ec0ad14730 100644 --- a/quicknes/nes_emu/Nes_Ppu_Rendering.cpp +++ b/quicknes/nes_emu/Nes_Ppu_Rendering.cpp @@ -32,7 +32,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // Nes_Ppu_Impl inline Nes_Ppu_Impl::cached_tile_t const& - Nes_Ppu_Impl::get_sprite_tile( byte const* sprite ) const + Nes_Ppu_Impl::get_sprite_tile( byte const* sprite ) /*const*/ { cached_tile_t* tiles = tile_cache; if ( sprite [2] & 0x40 ) @@ -45,7 +45,7 @@ inline Nes_Ppu_Impl::cached_tile_t const& ((byte*) tiles + map_chr_addr( index * bytes_per_tile )); } -inline Nes_Ppu_Impl::cached_tile_t const& Nes_Ppu_Impl::get_bg_tile( int index ) const +inline Nes_Ppu_Impl::cached_tile_t const& Nes_Ppu_Impl::get_bg_tile( int index ) /*const*/ { // use index directly, since cached tile is same size as native tile BOOST_STATIC_ASSERT( sizeof (cached_tile_t) == bytes_per_tile );