summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Impl.h
blob: d7c5ad4fed1ae3f5aa9755e04662e6f1ae774e6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Loads NSF file and emulates CPU and RAM, no sound chips

// Game_Music_Emu 0.6-pre
#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(); }
	
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;
	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