Browse code

Update snes9x code from 1.53 to 1.6.0.

Notable differences from upstream:
* Did not use the same #include setup for the new byuu apu and instead chose to do things more traditionally, as the latter is also much easier to deal with in Visual Studio than just including .cpp files into other .cpp files...
* MSU1 support not included (I see no need for this in a plugin meant to be used for original SNES music).
* The dynamic rate control on the APU not included (I also see no need for it in this plugin).
* The extra resamplers I added in were removed as the original hermite resampler was folded into a single non-inhertiable resampler.
* Bits of cleanup.

Naram Qashat authored on 2021/04/19 21:20:36
Showing 1 changed files
1 1
deleted file mode 100644
... ...
@@ -1,327 +0,0 @@
1
-// Core SPC emulation: CPU, timers, SMP registers, memory
2
-
3
-// snes_spc 0.9.0. http://www.slack.net/~ant/
4
-
5
-#include <algorithm>
6
-#include <cstring>
7
-#include "SNES_SPC.h"
8
-
9
-/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
10
-can redistribute it and/or modify it under the terms of the GNU Lesser
11
-General Public License as published by the Free Software Foundation; either
12
-version 2.1 of the License, or (at your option) any later version. This
13
-module is distributed in the hope that it will be useful, but WITHOUT ANY
14
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16
-details. You should have received a copy of the GNU Lesser General Public
17
-License along with this module; if not, write to the Free Software Foundation,
18
-Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19
-
20
-//// Timers
21
-
22
-#ifdef SPC_DISABLE_TEMPO
23
-template<typename T> static inline T TIMER_DIV(SNES_SPC::Timer *t, const T &n) { return n >> t->prescaler; }
24
-template<typename T> static inline T TIMER_MUL(SNES_SPC::Timer *t, const T &n) { return n << t->prescaler; }
25
-#else
26
-template<typename T> static inline T TIMER_DIV(SNES_SPC::Timer *t, const T &n) { return n / t->prescaler; }
27
-template<typename T> static inline T TIMER_MUL(SNES_SPC::Timer *t, const T &n) { return n * t->prescaler; }
28
-#endif
29
-
30
-auto SNES_SPC::run_timer_(Timer *t, rel_time_t time) -> Timer *
31
-{
32
-	int elapsed = TIMER_DIV(t, time - t->next_time) + 1;
33
-	t->next_time += TIMER_MUL(t, elapsed);
34
-
35
-	if (t->enabled)
36
-	{
37
-		int remain = IF_0_THEN_256(t->period - t->divider);
38
-		int divider = t->divider + elapsed;
39
-		int over = elapsed - remain;
40
-		if (over >= 0)
41
-		{
42
-			int n = over / t->period;
43
-			t->counter = (t->counter + 1 + n) & 0x0F;
44
-			divider = over - n * t->period;
45
-		}
46
-		t->divider = static_cast<uint8_t>(divider);
47
-	}
48
-	return t;
49
-}
50
-
51
-auto SNES_SPC::run_timer(Timer *t, rel_time_t time) -> Timer *
52
-{
53
-	if (time >= t->next_time)
54
-		t = this->run_timer_(t, time);
55
-	return t;
56
-}
57
-
58
-//// ROM
59
-
60
-void SNES_SPC::enable_rom(bool enable)
61
-{
62
-	if (this->m.rom_enabled != enable)
63
-	{
64
-		this->m.rom_enabled = this->dsp.rom_enabled = enable;
65
-		if (enable)
66
-			std::copy_n(&this->m.ram.ram[rom_addr], static_cast<int>(rom_size), &this->m.hi_ram[0]);
67
-		auto data = enable ? &this->m.rom[0] : &this->m.hi_ram[0];
68
-		std::copy_n(&data[0], static_cast<int>(rom_size), &this->m.ram.ram[rom_addr]);
69
-		// TODO: ROM can still get overwritten when DSP writes to echo buffer
70
-	}
71
-}
72
-
73
-//// DSP
74
-
75
-void SNES_SPC::RUN_DSP(rel_time_t time)
76
-{
77
-	int count = time - this->m.dsp_time;
78
-	if (count)
79
-	{
80
-		assert(count > 0);
81
-		this->m.dsp_time = time;
82
-		this->dsp.run(count);
83
-	}
84
-}
85
-
86
-int SNES_SPC::dsp_read(rel_time_t time)
87
-{
88
-	this->RUN_DSP(time);
89
-
90
-	int result = this->dsp.read(this->m.smp_regs[0][r_dspaddr] & 0x7F);
91
-
92
-#ifdef SPC_DSP_READ_HOOK
93
-	SPC_DSP_READ_HOOK(spc_time + time, this->m.smp_regs[0][r_dspaddr] & 0x7F, result);
94
-#endif
95
-
96
-	return result;
97
-}
98
-
99
-void SNES_SPC::dsp_write(int data, rel_time_t time)
100
-{
101
-	this->RUN_DSP(time);
102
-
103
-#ifdef SPC_DSP_WRITE_HOOK
104
-	SPC_DSP_WRITE_HOOK(this->m.spc_time + time, this->m.smp_regs[0][r_dspaddr], static_cast<uint8_t>(data));
105
-#endif
106
-
107
-	if (this->m.smp_regs[0][r_dspaddr] <= 0x7F)
108
-		this->dsp.write(this->m.smp_regs[0][r_dspaddr], data);
109
-}
110
-
111
-//// CPU write
112
-
113
-// divided into multiple functions to keep rarely-used functionality separate
114
-// so often-used functionality can be optimized better by compiler
115
-
116
-// If write isn't preceded by read, data has this added to it
117
-static const int no_read_before_write = 0x2000;
118
-
119
-void SNES_SPC::cpu_write_smp_reg_(int data, rel_time_t time, int addr)
120
-{
121
-	switch (addr)
122
-	{
123
-		case r_t0target:
124
-		case r_t1target:
125
-		case r_t2target:
126
-		{
127
-			auto t = &this->m.timers[addr - r_t0target];
128
-			int period = IF_0_THEN_256(data);
129
-			if (t->period != period)
130
-			{
131
-				t = this->run_timer(t, time);
132
-				t->period = period;
133
-			}
134
-			break;
135
-		}
136
-
137
-		case r_t0out:
138
-		case r_t1out:
139
-		case r_t2out:
140
-			if (data < no_read_before_write  / 2)
141
-				this->run_timer(&this->m.timers[addr - r_t0out], time - 1)->counter = 0;
142
-			break;
143
-
144
-		// Registers that act like RAM
145
-		case 0x8:
146
-		case 0x9:
147
-			this->m.smp_regs[1][addr] = static_cast<uint8_t>(data);
148
-			break;
149
-
150
-		case r_test:
151
-			break;
152
-
153
-		case r_control:
154
-			// port clears
155
-			if (data & 0x10)
156
-			{
157
-				this->m.smp_regs[1][r_cpuio0] = 0;
158
-				this->m.smp_regs[1][r_cpuio1] = 0;
159
-			}
160
-			if (data & 0x20)
161
-			{
162
-				this->m.smp_regs[1][r_cpuio2] = 0;
163
-				this->m.smp_regs[1][r_cpuio3] = 0;
164
-			}
165
-
166
-			// timers
167
-			for (int i = 0; i < timer_count; ++i)
168
-			{
169
-				auto t = &this->m.timers[i];
170
-				bool enabled = !!((data >> i) & 1);
171
-				if (t->enabled != enabled)
172
-				{
173
-					t = this->run_timer(t, time);
174
-					t->enabled = enabled;
175
-					if (enabled)
176
-						t->divider = t->counter = 0;
177
-				}
178
-			}
179
-			this->enable_rom(!!(data & 0x80));
180
-	}
181
-}
182
-
183
-void SNES_SPC::cpu_write_smp_reg(int data, rel_time_t time, int addr)
184
-{
185
-	if (addr == r_dspdata) // 99%
186
-		this->dsp_write(data, time);
187
-	else
188
-		this->cpu_write_smp_reg_(data, time, addr);
189
-}
190
-
191
-void SNES_SPC::cpu_write_high(int data, int i, rel_time_t time)
192
-{
193
-	if (i < rom_size)
194
-	{
195
-		this->m.hi_ram [i] = static_cast<uint8_t>(data);
196
-		if (this->m.rom_enabled)
197
-			this->m.ram.ram[i + rom_addr] = this->m.rom[i]; // restore overwritten ROM
198
-	}
199
-	else
200
-	{
201
-		assert(this->m.ram.ram[i + rom_addr] == static_cast<uint8_t>(data));
202
-		this->m.ram.ram[i + rom_addr] = cpu_pad_fill; // restore overwritten padding
203
-		this->cpu_write(data, i + rom_addr - 0x10000, time);
204
-	}
205
-}
206
-
207
-static const int bits_in_int = CHAR_BIT * sizeof(int);
208
-
209
-void SNES_SPC::cpu_write(int data, int addr, rel_time_t time)
210
-{
211
-	// RAM
212
-	this->m.ram.ram[addr] = static_cast<uint8_t>(data);
213
-	int reg = addr - 0xF0;
214
-	if (reg >= 0) // 64%
215
-	{
216
-		// $F0-$FF
217
-		if (reg < reg_count) // 87%
218
-		{
219
-			this->m.smp_regs[0][reg] = static_cast<uint8_t>(data);
220
-
221
-			// Ports
222
-#ifdef SPC_PORT_WRITE_HOOK
223
-			if (static_cast<unsigned>(reg - r_cpuio0) < port_count)
224
-				SPC_PORT_WRITE_HOOK(this->m.spc_time + time, (reg - r_cpuio0), static_cast<uint8_t>(data), &this->m.smp_regs[0][r_cpuio0]);
225
-#endif
226
-
227
-			// Registers other than $F2 and $F4-$F7
228
-			//if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
229
-			// TODO: this is a bit on the fragile side
230
-			if (((~0x2F00 << (bits_in_int - 16)) << reg) < 0) // 36%
231
-				this->cpu_write_smp_reg(data, time, reg);
232
-		}
233
-		// High mem/address wrap-around
234
-		else
235
-		{
236
-			reg -= rom_addr - 0xF0;
237
-			if (reg >= 0) // 1% in IPL ROM area or address wrapped around
238
-				this->cpu_write_high(data, reg, time);
239
-		}
240
-	}
241
-}
242
-
243
-//// CPU read
244
-
245
-int SNES_SPC::cpu_read_smp_reg(int reg, rel_time_t time)
246
-{
247
-	int result = this->m.smp_regs[1][reg];
248
-	reg -= r_dspaddr;
249
-	// DSP addr and data
250
-	if (static_cast<unsigned>(reg) <= 1) // 4% 0xF2 and 0xF3
251
-	{
252
-		result = this->m.smp_regs[0][r_dspaddr];
253
-		if (static_cast<unsigned>(reg) == 1)
254
-			result = this->dsp_read(time); // 0xF3
255
-	}
256
-	return result;
257
-}
258
-
259
-int SNES_SPC::cpu_read(int addr, rel_time_t time)
260
-{
261
-	// RAM
262
-	int result = this->m.ram.ram[addr];
263
-	int reg = addr - 0xF0;
264
-	if (reg >= 0) // 40%
265
-	{
266
-		reg -= 0x10;
267
-		if (static_cast<unsigned>(reg) >= 0xFF00) // 21%
268
-		{
269
-			reg += 0x10 - r_t0out;
270
-
271
-			// Timers
272
-			if (static_cast<unsigned>(reg) < timer_count) // 90%
273
-			{
274
-				auto t = &this->m.timers[reg];
275
-				if (time >= t->next_time)
276
-					t = this->run_timer_(t, time);
277
-				result = t->counter;
278
-				t->counter = 0;
279
-			}
280
-			// Other registers
281
-			else if (reg < 0) // 10%
282
-				result = this->cpu_read_smp_reg(reg + r_t0out, time);
283
-			else // 1%
284
-			{
285
-				assert(reg + (r_t0out + 0xF0 - 0x10000) < 0x100);
286
-				result = this->cpu_read(reg + (r_t0out + 0xF0 - 0x10000), time);
287
-			}
288
-		}
289
-	}
290
-
291
-	return result;
292
-}
293
-
294
-//// Run
295
-
296
-static const int cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
297
-
298
-void SNES_SPC::end_frame(time_t end_time)
299
-{
300
-	// Catch CPU up to as close to end as possible. If final instruction
301
-	// would exceed end, does NOT execute it and leaves m.spc_time < end.
302
-	if (end_time > this->m.spc_time)
303
-		this->run_until_(end_time);
304
-
305
-	this->m.spc_time -= end_time;
306
-	this->m.extra_clocks += end_time;
307
-
308
-	// Greatest number of clocks early that emulation can stop early due to
309
-	// not being able to execute current instruction without going over
310
-	// allowed time.
311
-	assert(-cpu_lag_max <= this->m.spc_time && this->m.spc_time <= cpu_lag_max);
312
-
313
-	// Catch timers up to CPU
314
-	for (int i = 0; i < timer_count; ++i)
315
-		this->run_timer(&this->m.timers [i], 0);
316
-
317
-	// Catch DSP up to CPU
318
-	if (this->m.dsp_time < 0)
319
-		this->RUN_DSP(0);
320
-
321
-	// Save any extra samples beyond what should be generated
322
-	if (this->m.buf_begin)
323
-		this->save_extra();
324
-}
325
-
326
-// Inclusion here allows static memory access functions and better optimization
327
-#include "SPC_CPU.h"
Browse code

* Small Makefile changes.

* Removed a couple files that were not being used.
* Cleanups found with clang's -Weverything (and shutting it up about a
lot of things that were ridiculous, as well as ignoring some of the
warnings still coming up).

Naram Qashat authored on 2014/09/25 12:28:21
Showing 1 changed files
... ...
@@ -19,7 +19,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19 19
 
20 20
 //// Timers
21 21
 
22
-#if SPC_DISABLE_TEMPO
22
+#ifdef SPC_DISABLE_TEMPO
23 23
 template<typename T> static inline T TIMER_DIV(SNES_SPC::Timer *t, const T &n) { return n >> t->prescaler; }
24 24
 template<typename T> static inline T TIMER_MUL(SNES_SPC::Timer *t, const T &n) { return n << t->prescaler; }
25 25
 #else
Browse code

[SNSF] Replaced most uses of fill/copy with fill_n/copy_n, and a few other minor code cleanups.

Naram Qashat authored on 2013/05/27 22:34:22
Showing 1 changed files
... ...
@@ -63,9 +63,9 @@ void SNES_SPC::enable_rom(bool enable)
63 63
 	{
64 64
 		this->m.rom_enabled = this->dsp.rom_enabled = enable;
65 65
 		if (enable)
66
-			std::copy(&this->m.ram.ram[rom_addr], &this->m.ram.ram[rom_addr + rom_size], &this->m.hi_ram[0]);
66
+			std::copy_n(&this->m.ram.ram[rom_addr], static_cast<int>(rom_size), &this->m.hi_ram[0]);
67 67
 		auto data = enable ? &this->m.rom[0] : &this->m.hi_ram[0];
68
-		std::copy(&data[0], &data[rom_size], &this->m.ram.ram[rom_addr]);
68
+		std::copy_n(&data[0], static_cast<int>(rom_size), &this->m.ram.ram[rom_addr]);
69 69
 		// TODO: ROM can still get overwritten when DSP writes to echo buffer
70 70
 	}
71 71
 }
... ...
@@ -173,10 +173,7 @@ void SNES_SPC::cpu_write_smp_reg_(int data, rel_time_t time, int addr)
173 173
 					t = this->run_timer(t, time);
174 174
 					t->enabled = enabled;
175 175
 					if (enabled)
176
-					{
177
-						t->divider = 0;
178
-						t->counter = 0;
179
-					}
176
+						t->divider = t->counter = 0;
180 177
 				}
181 178
 			}
182 179
 			this->enable_rom(!!(data & 0x80));
... ...
@@ -296,28 +293,6 @@ int SNES_SPC::cpu_read(int addr, rel_time_t time)
296 293
 
297 294
 //// Run
298 295
 
299
-// Prefix and suffix for CPU emulator function
300
-#define SPC_CPU_RUN_FUNC \
301
-uint8_t *SNES_SPC::run_until_(time_t end_time) \
302
-{ \
303
-	rel_time_t rel_time = this->m.spc_time - end_time; \
304
-	/*assert( rel_time <= 0 );*/ \
305
-	this->m.spc_time = end_time; \
306
-	this->m.dsp_time += rel_time; \
307
-	this->m.timers[0].next_time += rel_time; \
308
-	this->m.timers[1].next_time += rel_time; \
309
-	this->m.timers[2].next_time += rel_time;
310
-
311
-#define SPC_CPU_RUN_FUNC_END \
312
-	this->m.spc_time += rel_time; \
313
-	this->m.dsp_time -= rel_time; \
314
-	this->m.timers[0].next_time -= rel_time; \
315
-	this->m.timers[1].next_time -= rel_time; \
316
-	this->m.timers[2].next_time -= rel_time; \
317
-	/*assert( m.spc_time >= end_time );*/ \
318
-	return &this->m.smp_regs[0][r_cpuio0]; \
319
-}
320
-
321 296
 static const int cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
322 297
 
323 298
 void SNES_SPC::end_frame(time_t end_time)
Browse code

[SNSF] Massive code cleanup, as well as removing as much unused code/variables as possible.

Naram Qashat authored on 2013/05/23 06:02:16
Showing 1 changed files
... ...
@@ -2,10 +2,10 @@
2 2
 
3 3
 // snes_spc 0.9.0. http://www.slack.net/~ant/
4 4
 
5
+#include <algorithm>
6
+#include <cstring>
5 7
 #include "SNES_SPC.h"
6 8
 
7
-#include <string.h>
8
-
9 9
 /* Copyright (C) 2004-2007 Shay Green. This module is free software; you
10 10
 can redistribute it and/or modify it under the terms of the GNU Lesser
11 11
 General Public License as published by the Free Software Foundation; either
... ...
@@ -17,547 +17,335 @@ details. You should have received a copy of the GNU Lesser General Public
17 17
 License along with this module; if not, write to the Free Software Foundation,
18 18
 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19 19
 
20
-#include "blargg_source.h"
21
-
22
-#define RAM         (m.ram.ram)
23
-#define REGS        (m.smp_regs [0])
24
-#define REGS_IN     (m.smp_regs [1])
25
-
26
-// (n ? n : 256)
27
-#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
28
-
29
-// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which
30
-// do crazy echo buffer accesses.
31
-#ifndef SPC_MORE_ACCURACY
32
-	#define SPC_MORE_ACCURACY 0
33
-#endif
34
-
35
-#ifdef BLARGG_ENABLE_OPTIMIZER
36
-	#include BLARGG_ENABLE_OPTIMIZER
37
-#endif
38
-
39
-
40 20
 //// Timers
41 21
 
42 22
 #if SPC_DISABLE_TEMPO
43
-	#define TIMER_DIV( t, n ) ((n) >> t->prescaler)
44
-	#define TIMER_MUL( t, n ) ((n) << t->prescaler)
23
+template<typename T> static inline T TIMER_DIV(SNES_SPC::Timer *t, const T &n) { return n >> t->prescaler; }
24
+template<typename T> static inline T TIMER_MUL(SNES_SPC::Timer *t, const T &n) { return n << t->prescaler; }
45 25
 #else
46
-	#define TIMER_DIV( t, n ) ((n) / t->prescaler)
47
-	#define TIMER_MUL( t, n ) ((n) * t->prescaler)
26
+template<typename T> static inline T TIMER_DIV(SNES_SPC::Timer *t, const T &n) { return n / t->prescaler; }
27
+template<typename T> static inline T TIMER_MUL(SNES_SPC::Timer *t, const T &n) { return n * t->prescaler; }
48 28
 #endif
49 29
 
50
-SNES_SPC::Timer* SNES_SPC::run_timer_( Timer* t, rel_time_t time )
30
+auto SNES_SPC::run_timer_(Timer *t, rel_time_t time) -> Timer *
51 31
 {
52
-	int elapsed = TIMER_DIV( t, time - t->next_time ) + 1;
53
-	t->next_time += TIMER_MUL( t, elapsed );
54
-	
55
-	if ( t->enabled )
32
+	int elapsed = TIMER_DIV(t, time - t->next_time) + 1;
33
+	t->next_time += TIMER_MUL(t, elapsed);
34
+
35
+	if (t->enabled)
56 36
 	{
57
-		int remain = IF_0_THEN_256( t->period - t->divider );
37
+		int remain = IF_0_THEN_256(t->period - t->divider);
58 38
 		int divider = t->divider + elapsed;
59 39
 		int over = elapsed - remain;
60
-		if ( over >= 0 )
40
+		if (over >= 0)
61 41
 		{
62 42
 			int n = over / t->period;
63 43
 			t->counter = (t->counter + 1 + n) & 0x0F;
64 44
 			divider = over - n * t->period;
65 45
 		}
66
-		t->divider = (uint8_t) divider;
46
+		t->divider = static_cast<uint8_t>(divider);
67 47
 	}
68 48
 	return t;
69 49
 }
70 50
 
71
-inline SNES_SPC::Timer* SNES_SPC::run_timer( Timer* t, rel_time_t time )
51
+auto SNES_SPC::run_timer(Timer *t, rel_time_t time) -> Timer *
72 52
 {
73
-	if ( time >= t->next_time )
74
-		t = run_timer_( t, time );
53
+	if (time >= t->next_time)
54
+		t = this->run_timer_(t, time);
75 55
 	return t;
76 56
 }
77 57
 
78
-
79 58
 //// ROM
80 59
 
81
-void SNES_SPC::enable_rom( int enable )
60
+void SNES_SPC::enable_rom(bool enable)
82 61
 {
83
-	if ( m.rom_enabled != enable )
62
+	if (this->m.rom_enabled != enable)
84 63
 	{
85
-		m.rom_enabled = dsp.rom_enabled = enable;
86
-		if ( enable )
87
-			memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram );
88
-		memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size );
64
+		this->m.rom_enabled = this->dsp.rom_enabled = enable;
65
+		if (enable)
66
+			std::copy(&this->m.ram.ram[rom_addr], &this->m.ram.ram[rom_addr + rom_size], &this->m.hi_ram[0]);
67
+		auto data = enable ? &this->m.rom[0] : &this->m.hi_ram[0];
68
+		std::copy(&data[0], &data[rom_size], &this->m.ram.ram[rom_addr]);
89 69
 		// TODO: ROM can still get overwritten when DSP writes to echo buffer
90 70
 	}
91 71
 }
92 72
 
93
-
94 73
 //// DSP
95 74
 
96
-#if SPC_LESS_ACCURATE
97
-	int const max_reg_time = 29;
98
-	
99
-	signed char const SNES_SPC::reg_times_ [256] =
75
+void SNES_SPC::RUN_DSP(rel_time_t time)
76
+{
77
+	int count = time - this->m.dsp_time;
78
+	if (count)
100 79
 	{
101
-		 -1,  0,-11,-10,-15,-11, -2, -2,  4,  3, 14, 14, 26, 26, 14, 22,
102
-		  2,  3,  0,  1,-12,  0,  1,  1,  7,  6, 14, 14, 27, 14, 14, 23,
103
-		  5,  6,  3,  4, -1,  3,  4,  4, 10,  9, 14, 14, 26, -5, 14, 23,
104
-		  8,  9,  6,  7,  2,  6,  7,  7, 13, 12, 14, 14, 27, -4, 14, 24,
105
-		 11, 12,  9, 10,  5,  9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24,
106
-		 14, 15, 12, 13,  8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24,
107
-		 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25,
108
-		 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25,
109
-		 
110
-		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
111
-		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
112
-		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
113
-		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
114
-		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
115
-		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
116
-		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
117
-		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
118
-	};
119
-	
120
-	#define RUN_DSP( time, offset ) \
121
-		int count = (time) - (offset) - m.dsp_time;\
122
-		if ( count >= 0 )\
123
-		{\
124
-			int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\
125
-			m.dsp_time += clock_count;\
126
-			dsp.run( clock_count );\
127
-		}
128
-#else
129
-	#define RUN_DSP( time, offset ) \
130
-		{\
131
-			int count = (time) - m.dsp_time;\
132
-			if ( !SPC_MORE_ACCURACY || count )\
133
-			{\
134
-				assert( count > 0 );\
135
-				m.dsp_time = (time);\
136
-				dsp.run( count );\
137
-			}\
138
-		}
139
-#endif
80
+		assert(count > 0);
81
+		this->m.dsp_time = time;
82
+		this->dsp.run(count);
83
+	}
84
+}
140 85
 
141
-int SNES_SPC::dsp_read( rel_time_t time )
86
+int SNES_SPC::dsp_read(rel_time_t time)
142 87
 {
143
-	RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
144
-	
145
-	int result = dsp.read( REGS [r_dspaddr] & 0x7F );
146
-	
147
-	#ifdef SPC_DSP_READ_HOOK
148
-		SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
149
-	#endif
150
-	
88
+	this->RUN_DSP(time);
89
+
90
+	int result = this->dsp.read(this->m.smp_regs[0][r_dspaddr] & 0x7F);
91
+
92
+#ifdef SPC_DSP_READ_HOOK
93
+	SPC_DSP_READ_HOOK(spc_time + time, this->m.smp_regs[0][r_dspaddr] & 0x7F, result);
94
+#endif
95
+
151 96
 	return result;
152 97
 }
153 98
 
154
-inline void SNES_SPC::dsp_write( int data, rel_time_t time )
99
+void SNES_SPC::dsp_write(int data, rel_time_t time)
155 100
 {
156
-	RUN_DSP( time, reg_times [REGS [r_dspaddr]] )
157
-	#if SPC_LESS_ACCURATE
158
-		else if ( m.dsp_time == skipping_time )
159
-		{
160
-			int r = REGS [r_dspaddr];
161
-			if ( r == SPC_DSP::r_kon )
162
-				m.skipped_kon |= data & ~dsp.read( SPC_DSP::r_koff );
163
-			
164
-			if ( r == SPC_DSP::r_koff )
165
-			{
166
-				m.skipped_koff |= data;
167
-				m.skipped_kon &= ~data;
168
-			}
169
-		}
170
-	#endif
171
-	
172
-	#ifdef SPC_DSP_WRITE_HOOK
173
-		SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
174
-	#endif
175
-	
176
-	if ( REGS [r_dspaddr] <= 0x7F )
177
-		dsp.write( REGS [r_dspaddr], data );
178
-	else if ( !SPC_MORE_ACCURACY )
179
-		dprintf( "SPC wrote to DSP register > $7F\n" );
180
-}
181
-
182
-
183
-//// Memory access extras
101
+	this->RUN_DSP(time);
184 102
 
185
-#if SPC_MORE_ACCURACY
186
-	#define MEM_ACCESS( time, addr ) \
187
-	{\
188
-		if ( time >= m.dsp_time )\
189
-		{\
190
-			RUN_DSP( time, max_reg_time );\
191
-		}\
192
-	}
193
-#elif !defined (NDEBUG)
194
-	// Debug-only check for read/write within echo buffer, since this might result in
195
-	// inaccurate emulation due to the DSP not being caught up to the present.
196
-	
197
-	bool SNES_SPC::check_echo_access( int addr )
198
-	{
199
-		if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
200
-		{
201
-			int start = 0x100 * dsp.read( SPC_DSP::r_esa );
202
-			int size  = 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
203
-			int end   = start + (size ? size : 4);
204
-			if ( start <= addr && addr < end )
205
-			{
206
-				if ( !m.echo_accessed )
207
-				{
208
-					m.echo_accessed = 1;
209
-					return true;
210
-				}
211
-			}
212
-		}
213
-		return false;
214
-	}
215
-	
216
-	#define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
217
-#else
218
-	#define MEM_ACCESS( time, addr )
103
+#ifdef SPC_DSP_WRITE_HOOK
104
+	SPC_DSP_WRITE_HOOK(this->m.spc_time + time, this->m.smp_regs[0][r_dspaddr], static_cast<uint8_t>(data));
219 105
 #endif
220 106
 
107
+	if (this->m.smp_regs[0][r_dspaddr] <= 0x7F)
108
+		this->dsp.write(this->m.smp_regs[0][r_dspaddr], data);
109
+}
221 110
 
222 111
 //// CPU write
223 112
 
224
-#if SPC_MORE_ACCURACY
225
-static unsigned char const glitch_probs [3] [256] =
226
-{
227
-	0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B,
228
-	0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08,
229
-	0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09,
230
-	0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01,
231
-	0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05,
232
-	0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07,
233
-	0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07,
234
-	0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01,
235
-	0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09,
236
-	0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08,
237
-	0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03,
238
-	0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03,
239
-	0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07,
240
-	0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02,
241
-	0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02,
242
-	0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01,
243
-	
244
-	0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07,
245
-	0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06,
246
-	0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09,
247
-	0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03,
248
-	0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07,
249
-	0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03,
250
-	0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06,
251
-	0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03,
252
-	0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05,
253
-	0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04,
254
-	0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05,
255
-	0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01,
256
-	0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05,
257
-	0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01,
258
-	0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03,
259
-	0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01,
260
-	
261
-	0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A,
262
-	0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A,
263
-	0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A,
264
-	0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09,
265
-	0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09,
266
-	0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02,
267
-	0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07,
268
-	0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04,
269
-	0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A,
270
-	0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07,
271
-	0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04,
272
-	0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02,
273
-	0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06,
274
-	0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03,
275
-	0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02,
276
-	0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03,
277
-};
278
-#endif
279
-
280 113
 // divided into multiple functions to keep rarely-used functionality separate
281 114
 // so often-used functionality can be optimized better by compiler
282 115
 
283 116
 // If write isn't preceded by read, data has this added to it
284
-int const no_read_before_write = 0x2000;
117
+static const int no_read_before_write = 0x2000;
285 118
 
286
-void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr )
119
+void SNES_SPC::cpu_write_smp_reg_(int data, rel_time_t time, int addr)
287 120
 {
288
-	switch ( addr )
121
+	switch (addr)
289 122
 	{
290
-	case r_t0target:
291
-	case r_t1target:
292
-	case r_t2target: {
293
-		Timer* t = &m.timers [addr - r_t0target];
294
-		int period = IF_0_THEN_256( data );
295
-		if ( t->period != period )
296
-		{
297
-			t = run_timer( t, time );
298
-			#if SPC_MORE_ACCURACY
299
-				// Insane behavior when target is written just after counter is
300
-				// clocked and counter matches new period and new period isn't 1, 2, 4, or 8
301
-				if ( t->divider == (period & 0xFF) &&
302
-						t->next_time == time + TIMER_MUL( t, 1 ) &&
303
-						((period - 1) | ~0x0F) & period )
304
-				{
305
-					//dprintf( "SPC pathological timer target write\n" );
306
-					
307
-					// If the period is 3, 5, or 9, there's a probability this behavior won't occur,
308
-					// based on the previous period
309
-					int prob = 0xFF;
310
-					int old_period = t->period & 0xFF;
311
-					if ( period == 3 ) prob = glitch_probs [0] [old_period];
312
-					if ( period == 5 ) prob = glitch_probs [1] [old_period];
313
-					if ( period == 9 ) prob = glitch_probs [2] [old_period];
314
-					
315
-					// The glitch suppresses incrementing of one of the counter bits, based on
316
-					// the lowest set bit in the new period
317
-					int b = 1;
318
-					while ( !(period & b) )
319
-						b <<= 1;
320
-					
321
-					if ( (rand() >> 4 & 0xFF) <= prob )
322
-						t->divider = (t->divider - b) & 0xFF;
323
-				}
324
-			#endif
325
-			t->period = period;
326
-		}
327
-		break;
328
-	}
329
-	
330
-	case r_t0out:
331
-	case r_t1out:
332
-	case r_t2out:
333
-		if ( !SPC_MORE_ACCURACY )
334
-			dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out );
335
-		
336
-		if ( data < no_read_before_write  / 2 )
337
-			run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
338
-		break;
339
-	
340
-	// Registers that act like RAM
341
-	case 0x8:
342
-	case 0x9:
343
-		REGS_IN [addr] = (uint8_t) data;
344
-		break;
345
-	
346
-	case r_test:
347
-		if ( (uint8_t) data != 0x0A )
348
-			dprintf( "SPC wrote to test register\n" );
349
-		break;
350
-	
351
-	case r_control:
352
-		// port clears
353
-		if ( data & 0x10 )
123
+		case r_t0target:
124
+		case r_t1target:
125
+		case r_t2target:
354 126
 		{
355
-			REGS_IN [r_cpuio0] = 0;
356
-			REGS_IN [r_cpuio1] = 0;
357
-		}
358
-		if ( data & 0x20 )
359
-		{
360
-			REGS_IN [r_cpuio2] = 0;
361
-			REGS_IN [r_cpuio3] = 0;
127
+			auto t = &this->m.timers[addr - r_t0target];
128
+			int period = IF_0_THEN_256(data);
129
+			if (t->period != period)
130
+			{
131
+				t = this->run_timer(t, time);
132
+				t->period = period;
133
+			}
134
+			break;
362 135
 		}
363
-		
364
-		// timers
365
-		{
366
-			for ( int i = 0; i < timer_count; i++ )
136
+
137
+		case r_t0out:
138
+		case r_t1out:
139
+		case r_t2out:
140
+			if (data < no_read_before_write  / 2)
141
+				this->run_timer(&this->m.timers[addr - r_t0out], time - 1)->counter = 0;
142
+			break;
143
+
144
+		// Registers that act like RAM
145
+		case 0x8:
146
+		case 0x9:
147
+			this->m.smp_regs[1][addr] = static_cast<uint8_t>(data);
148
+			break;
149
+
150
+		case r_test:
151
+			break;
152
+
153
+		case r_control:
154
+			// port clears
155
+			if (data & 0x10)
156
+			{
157
+				this->m.smp_regs[1][r_cpuio0] = 0;
158
+				this->m.smp_regs[1][r_cpuio1] = 0;
159
+			}
160
+			if (data & 0x20)
161
+			{
162
+				this->m.smp_regs[1][r_cpuio2] = 0;
163
+				this->m.smp_regs[1][r_cpuio3] = 0;
164
+			}
165
+
166
+			// timers
167
+			for (int i = 0; i < timer_count; ++i)
367 168
 			{
368
-				Timer* t = &m.timers [i];
369
-				int enabled = data >> i & 1;
370
-				if ( t->enabled != enabled )
169
+				auto t = &this->m.timers[i];
170
+				bool enabled = !!((data >> i) & 1);
171
+				if (t->enabled != enabled)
371 172
 				{
372
-					t = run_timer( t, time );
173
+					t = this->run_timer(t, time);
373 174
 					t->enabled = enabled;
374
-					if ( enabled )
175
+					if (enabled)
375 176
 					{
376 177
 						t->divider = 0;
377 178
 						t->counter = 0;
378 179
 					}
379 180
 				}
380 181
 			}
381
-		}
382
-		enable_rom( data & 0x80 );
383
-		break;
182
+			this->enable_rom(!!(data & 0x80));
384 183
 	}
385 184
 }
386 185
 
387
-void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr )
186
+void SNES_SPC::cpu_write_smp_reg(int data, rel_time_t time, int addr)
388 187
 {
389
-	if ( addr == r_dspdata ) // 99%
390
-		dsp_write( data, time );
188
+	if (addr == r_dspdata) // 99%
189
+		this->dsp_write(data, time);
391 190
 	else
392
-		cpu_write_smp_reg_( data, time, addr );
191
+		this->cpu_write_smp_reg_(data, time, addr);
393 192
 }
394 193
 
395
-void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time )
194
+void SNES_SPC::cpu_write_high(int data, int i, rel_time_t time)
396 195
 {
397
-	if ( i < rom_size )
196
+	if (i < rom_size)
398 197
 	{
399
-		m.hi_ram [i] = (uint8_t) data;
400
-		if ( m.rom_enabled )
401
-			RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM
198
+		this->m.hi_ram [i] = static_cast<uint8_t>(data);
199
+		if (this->m.rom_enabled)
200
+			this->m.ram.ram[i + rom_addr] = this->m.rom[i]; // restore overwritten ROM
402 201
 	}
403 202
 	else
404 203
 	{
405
-		assert( *(&(RAM [0]) + i + rom_addr) == (uint8_t) data );
406
-		*(&(RAM [0]) + i + rom_addr) = cpu_pad_fill; // restore overwritten padding
407
-		cpu_write( data, i + rom_addr - 0x10000, time );
204
+		assert(this->m.ram.ram[i + rom_addr] == static_cast<uint8_t>(data));
205
+		this->m.ram.ram[i + rom_addr] = cpu_pad_fill; // restore overwritten padding
206
+		this->cpu_write(data, i + rom_addr - 0x10000, time);
408 207
 	}
409 208
 }
410 209
 
411
-int const bits_in_int = CHAR_BIT * sizeof (int);
210
+static const int bits_in_int = CHAR_BIT * sizeof(int);
412 211
 
413
-void SNES_SPC::cpu_write( int data, int addr, rel_time_t time )
212
+void SNES_SPC::cpu_write(int data, int addr, rel_time_t time)
414 213
 {
415
-	MEM_ACCESS( time, addr )
416
-	
417 214
 	// RAM
418
-	RAM [addr] = (uint8_t) data;
215
+	this->m.ram.ram[addr] = static_cast<uint8_t>(data);
419 216
 	int reg = addr - 0xF0;
420
-	if ( reg >= 0 ) // 64%
217
+	if (reg >= 0) // 64%
421 218
 	{
422 219
 		// $F0-$FF
423
-		if ( reg < reg_count ) // 87%
220
+		if (reg < reg_count) // 87%
424 221
 		{
425
-			REGS [reg] = (uint8_t) data;
426
-			
222
+			this->m.smp_regs[0][reg] = static_cast<uint8_t>(data);
223
+
427 224
 			// Ports
428
-			#ifdef SPC_PORT_WRITE_HOOK
429
-				if ( (unsigned) (reg - r_cpuio0) < port_count )
430
-					SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),
431
-							(uint8_t) data, &REGS [r_cpuio0] );
432
-			#endif
433
-			
225
+#ifdef SPC_PORT_WRITE_HOOK
226
+			if (static_cast<unsigned>(reg - r_cpuio0) < port_count)
227
+				SPC_PORT_WRITE_HOOK(this->m.spc_time + time, (reg - r_cpuio0), static_cast<uint8_t>(data), &this->m.smp_regs[0][r_cpuio0]);
228
+#endif
229
+
434 230
 			// Registers other than $F2 and $F4-$F7
435 231
 			//if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
436 232
 			// TODO: this is a bit on the fragile side
437
-			if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
438
-				cpu_write_smp_reg( data, time, reg );
233
+			if (((~0x2F00 << (bits_in_int - 16)) << reg) < 0) // 36%
234
+				this->cpu_write_smp_reg(data, time, reg);
439 235
 		}
440 236
 		// High mem/address wrap-around
441 237
 		else
442 238
 		{
443 239
 			reg -= rom_addr - 0xF0;
444
-			if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around
445
-				cpu_write_high( data, reg, time );
240
+			if (reg >= 0) // 1% in IPL ROM area or address wrapped around
241
+				this->cpu_write_high(data, reg, time);
446 242
 		}
447 243
 	}
448 244
 }
449 245
 
450
-
451 246
 //// CPU read
452 247
 
453
-inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time )
248
+int SNES_SPC::cpu_read_smp_reg(int reg, rel_time_t time)
454 249
 {
455
-	int result = REGS_IN [reg];
250
+	int result = this->m.smp_regs[1][reg];
456 251
 	reg -= r_dspaddr;
457 252
 	// DSP addr and data
458
-	if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3
253
+	if (static_cast<unsigned>(reg) <= 1) // 4% 0xF2 and 0xF3
459 254
 	{
460
-		result = REGS [r_dspaddr];
461
-		if ( (unsigned) reg == 1 )
462
-			result = dsp_read( time ); // 0xF3
255
+		result = this->m.smp_regs[0][r_dspaddr];
256
+		if (static_cast<unsigned>(reg) == 1)
257
+			result = this->dsp_read(time); // 0xF3
463 258
 	}
464 259
 	return result;
465 260
 }
466 261
 
467
-int SNES_SPC::cpu_read( int addr, rel_time_t time )
262
+int SNES_SPC::cpu_read(int addr, rel_time_t time)
468 263
 {
469
-	MEM_ACCESS( time, addr )
470
-	
471 264
 	// RAM
472
-	int result = RAM [addr];
265
+	int result = this->m.ram.ram[addr];
473 266
 	int reg = addr - 0xF0;
474
-	if ( reg >= 0 ) // 40%
267
+	if (reg >= 0) // 40%
475 268
 	{
476 269
 		reg -= 0x10;
477
-		if ( (unsigned) reg >= 0xFF00 ) // 21%
270
+		if (static_cast<unsigned>(reg) >= 0xFF00) // 21%
478 271
 		{
479 272
 			reg += 0x10 - r_t0out;
480
-			
273
+
481 274
 			// Timers
482
-			if ( (unsigned) reg < timer_count ) // 90%
275
+			if (static_cast<unsigned>(reg) < timer_count) // 90%
483 276
 			{
484
-				Timer* t = &m.timers [reg];
485
-				if ( time >= t->next_time )
486
-					t = run_timer_( t, time );
277
+				auto t = &this->m.timers[reg];
278
+				if (time >= t->next_time)
279
+					t = this->run_timer_(t, time);
487 280
 				result = t->counter;
488 281
 				t->counter = 0;
489 282
 			}
490 283
 			// Other registers
491
-			else if ( reg < 0 ) // 10%
492
-			{
493
-				result = cpu_read_smp_reg( reg + r_t0out, time );
494
-			}
284
+			else if (reg < 0) // 10%
285
+				result = this->cpu_read_smp_reg(reg + r_t0out, time);
495 286
 			else // 1%
496 287
 			{
497
-				assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 );
498
-				result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
288
+				assert(reg + (r_t0out + 0xF0 - 0x10000) < 0x100);
289
+				result = this->cpu_read(reg + (r_t0out + 0xF0 - 0x10000), time);
499 290
 			}
500 291
 		}
501 292
 	}
502
-	
293
+
503 294
 	return result;
504 295
 }
505 296
 
506
-
507 297
 //// Run
508 298
 
509 299
 // Prefix and suffix for CPU emulator function
510 300
 #define SPC_CPU_RUN_FUNC \
511
-BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\
512
-{\
513
-	rel_time_t rel_time = m.spc_time - end_time;\
514
-	/*assert( rel_time <= 0 );*/\
515
-	m.spc_time = end_time;\
516
-	m.dsp_time += rel_time;\
517
-	m.timers [0].next_time += rel_time;\
518
-	m.timers [1].next_time += rel_time;\
519
-	m.timers [2].next_time += rel_time;
301
+uint8_t *SNES_SPC::run_until_(time_t end_time) \
302
+{ \
303
+	rel_time_t rel_time = this->m.spc_time - end_time; \
304
+	/*assert( rel_time <= 0 );*/ \
305
+	this->m.spc_time = end_time; \
306
+	this->m.dsp_time += rel_time; \
307
+	this->m.timers[0].next_time += rel_time; \
308
+	this->m.timers[1].next_time += rel_time; \
309
+	this->m.timers[2].next_time += rel_time;
520 310
 
521 311
 #define SPC_CPU_RUN_FUNC_END \
522
-	m.spc_time += rel_time;\
523
-	m.dsp_time -= rel_time;\
524
-	m.timers [0].next_time -= rel_time;\
525
-	m.timers [1].next_time -= rel_time;\
526
-	m.timers [2].next_time -= rel_time;\
527
-	/*assert( m.spc_time >= end_time );*/\
528
-	return &REGS [r_cpuio0];\
312
+	this->m.spc_time += rel_time; \
313
+	this->m.dsp_time -= rel_time; \
314
+	this->m.timers[0].next_time -= rel_time; \
315
+	this->m.timers[1].next_time -= rel_time; \
316
+	this->m.timers[2].next_time -= rel_time; \
317
+	/*assert( m.spc_time >= end_time );*/ \
318
+	return &this->m.smp_regs[0][r_cpuio0]; \
529 319
 }
530 320
 
531
-int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
321
+static const int cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
532 322
 
533
-void SNES_SPC::end_frame( time_t end_time )
323
+void SNES_SPC::end_frame(time_t end_time)
534 324
 {
535 325
 	// Catch CPU up to as close to end as possible. If final instruction
536 326
 	// would exceed end, does NOT execute it and leaves m.spc_time < end.
537
-	if ( end_time > m.spc_time )
538
-		run_until_( end_time );
539
-	
540
-	m.spc_time     -= end_time;
541
-	m.extra_clocks += end_time;
542
-	
327
+	if (end_time > this->m.spc_time)
328
+		this->run_until_(end_time);
329
+
330
+	this->m.spc_time -= end_time;
331
+	this->m.extra_clocks += end_time;
332
+
543 333
 	// Greatest number of clocks early that emulation can stop early due to
544 334
 	// not being able to execute current instruction without going over
545 335
 	// allowed time.
546
-	assert( -cpu_lag_max <= m.spc_time && m.spc_time <= cpu_lag_max );
547
-	
336
+	assert(-cpu_lag_max <= this->m.spc_time && this->m.spc_time <= cpu_lag_max);
337
+
548 338
 	// Catch timers up to CPU
549
-	for ( int i = 0; i < timer_count; i++ )
550
-		run_timer( &m.timers [i], 0 );
551
-	
339
+	for (int i = 0; i < timer_count; ++i)
340
+		this->run_timer(&this->m.timers [i], 0);
341
+
552 342
 	// Catch DSP up to CPU
553
-	if ( m.dsp_time < 0 )
554
-	{
555
-		RUN_DSP( 0, max_reg_time );
556
-	}
557
-	
343
+	if (this->m.dsp_time < 0)
344
+		this->RUN_DSP(0);
345
+
558 346
 	// Save any extra samples beyond what should be generated
559
-	if ( m.buf_begin )
560
-		save_extra();
347
+	if (this->m.buf_begin)
348
+		this->save_extra();
561 349
 }
562 350
 
563 351
 // Inclusion here allows static memory access functions and better optimization
Browse code

Import actual code.

Naram Qashat authored on 2013/03/26 02:41:19
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,564 @@
1
+// Core SPC emulation: CPU, timers, SMP registers, memory
2
+
3
+// snes_spc 0.9.0. http://www.slack.net/~ant/
4
+
5
+#include "SNES_SPC.h"
6
+
7
+#include <string.h>
8
+
9
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
10
+can redistribute it and/or modify it under the terms of the GNU Lesser
11
+General Public License as published by the Free Software Foundation; either
12
+version 2.1 of the License, or (at your option) any later version. This
13
+module is distributed in the hope that it will be useful, but WITHOUT ANY
14
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16
+details. You should have received a copy of the GNU Lesser General Public
17
+License along with this module; if not, write to the Free Software Foundation,
18
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19
+
20
+#include "blargg_source.h"
21
+
22
+#define RAM         (m.ram.ram)
23
+#define REGS        (m.smp_regs [0])
24
+#define REGS_IN     (m.smp_regs [1])
25
+
26
+// (n ? n : 256)
27
+#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
28
+
29
+// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which
30
+// do crazy echo buffer accesses.
31
+#ifndef SPC_MORE_ACCURACY
32
+	#define SPC_MORE_ACCURACY 0
33
+#endif
34
+
35
+#ifdef BLARGG_ENABLE_OPTIMIZER
36
+	#include BLARGG_ENABLE_OPTIMIZER
37
+#endif
38
+
39
+
40
+//// Timers
41
+
42
+#if SPC_DISABLE_TEMPO
43
+	#define TIMER_DIV( t, n ) ((n) >> t->prescaler)
44
+	#define TIMER_MUL( t, n ) ((n) << t->prescaler)
45
+#else
46
+	#define TIMER_DIV( t, n ) ((n) / t->prescaler)
47
+	#define TIMER_MUL( t, n ) ((n) * t->prescaler)
48
+#endif
49
+
50
+SNES_SPC::Timer* SNES_SPC::run_timer_( Timer* t, rel_time_t time )
51
+{
52
+	int elapsed = TIMER_DIV( t, time - t->next_time ) + 1;
53
+	t->next_time += TIMER_MUL( t, elapsed );
54
+	
55
+	if ( t->enabled )
56
+	{
57
+		int remain = IF_0_THEN_256( t->period - t->divider );
58
+		int divider = t->divider + elapsed;
59
+		int over = elapsed - remain;
60
+		if ( over >= 0 )
61
+		{
62
+			int n = over / t->period;
63
+			t->counter = (t->counter + 1 + n) & 0x0F;
64
+			divider = over - n * t->period;
65
+		}
66
+		t->divider = (uint8_t) divider;
67
+	}
68
+	return t;
69
+}
70
+
71
+inline SNES_SPC::Timer* SNES_SPC::run_timer( Timer* t, rel_time_t time )
72
+{
73
+	if ( time >= t->next_time )
74
+		t = run_timer_( t, time );
75
+	return t;
76
+}
77
+
78
+
79
+//// ROM
80
+
81
+void SNES_SPC::enable_rom( int enable )
82
+{
83
+	if ( m.rom_enabled != enable )
84
+	{
85
+		m.rom_enabled = dsp.rom_enabled = enable;
86
+		if ( enable )
87
+			memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram );
88
+		memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size );
89
+		// TODO: ROM can still get overwritten when DSP writes to echo buffer
90
+	}
91
+}
92
+
93
+
94
+//// DSP
95
+
96
+#if SPC_LESS_ACCURATE
97
+	int const max_reg_time = 29;
98
+	
99
+	signed char const SNES_SPC::reg_times_ [256] =
100
+	{
101
+		 -1,  0,-11,-10,-15,-11, -2, -2,  4,  3, 14, 14, 26, 26, 14, 22,
102
+		  2,  3,  0,  1,-12,  0,  1,  1,  7,  6, 14, 14, 27, 14, 14, 23,
103
+		  5,  6,  3,  4, -1,  3,  4,  4, 10,  9, 14, 14, 26, -5, 14, 23,
104
+		  8,  9,  6,  7,  2,  6,  7,  7, 13, 12, 14, 14, 27, -4, 14, 24,
105
+		 11, 12,  9, 10,  5,  9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24,
106
+		 14, 15, 12, 13,  8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24,
107
+		 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25,
108
+		 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25,
109
+		 
110
+		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
111
+		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
112
+		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
113
+		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
114
+		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
115
+		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
116
+		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
117
+		 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
118
+	};
119
+	
120
+	#define RUN_DSP( time, offset ) \
121
+		int count = (time) - (offset) - m.dsp_time;\
122
+		if ( count >= 0 )\
123
+		{\
124
+			int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\
125
+			m.dsp_time += clock_count;\
126
+			dsp.run( clock_count );\
127
+		}
128
+#else
129
+	#define RUN_DSP( time, offset ) \
130
+		{\
131
+			int count = (time) - m.dsp_time;\
132
+			if ( !SPC_MORE_ACCURACY || count )\
133
+			{\
134
+				assert( count > 0 );\
135
+				m.dsp_time = (time);\
136
+				dsp.run( count );\
137
+			}\
138
+		}
139
+#endif
140
+
141
+int SNES_SPC::dsp_read( rel_time_t time )
142
+{
143
+	RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
144
+	
145
+	int result = dsp.read( REGS [r_dspaddr] & 0x7F );
146
+	
147
+	#ifdef SPC_DSP_READ_HOOK
148
+		SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
149
+	#endif
150
+	
151
+	return result;
152
+}
153
+
154
+inline void SNES_SPC::dsp_write( int data, rel_time_t time )
155
+{
156
+	RUN_DSP( time, reg_times [REGS [r_dspaddr]] )
157
+	#if SPC_LESS_ACCURATE
158
+		else if ( m.dsp_time == skipping_time )
159
+		{
160
+			int r = REGS [r_dspaddr];
161
+			if ( r == SPC_DSP::r_kon )
162
+				m.skipped_kon |= data & ~dsp.read( SPC_DSP::r_koff );
163
+			
164
+			if ( r == SPC_DSP::r_koff )
165
+			{
166
+				m.skipped_koff |= data;
167
+				m.skipped_kon &= ~data;
168
+			}
169
+		}
170
+	#endif
171
+	
172
+	#ifdef SPC_DSP_WRITE_HOOK
173
+		SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
174
+	#endif
175
+	
176
+	if ( REGS [r_dspaddr] <= 0x7F )
177
+		dsp.write( REGS [r_dspaddr], data );
178
+	else if ( !SPC_MORE_ACCURACY )
179
+		dprintf( "SPC wrote to DSP register > $7F\n" );
180
+}
181
+
182
+
183
+//// Memory access extras
184
+
185
+#if SPC_MORE_ACCURACY
186
+	#define MEM_ACCESS( time, addr ) \
187
+	{\
188
+		if ( time >= m.dsp_time )\
189
+		{\
190
+			RUN_DSP( time, max_reg_time );\
191
+		}\
192
+	}
193
+#elif !defined (NDEBUG)
194
+	// Debug-only check for read/write within echo buffer, since this might result in
195
+	// inaccurate emulation due to the DSP not being caught up to the present.
196
+	
197
+	bool SNES_SPC::check_echo_access( int addr )
198
+	{
199
+		if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) )
200
+		{
201
+			int start = 0x100 * dsp.read( SPC_DSP::r_esa );
202
+			int size  = 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
203
+			int end   = start + (size ? size : 4);
204
+			if ( start <= addr && addr < end )
205
+			{
206
+				if ( !m.echo_accessed )
207
+				{
208
+					m.echo_accessed = 1;
209
+					return true;
210
+				}
211
+			}
212
+		}
213
+		return false;
214
+	}
215
+	
216
+	#define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
217
+#else
218
+	#define MEM_ACCESS( time, addr )
219
+#endif
220
+
221
+
222
+//// CPU write
223
+
224
+#if SPC_MORE_ACCURACY
225
+static unsigned char const glitch_probs [3] [256] =
226
+{
227
+	0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B,
228
+	0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08,
229
+	0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09,
230
+	0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01,
231
+	0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05,
232
+	0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07,
233
+	0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07,
234
+	0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01,
235
+	0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09,
236
+	0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08,
237
+	0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03,
238
+	0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03,
239
+	0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07,
240
+	0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02,
241
+	0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02,
242
+	0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01,
243
+	
244
+	0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07,
245
+	0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06,
246
+	0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09,
247
+	0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03,
248
+	0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07,
249
+	0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03,
250
+	0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06,
251
+	0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03,
252
+	0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05,
253
+	0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04,
254
+	0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05,
255
+	0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01,
256
+	0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05,
257
+	0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01,
258
+	0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03,
259
+	0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01,
260
+	
261
+	0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A,
262
+	0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A,
263
+	0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A,
264
+	0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09,
265
+	0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09,
266
+	0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02,
267
+	0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07,
268
+	0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04,
269
+	0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A,
270
+	0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07,
271
+	0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04,
272
+	0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02,
273
+	0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06,
274
+	0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03,
275
+	0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02,
276
+	0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03,
277
+};
278
+#endif
279
+
280
+// divided into multiple functions to keep rarely-used functionality separate
281
+// so often-used functionality can be optimized better by compiler
282
+
283
+// If write isn't preceded by read, data has this added to it
284
+int const no_read_before_write = 0x2000;
285
+
286
+void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr )
287
+{
288
+	switch ( addr )
289
+	{
290
+	case r_t0target:
291
+	case r_t1target:
292
+	case r_t2target: {
293
+		Timer* t = &m.timers [addr - r_t0target];
294
+		int period = IF_0_THEN_256( data );
295
+		if ( t->period != period )
296
+		{
297
+			t = run_timer( t, time );
298
+			#if SPC_MORE_ACCURACY
299
+				// Insane behavior when target is written just after counter is
300
+				// clocked and counter matches new period and new period isn't 1, 2, 4, or 8
301
+				if ( t->divider == (period & 0xFF) &&
302
+						t->next_time == time + TIMER_MUL( t, 1 ) &&
303
+						((period - 1) | ~0x0F) & period )
304
+				{
305
+					//dprintf( "SPC pathological timer target write\n" );
306
+					
307
+					// If the period is 3, 5, or 9, there's a probability this behavior won't occur,
308
+					// based on the previous period
309
+					int prob = 0xFF;
310
+					int old_period = t->period & 0xFF;
311
+					if ( period == 3 ) prob = glitch_probs [0] [old_period];
312
+					if ( period == 5 ) prob = glitch_probs [1] [old_period];
313
+					if ( period == 9 ) prob = glitch_probs [2] [old_period];
314
+					
315
+					// The glitch suppresses incrementing of one of the counter bits, based on
316
+					// the lowest set bit in the new period
317
+					int b = 1;
318
+					while ( !(period & b) )
319
+						b <<= 1;
320
+					
321
+					if ( (rand() >> 4 & 0xFF) <= prob )
322
+						t->divider = (t->divider - b) & 0xFF;
323
+				}
324
+			#endif
325
+			t->period = period;
326
+		}
327
+		break;
328
+	}
329
+	
330
+	case r_t0out:
331
+	case r_t1out:
332
+	case r_t2out:
333
+		if ( !SPC_MORE_ACCURACY )
334
+			dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out );
335
+		
336
+		if ( data < no_read_before_write  / 2 )
337
+			run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
338
+		break;
339
+	
340
+	// Registers that act like RAM
341
+	case 0x8:
342
+	case 0x9:
343
+		REGS_IN [addr] = (uint8_t) data;
344
+		break;
345
+	
346
+	case r_test:
347
+		if ( (uint8_t) data != 0x0A )
348
+			dprintf( "SPC wrote to test register\n" );
349
+		break;
350
+	
351
+	case r_control:
352
+		// port clears
353
+		if ( data & 0x10 )
354
+		{
355
+			REGS_IN [r_cpuio0] = 0;
356
+			REGS_IN [r_cpuio1] = 0;
357
+		}
358
+		if ( data & 0x20 )
359
+		{
360
+			REGS_IN [r_cpuio2] = 0;
361
+			REGS_IN [r_cpuio3] = 0;
362
+		}
363
+		
364
+		// timers
365
+		{
366
+			for ( int i = 0; i < timer_count; i++ )
367
+			{
368
+				Timer* t = &m.timers [i];
369
+				int enabled = data >> i & 1;
370
+				if ( t->enabled != enabled )
371
+				{
372
+					t = run_timer( t, time );
373
+					t->enabled = enabled;
374
+					if ( enabled )
375
+					{
376
+						t->divider = 0;
377
+						t->counter = 0;
378
+					}
379
+				}
380
+			}
381
+		}
382
+		enable_rom( data & 0x80 );
383
+		break;
384
+	}
385
+}
386
+
387
+void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr )
388
+{
389
+	if ( addr == r_dspdata ) // 99%
390
+		dsp_write( data, time );
391
+	else
392
+		cpu_write_smp_reg_( data, time, addr );
393
+}
394
+
395
+void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time )
396
+{
397
+	if ( i < rom_size )
398
+	{
399
+		m.hi_ram [i] = (uint8_t) data;
400
+		if ( m.rom_enabled )
401
+			RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM
402
+	}
403
+	else
404
+	{
405
+		assert( *(&(RAM [0]) + i + rom_addr) == (uint8_t) data );
406
+		*(&(RAM [0]) + i + rom_addr) = cpu_pad_fill; // restore overwritten padding
407
+		cpu_write( data, i + rom_addr - 0x10000, time );
408
+	}
409
+}
410
+
411
+int const bits_in_int = CHAR_BIT * sizeof (int);
412
+
413
+void SNES_SPC::cpu_write( int data, int addr, rel_time_t time )
414
+{
415
+	MEM_ACCESS( time, addr )
416
+	
417
+	// RAM
418
+	RAM [addr] = (uint8_t) data;
419
+	int reg = addr - 0xF0;
420
+	if ( reg >= 0 ) // 64%
421
+	{
422
+		// $F0-$FF
423
+		if ( reg < reg_count ) // 87%
424
+		{
425
+			REGS [reg] = (uint8_t) data;
426
+			
427
+			// Ports
428
+			#ifdef SPC_PORT_WRITE_HOOK
429
+				if ( (unsigned) (reg - r_cpuio0) < port_count )
430
+					SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),
431
+							(uint8_t) data, &REGS [r_cpuio0] );
432
+			#endif
433
+			
434
+			// Registers other than $F2 and $F4-$F7
435
+			//if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
436
+			// TODO: this is a bit on the fragile side
437
+			if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
438
+				cpu_write_smp_reg( data, time, reg );
439
+		}
440
+		// High mem/address wrap-around
441
+		else
442
+		{
443
+			reg -= rom_addr - 0xF0;
444
+			if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around
445
+				cpu_write_high( data, reg, time );
446
+		}
447
+	}
448
+}
449
+
450
+
451
+//// CPU read
452
+
453
+inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time )
454
+{
455
+	int result = REGS_IN [reg];
456
+	reg -= r_dspaddr;
457
+	// DSP addr and data
458
+	if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3
459
+	{
460
+		result = REGS [r_dspaddr];
461
+		if ( (unsigned) reg == 1 )
462
+			result = dsp_read( time ); // 0xF3
463
+	}
464
+	return result;
465
+}
466
+
467
+int SNES_SPC::cpu_read( int addr, rel_time_t time )
468
+{
469
+	MEM_ACCESS( time, addr )
470
+	
471
+	// RAM
472
+	int result = RAM [addr];
473
+	int reg = addr - 0xF0;
474
+	if ( reg >= 0 ) // 40%
475
+	{
476
+		reg -= 0x10;
477
+		if ( (unsigned) reg >= 0xFF00 ) // 21%
478
+		{
479
+			reg += 0x10 - r_t0out;
480
+			
481
+			// Timers
482
+			if ( (unsigned) reg < timer_count ) // 90%
483
+			{
484
+				Timer* t = &m.timers [reg];
485
+				if ( time >= t->next_time )
486
+					t = run_timer_( t, time );
487
+				result = t->counter;
488
+				t->counter = 0;
489
+			}
490
+			// Other registers
491
+			else if ( reg < 0 ) // 10%
492
+			{
493
+				result = cpu_read_smp_reg( reg + r_t0out, time );
494
+			}
495
+			else // 1%
496
+			{
497
+				assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 );
498
+				result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
499
+			}
500
+		}
501
+	}
502
+	
503
+	return result;
504
+}
505
+
506
+
507
+//// Run
508
+
509
+// Prefix and suffix for CPU emulator function
510
+#define SPC_CPU_RUN_FUNC \
511
+BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\
512
+{\
513
+	rel_time_t rel_time = m.spc_time - end_time;\
514
+	/*assert( rel_time <= 0 );*/\
515
+	m.spc_time = end_time;\
516
+	m.dsp_time += rel_time;\
517
+	m.timers [0].next_time += rel_time;\
518
+	m.timers [1].next_time += rel_time;\
519
+	m.timers [2].next_time += rel_time;
520
+
521
+#define SPC_CPU_RUN_FUNC_END \
522
+	m.spc_time += rel_time;\
523
+	m.dsp_time -= rel_time;\
524
+	m.timers [0].next_time -= rel_time;\
525
+	m.timers [1].next_time -= rel_time;\
526
+	m.timers [2].next_time -= rel_time;\
527
+	/*assert( m.spc_time >= end_time );*/\
528
+	return &REGS [r_cpuio0];\
529
+}
530
+
531
+int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
532
+
533
+void SNES_SPC::end_frame( time_t end_time )
534
+{
535
+	// Catch CPU up to as close to end as possible. If final instruction
536
+	// would exceed end, does NOT execute it and leaves m.spc_time < end.
537
+	if ( end_time > m.spc_time )
538
+		run_until_( end_time );
539
+	
540
+	m.spc_time     -= end_time;
541
+	m.extra_clocks += end_time;
542
+	
543
+	// Greatest number of clocks early that emulation can stop early due to
544
+	// not being able to execute current instruction without going over
545
+	// allowed time.
546
+	assert( -cpu_lag_max <= m.spc_time && m.spc_time <= cpu_lag_max );
547
+	
548
+	// Catch timers up to CPU
549
+	for ( int i = 0; i < timer_count; i++ )
550
+		run_timer( &m.timers [i], 0 );
551
+	
552
+	// Catch DSP up to CPU
553
+	if ( m.dsp_time < 0 )
554
+	{
555
+		RUN_DSP( 0, max_reg_time );
556
+	}
557
+	
558
+	// Save any extra samples beyond what should be generated
559
+	if ( m.buf_begin )
560
+		save_extra();
561
+}
562
+
563
+// Inclusion here allows static memory access functions and better optimization
564
+#include "SPC_CPU.h"