summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu.h
blob: 41b5163cc99c0269f3f95974506a7bf9ab808dc6 (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
// NES CPU emulator

// Game_Music_Emu 0.6-pre
#ifndef NES_CPU_H
#define NES_CPU_H

#include "blargg_common.h"

class Nes_Cpu {
public:
	typedef BOOST::uint8_t byte;
	typedef int time_t;
	typedef int addr_t;
	enum { future_time = INT_MAX/2 + 1 };
	
	// Clears registers and maps all pages to unmapped_page
	void reset( void const* unmapped_page = NULL );
	
	// Maps code memory (memory accessed via the program counter). Start and size
	// must be multiple of page_size. If mirror_size is non-zero, the first
	// mirror_size bytes are repeated over the range. mirror_size must be a
	// multiple of page_size.
	enum { page_bits = 11 };
	enum { page_size = 1 << page_bits };
	void map_code( addr_t start, int size, void const* code, int mirror_size = 0 );
	
	// Accesses emulated memory as CPU does
	byte const* get_code( addr_t ) const;
	
	// NES 6502 registers. NOT kept updated during emulation.
	struct registers_t {
		BOOST::uint16_t pc;
		byte a;
		byte x;
		byte y;
		byte flags;
		byte sp;
	};
	registers_t r;
	
	// Time of beginning of next instruction to be executed
	time_t time() const             { return cpu_state->time + cpu_state->base; }
	void set_time( time_t t )       { cpu_state->time = t - cpu_state->base; }
	void adjust_time( int delta )   { cpu_state->time += delta; }
	
	// Clocks past end (negative if before)
	int time_past_end() const       { return cpu_state->time; }
	
	// Time of next IRQ
	time_t irq_time() const         { return irq_time_; }
	void set_irq_time( time_t );
	
	// Emulation stops once time >= end_time
	time_t end_time() const         { return end_time_; }
	void set_end_time( time_t );
	
	// Number of unimplemented instructions encountered and skipped
	void clear_error_count()        { error_count_ = 0; }
	unsigned error_count() const    { return error_count_; }
	void count_error()              { error_count_++; }
	
	// Unmapped page should be filled with this
	enum { halt_opcode = 0x22 };
	
	enum { irq_inhibit_mask = 0x04 };
	
	// Can read this many bytes past end of a page
	enum { cpu_padding = 8 };

private:
	// noncopyable
	Nes_Cpu( const Nes_Cpu& );
	Nes_Cpu& operator = ( const Nes_Cpu& );


// Implementation
public:
	Nes_Cpu() { cpu_state = &cpu_state_; }
	enum { page_count = 0x10000 >> page_bits };
	
	struct cpu_state_t {
		byte const* code_map [page_count + 1];
		time_t base;
		int time;
	};
	cpu_state_t* cpu_state; // points to cpu_state_ or a local copy
	cpu_state_t cpu_state_;
	time_t irq_time_;
	time_t end_time_;
	unsigned error_count_;
	
private:
	void set_code_page( int, void const* );
	inline void update_end_time( time_t end, time_t irq );
};

#define NES_CPU_PAGE( addr ) ((unsigned) (addr) >> Nes_Cpu::page_bits)

#if BLARGG_NONPORTABLE
	#define NES_CPU_OFFSET( addr ) (addr)
#else
	#define NES_CPU_OFFSET( addr ) ((addr) & (Nes_Cpu::page_size - 1))
#endif

inline BOOST::uint8_t const* Nes_Cpu::get_code( addr_t addr ) const
{
	return cpu_state_.code_map [NES_CPU_PAGE( addr )] + NES_CPU_OFFSET( addr );
}

inline void Nes_Cpu::update_end_time( time_t end, time_t irq )
{
	if ( end > irq && !(r.flags & irq_inhibit_mask) )
		end = irq;
	
	cpu_state->time += cpu_state->base - end;
	cpu_state->base = end;
}

inline void Nes_Cpu::set_irq_time( time_t t )
{
	irq_time_ = t;
	update_end_time( end_time_, t );
}

inline void Nes_Cpu::set_end_time( time_t t )
{
	end_time_ = t;
	update_end_time( t, irq_time_ );
}   

#endif