195 lines
5.2 KiB
C++
195 lines
5.2 KiB
C++
// Loads NSF file and emulates CPU and RAM, no sound chips
|
|
|
|
// Game_Music_Emu $vers
|
|
#ifndef NSF_IMPL_H
|
|
#define NSF_IMPL_H
|
|
|
|
#include "Gme_Loader.h"
|
|
#include "Nes_Cpu.h"
|
|
#include "Rom_Data.h"
|
|
#include "Nes_Apu.h"
|
|
|
|
// NSF file header
|
|
struct nsf_header_t
|
|
{
|
|
typedef unsigned char byte;
|
|
enum { size = 0x80 };
|
|
|
|
char tag [ 5];
|
|
byte vers;
|
|
byte track_count;
|
|
byte first_track;
|
|
byte load_addr [ 2];
|
|
byte init_addr [ 2];
|
|
byte play_addr [ 2];
|
|
char game [32]; // NOT null-terminated if 32 chars in length
|
|
char author [32];
|
|
char copyright [32];
|
|
byte ntsc_speed [ 2];
|
|
byte banks [ 8];
|
|
byte pal_speed [ 2];
|
|
byte speed_flags;
|
|
byte chip_flags;
|
|
byte unused [ 4];
|
|
|
|
// Sound chip masks
|
|
enum {
|
|
vrc6_mask = 1 << 0,
|
|
vrc7_mask = 1 << 1,
|
|
fds_mask = 1 << 2,
|
|
mmc5_mask = 1 << 3,
|
|
namco_mask = 1 << 4,
|
|
fme7_mask = 1 << 5,
|
|
all_mask = (1 << 6) - 1
|
|
};
|
|
|
|
// True if header has proper NSF file signature
|
|
bool valid_tag() const;
|
|
|
|
// True if file supports only PAL speed
|
|
bool pal_only() const { return (speed_flags & 3) == 1; }
|
|
|
|
// Clocks per second
|
|
double clock_rate() const;
|
|
|
|
// Clocks between calls to play routine
|
|
int play_period() const;
|
|
};
|
|
|
|
/* Loads NSF file into memory, then emulates CPU, RAM, and ROM.
|
|
Non-memory accesses are routed through cpu_read() and cpu_write(). */
|
|
class Nsf_Impl : public Gme_Loader {
|
|
public:
|
|
|
|
// Sound chip
|
|
Nes_Apu* nes_apu() { return &apu; }
|
|
|
|
// Starts track, where 0 is the first
|
|
virtual blargg_err_t start_track( int );
|
|
|
|
// Emulates to at least time t, then begins new time frame at
|
|
// time t. Might emulate a few clocks extra, so after returning,
|
|
// time() may not be zero.
|
|
typedef int time_t; // clock count
|
|
virtual void end_frame( time_t n );
|
|
|
|
// Finer control
|
|
|
|
// Header for currently loaded file
|
|
typedef nsf_header_t header_t;
|
|
header_t const& header() const { return header_; }
|
|
|
|
// Sets clocks between calls to play routine to p + 1/2 clock
|
|
void set_play_period( int p ) { play_period = p; }
|
|
|
|
// Time play routine will next be called
|
|
time_t play_time() const { return next_play; }
|
|
|
|
// Emulates to at least time t. Might emulate a few clocks extra.
|
|
virtual void run_until( time_t t );
|
|
|
|
// Time emulated to
|
|
time_t time() const { return cpu.time(); }
|
|
|
|
void enable_w4011_(bool enable = true) { enable_w4011 = enable; }
|
|
|
|
Rom_Data const& rom_() const { return rom; }
|
|
|
|
protected:
|
|
// Nsf_Core use
|
|
|
|
typedef int addr_t;
|
|
|
|
// Called for unmapped accesses. Default just prints info if debugging.
|
|
virtual void unmapped_write( addr_t, int data );
|
|
virtual int unmapped_read( addr_t );
|
|
|
|
// Override in derived class
|
|
// Bank writes and RAM at 0-$7FF and $6000-$7FFF are handled internally
|
|
virtual int cpu_read( addr_t a ) { return unmapped_read( a ); }
|
|
virtual void cpu_write( addr_t a, int data ){ unmapped_write( a, data ); }
|
|
|
|
// Reads byte as CPU would when executing code. Only works for RAM/ROM,
|
|
// NOT I/O like sound chips.
|
|
int read_code( addr_t addr ) const;
|
|
|
|
// Debugger services
|
|
|
|
enum { mem_size = 0x10000 };
|
|
|
|
// CPU sits here when waiting for next call to play routine
|
|
enum { idle_addr = 0x5FF6 };
|
|
|
|
Nes_Cpu cpu;
|
|
|
|
// Runs CPU to at least time t and returns false, or returns true
|
|
// if it encounters illegal instruction (halt).
|
|
virtual bool run_cpu_until( time_t t );
|
|
|
|
// CPU calls through to these to access memory (except instructions)
|
|
int read_mem( addr_t );
|
|
void write_mem( addr_t, int );
|
|
|
|
// Address of play routine
|
|
addr_t play_addr() const { return get_addr( header_.play_addr ); }
|
|
|
|
// Same as run_until, except emulation stops for any event (routine returned,
|
|
// play routine called, illegal instruction).
|
|
void run_once( time_t );
|
|
|
|
// Make a note of event
|
|
virtual void special_event( const char str [] );
|
|
|
|
|
|
// Implementation
|
|
public:
|
|
Nsf_Impl();
|
|
~Nsf_Impl();
|
|
|
|
protected:
|
|
virtual blargg_err_t load_( Data_Reader& );
|
|
virtual void unload();
|
|
|
|
private:
|
|
enum { low_ram_size = 0x800 };
|
|
enum { fdsram_size = 0x6000 };
|
|
enum { sram_size = 0x2000 };
|
|
enum { unmapped_size= Nes_Cpu::page_size + 8 };
|
|
enum { fds_banks = 2 };
|
|
enum { bank_count = fds_banks + 8 };
|
|
enum { banks_addr = idle_addr };
|
|
enum { sram_addr = 0x6000 };
|
|
|
|
blargg_vector<byte> high_ram;
|
|
Rom_Data rom;
|
|
|
|
// Play routine timing
|
|
time_t next_play;
|
|
time_t play_period;
|
|
int play_extra;
|
|
int play_delay;
|
|
bool enable_w4011;
|
|
Nes_Cpu::registers_t saved_state; // of interrupted init routine
|
|
|
|
// Large objects after others
|
|
header_t header_;
|
|
Nes_Apu apu;
|
|
byte low_ram [low_ram_size];
|
|
|
|
// Larger RAM areas allocated separately
|
|
enum { fdsram_offset = sram_size + unmapped_size };
|
|
byte* sram() { return high_ram.begin(); }
|
|
byte* unmapped_code() { return &high_ram [sram_size]; }
|
|
byte* fdsram() { return &high_ram [fdsram_offset]; }
|
|
int fds_enabled() const { return header_.chip_flags & header_t::fds_mask; }
|
|
|
|
void map_memory();
|
|
void write_bank( int index, int data );
|
|
void jsr_then_stop( byte const addr [] );
|
|
void push_byte( int );
|
|
static addr_t get_addr( byte const [] );
|
|
static int pcm_read( void*, int );
|
|
};
|
|
|
|
#endif
|