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 );