1// NES mapper interface23// Nes_Emu 0.7.045#ifndef NES_MAPPER6#define NES_MAPPER78#include "Nes_Cart.h"9#include "Nes_Cpu.h"10#include "nes_data.h"11#include "Nes_Core.h"12class Blip_Buffer;13class blip_eq_t;14class Nes_Core;1516class Nes_Mapper {17public:18// Register function that creates mapper for given code.19typedef Nes_Mapper* (*creator_func_t)();20static void register_mapper( int code, creator_func_t );2122// Register optional mappers included with Nes_Emu23void register_optional_mappers();2425// Create mapper appropriate for cartridge. Returns NULL if it uses unsupported mapper.26static Nes_Mapper* create( Nes_Cart const*, Nes_Core* );2728virtual ~Nes_Mapper();2930// Reset mapper to power-up state.31virtual void reset();3233// Save snapshot of mapper state. Default saves registered state.34virtual void save_state( mapper_state_t& );3536// Resets mapper, loads state, then applies it37virtual void load_state( mapper_state_t const& );3839// I/O4041// Read from memory42virtual int read( nes_time_t, nes_addr_t );4344// Write to memory45virtual void write( nes_time_t, nes_addr_t, int data ) = 0;4647// Write to memory below 0x8000 (returns false if mapper didn't handle write)48virtual bool write_intercepted( nes_time_t, nes_addr_t, int data );4950// Timing5152// Time returned when current mapper state won't ever cause an IRQ53enum { no_irq = LONG_MAX / 2 };5455// Time next IRQ will occur at56virtual nes_time_t next_irq( nes_time_t present );5758// Run mapper until given time59virtual void run_until( nes_time_t );6061// End video frame of given length62virtual void end_frame( nes_time_t length );6364// Sound6566// Number of sound channels67virtual int channel_count() const;6869// Set sound buffer for channel to output to, or NULL to silence channel.70virtual void set_channel_buf( int index, Blip_Buffer* );7172// Set treble equalization73virtual void set_treble( blip_eq_t const& );7475// Misc7677// Called when bit 12 of PPU's VRAM address changes from 0 to 1 due to78// $2006 and $2007 accesses (but not due to PPU scanline rendering).79virtual void a12_clocked();8081protected:82// Services provided for derived mapper classes83Nes_Mapper();8485// Register state data to automatically save and load. Be sure the binary86// layout is suitable for use in a file, including any byte-order issues.87// Automatically cleared to zero by default reset().88void register_state( void*, unsigned );8990// Enable 8K of RAM at 0x6000-0x7FFF, optionally read-only.91void enable_sram( bool enabled = true, bool read_only = false );9293// Cause CPU writes within given address range to call mapper's write() function.94// Might map a larger address range, which the mapper can ignore and pass to95// Nes_Mapper::write(). The range 0x8000-0xffff is always intercepted by the mapper.96void intercept_writes( nes_addr_t addr, unsigned size );9798// Cause CPU reads within given address range to call mapper's read() function.99// Might map a larger address range, which the mapper can ignore and pass to100// Nes_Mapper::read(). CPU opcode/operand reads and low-memory reads always101// go directly to memory and cannot be intercepted.102void intercept_reads( nes_addr_t addr, unsigned size );103104// Bank sizes for mapping105enum bank_size_t { // 1 << bank_Xk = X * 1024106bank_1k = 10,107bank_2k = 11,108bank_4k = 12,109bank_8k = 13,110bank_16k = 14,111bank_32k = 15112};113114// Index of last PRG/CHR bank. Last_bank selects last bank, last_bank - 1115// selects next-to-last bank, etc.116enum { last_bank = -1 };117118// Map 'size' bytes from 'PRG + bank * size' to CPU address space starting at 'addr'119void set_prg_bank( nes_addr_t addr, bank_size_t size, int bank );120121// Map 'size' bytes from 'CHR + bank * size' to PPU address space starting at 'addr'122void set_chr_bank( nes_addr_t addr, bank_size_t size, int bank );123void set_chr_bank_ex( nes_addr_t addr, bank_size_t size, int bank ); // mmc24 only124125// Set PPU mirroring. All mappings implemented using mirror_manual().126void mirror_manual( int page0, int page1, int page2, int page3 );127void mirror_single( int page );128void mirror_horiz( int page = 0 );129void mirror_vert( int page = 0 );130void mirror_full();131132// True if PPU rendering is enabled. Some mappers watch PPU memory accesses to determine133// when scanlines occur, and can only do this when rendering is enabled.134bool ppu_enabled() const;135136// Cartridge being emulated137Nes_Cart const& cart() const { return *cart_; }138139// Must be called when next_irq()'s return value is earlier than previous,140// current CPU run can be stopped earlier. Best to call whenever time may141// have changed (no performance impact if called even when time didn't change).142void irq_changed();143144// Handle data written to mapper that doesn't handle bus conflict arising due to145// PRG also reading data. Returns data that mapper should act as if were146// written. Currently always returns 'data' and just checks that data written is147// the same as byte in PRG at same address and writes debug message if it doesn't.148int handle_bus_conflict( nes_addr_t addr, int data );149150// Reference to emulator that uses this mapper.151Nes_Core& emu() const { return *emu_; }152153protected:154// Services derived classes provide155156// Read state from snapshot. Default reads data into registered state, then calls157// apply_mapping().158virtual void read_state( mapper_state_t const& );159160// Apply current mapping state to hardware. Called after reading mapper state161// from a snapshot.162virtual void apply_mapping() = 0;163164// Called by default reset() before apply_mapping() is called.165virtual void reset_state() { }166167// End of general interface168private:169Nes_Core* emu_;170void* state;171unsigned state_size;172Nes_Cart const* cart_;173174void default_reset_state();175176struct mapping_t {177int code;178Nes_Mapper::creator_func_t func;179};180static mapping_t mappers [];181static creator_func_t get_mapper_creator( int code );182183// built-in mappers184static Nes_Mapper* make_nrom();185static Nes_Mapper* make_unrom();186static Nes_Mapper* make_aorom();187static Nes_Mapper* make_cnrom();188static Nes_Mapper* make_mmc1();189static Nes_Mapper* make_mmc3();190};191192template<class T>193struct register_mapper {194/*void*/ register_mapper( int code ) { Nes_Mapper::register_mapper( code, create ); }195static Nes_Mapper* create() { return BLARGG_NEW T; }196};197198#ifdef NDEBUG199inline int Nes_Mapper::handle_bus_conflict( nes_addr_t addr, int data ) { return data; }200#endif201202inline void Nes_Mapper::mirror_horiz( int p ) { mirror_manual( p, p, p ^ 1, p ^ 1 ); }203inline void Nes_Mapper::mirror_vert( int p ) { mirror_manual( p, p ^ 1, p, p ^ 1 ); }204inline void Nes_Mapper::mirror_single( int p ) { mirror_manual( p, p, p, p ); }205inline void Nes_Mapper::mirror_full() { mirror_manual( 0, 1, 2, 3 ); }206207inline void Nes_Mapper::register_state( void* p, unsigned s )208{209assert( s <= max_mapper_state_size );210state = p;211state_size = s;212}213214inline bool Nes_Mapper::write_intercepted( nes_time_t, nes_addr_t, int ) { return false; }215216inline int Nes_Mapper::read( nes_time_t, nes_addr_t ) { return -1; } // signal to caller217218inline void Nes_Mapper::intercept_reads( nes_addr_t addr, unsigned size )219{220emu().add_mapper_intercept( addr, size, true, false );221}222223inline void Nes_Mapper::intercept_writes( nes_addr_t addr, unsigned size )224{225emu().add_mapper_intercept( addr, size, false, true );226}227228inline void Nes_Mapper::enable_sram( bool enabled, bool read_only )229{230emu_->enable_sram( enabled, read_only );231}232233#endif234235236237