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.
| 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" |
* 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).
| ... | ... |
@@ -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 |
| ... | ... |
@@ -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) |
| ... | ... |
@@ -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, ®S [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 ®S [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 |
| 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, ®S [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 ®S [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" |