| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
|
| 25 |
|
| 26 |
|
| 27 |
|
| 28 |
#include "YM2413NukeYKT.hh" |
| 29 |
|
| 30 |
#include "Math.hh" |
| 31 |
#include "cstd.hh" |
| 32 |
#include "enumerate.hh" |
| 33 |
#include "narrow.hh" |
| 34 |
#include "one_of.hh" |
| 35 |
#include "ranges.hh" |
| 36 |
#include "serialize.hh" |
| 37 |
#include "unreachable.hh" |
| 38 |
#include "xrange.hh" |
| 39 |
|
| 40 |
#include <algorithm> |
| 41 |
#include <array> |
| 42 |
#include <cstring> |
| 43 |
|
| 44 |
namespace openmsx { |
| 45 |
namespace YM2413NukeYKT { |
| 46 |
|
| 47 |
[[nodiscard]] static constexpr bool is_rm_cycle(int cycle) |
| 48 |
{ |
| 49 |
return (11 <= cycle) && (cycle <= 16); |
| 50 |
} |
| 51 |
[[nodiscard]] static constexpr YM2413::RmNum rm_for_cycle(int cycle) |
| 52 |
{ |
| 53 |
return static_cast<YM2413::RmNum>(cycle - 11); |
| 54 |
} |
| 55 |
|
| 56 |
static constexpr auto logSinTab = [] { |
| 57 |
std::array<uint16_t, 256> result = {}; |
| 58 |
|
| 59 |
for (int i = 0; i < 256; ++i) { |
| 60 |
result[i] = narrow_cast<uint16_t>(cstd::round(-cstd::log2<8, 3>(cstd::sin<2>((double(i) + 0.5) * Math::pi / 256.0 / 2.0)) * 256.0)); |
| 61 |
} |
| 62 |
return result; |
| 63 |
}(); |
| 64 |
static constexpr auto expTab = [] { |
| 65 |
std::array<uint16_t, 256> result = {}; |
| 66 |
|
| 67 |
for (int i = 0; i < 256; ++i) { |
| 68 |
result[i] = narrow_cast<uint16_t>(cstd::round((cstd::exp2<6>(double(255 - i) / 256.0)) * 1024.0)); |
| 69 |
} |
| 70 |
return result; |
| 71 |
}(); |
| 72 |
|
| 73 |
constexpr std::array<YM2413::Patch, 15> YM2413::m_patches = { |
| 74 |
YM2413::Patch{0x1e, 2, 7, {0, 0}, {1, 1}, {1, 1}, {1, 0}, {0x1, 0x1}, {0, 0}, {0xd, 0x7}, {0x0, 0x8}, {0x0, 0x1}, {0x0, 0x7}}, |
| 75 |
YM2413::Patch{0x1a, 1, 5, {0, 0}, {0, 1}, {0, 0}, {1, 0}, {0x3, 0x1}, {0, 0}, {0xd, 0xf}, {0x8, 0x7}, {0x2, 0x1}, {0x3, 0x3}}, |
| 76 |
YM2413::Patch{0x19, 0, 0, {0, 0}, {0, 0}, {0, 0}, {1, 0}, {0x3, 0x1}, {2, 0}, {0xf, 0xc}, {0x2, 0x4}, {0x1, 0x2}, {0x1, 0x3}}, |
| 77 |
YM2413::Patch{0x0e, 0, 7, {0, 0}, {0, 1}, {1, 1}, {1, 0}, {0x1, 0x1}, {0, 0}, {0xa, 0x6}, {0x8, 0x4}, {0x7, 0x2}, {0x0, 0x7}}, |
| 78 |
YM2413::Patch{0x1e, 0, 6, {0, 0}, {0, 0}, {1, 1}, {1, 0}, {0x2, 0x1}, {0, 0}, {0xe, 0x7}, {0x0, 0x6}, {0x0, 0x2}, {0x0, 0x8}}, |
| 79 |
YM2413::Patch{0x16, 0, 5, {0, 0}, {0, 0}, {1, 1}, {1, 0}, {0x1, 0x2}, {0, 0}, {0xe, 0x7}, {0x0, 0x1}, {0x0, 0x1}, {0x0, 0x8}}, |
| 80 |
YM2413::Patch{0x1d, 0, 7, {0, 0}, {0, 1}, {1, 1}, {0, 0}, {0x1, 0x1}, {0, 0}, {0x8, 0x8}, {0x2, 0x1}, {0x1, 0x0}, {0x0, 0x7}}, |
| 81 |
YM2413::Patch{0x2d, 2, 4, {0, 0}, {0, 0}, {1, 1}, {0, 0}, {0x3, 0x1}, {0, 0}, {0xa, 0x7}, {0x2, 0x2}, {0x0, 0x0}, {0x0, 0x7}}, |
| 82 |
YM2413::Patch{0x1b, 0, 6, {0, 0}, {1, 1}, {1, 1}, {0, 0}, {0x1, 0x1}, {0, 0}, {0x6, 0x6}, {0x4, 0x5}, {0x1, 0x1}, {0x0, 0x7}}, |
| 83 |
YM2413::Patch{0x0b, 3, 0, {0, 0}, {1, 1}, {0, 1}, {0, 0}, {0x1, 0x1}, {0, 0}, {0x8, 0xf}, {0x5, 0x7}, {0x7, 0x0}, {0x1, 0x7}}, |
| 84 |
YM2413::Patch{0x03, 2, 1, {0, 0}, {0, 0}, {0, 0}, {1, 0}, {0x3, 0x1}, {2, 0}, {0xf, 0xe}, {0xa, 0x4}, {0x1, 0x0}, {0x0, 0x4}}, |
| 85 |
YM2413::Patch{0x24, 0, 7, {0, 1}, {0, 1}, {0, 0}, {1, 0}, {0x7, 0x1}, {0, 0}, {0xf, 0xf}, {0x8, 0x8}, {0x2, 0x1}, {0x2, 0x2}}, |
| 86 |
YM2413::Patch{0x0c, 0, 5, {0, 0}, {1, 1}, {1, 0}, {0, 1}, {0x1, 0x0}, {0, 0}, {0xc, 0xf}, {0x2, 0x5}, {0x2, 0x4}, {0x0, 0x2}}, |
| 87 |
YM2413::Patch{0x15, 0, 3, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x1, 0x1}, {1, 0}, {0xc, 0x9}, {0x9, 0x5}, {0x0, 0x0}, {0x3, 0x2}}, |
| 88 |
YM2413::Patch{0x09, 0, 3, {0, 0}, {1, 1}, {1, 0}, {0, 0}, {0x1, 0x1}, {2, 0}, {0xf, 0xe}, {0x1, 0x4}, {0x4, 0x1}, {0x0, 0x3}}, |
| 89 |
}; |
| 90 |
|
| 91 |
constexpr array_with_enum_index<YM2413::RmNum, YM2413::Patch> YM2413::r_patches = { |
| 92 |
YM2413::Patch{0x18, 1, 7, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x1, 0x0}, {0, 0}, {0xd, 0x0}, {0xf, 0x0}, {0x6, 0x0}, {0xa, 0x0}}, |
| 93 |
YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x1, 0x0}, {0, 0}, {0xc, 0x0}, {0x8, 0x0}, {0xa, 0x0}, {0x7, 0x0}}, |
| 94 |
YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x5, 0x0}, {0, 0}, {0xf, 0x0}, {0x8, 0x0}, {0x5, 0x0}, {0x9, 0x0}}, |
| 95 |
YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x0, 0x1}, {0, 0}, {0x0, 0xf}, {0x0, 0x8}, {0x0, 0x6}, {0x0, 0xd}}, |
| 96 |
YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x0, 0x1}, {0, 0}, {0x0, 0xd}, {0x0, 0x8}, {0x0, 0x4}, {0x0, 0x8}}, |
| 97 |
YM2413::Patch{0x00, 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0x0, 0x1}, {0, 0}, {0x0, 0xa}, {0x0, 0xa}, {0x0, 0x5}, {0x0, 0x5}}, |
| 98 |
}; |
| 99 |
|
| 100 |
static constexpr std::array<uint8_t, 18> CH_OFFSET = { |
| 101 |
1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 8, 6, 7, 8, 0 |
| 102 |
}; |
| 103 |
|
| 104 |
static constexpr std::array<int8_t, 8> VIB_TAB = {0, 1, 2, 1, 0, -1, -2, -1}; |
| 105 |
|
| 106 |
|
| 107 |
|
| 108 |
|
| 109 |
using B4 = std::array<bool, 4>; |
| 110 |
constexpr std::array<B4, 4> EG_STEP_HI = { |
| 111 |
B4{false, true, true, true}, |
| 112 |
B4{false, false, false, true}, |
| 113 |
B4{false, false, true, true}, |
| 114 |
B4{false, false, false, false}, |
| 115 |
}; |
| 116 |
|
| 117 |
|
| 118 |
[[nodiscard]] constexpr auto computeAttackTable() |
| 119 |
{ |
| 120 |
std::array<std::array<std::array<uint8_t, 64 >, 4 >, 14 > result = {}; |
| 121 |
for (auto timer_shift : xrange(14)) { |
| 122 |
for (auto timer : xrange(4)) { |
| 123 |
for (auto rate : xrange(64)) { |
| 124 |
uint8_t rate_hi = rate >> 2; |
| 125 |
uint8_t rate_lo = rate & 3; |
| 126 |
bool inc_hi = EG_STEP_HI[timer][rate_lo]; |
| 127 |
bool inc_lo = [&]() -> bool { |
| 128 |
if ((rate_hi < 12) && (rate_hi != 0)) { |
| 129 |
switch (timer_shift + rate_hi) { |
| 130 |
case 12: return true; |
| 131 |
case 13: return rate_lo & 2; |
| 132 |
case 14: return rate_lo & 1; |
| 133 |
} |
| 134 |
} |
| 135 |
return false; |
| 136 |
}(); |
| 137 |
result[timer_shift][timer][rate] = [&]() { |
| 138 |
if (rate_hi != 0xf) { |
| 139 |
int32_t shift = (rate_hi < 12) |
| 140 |
? inc_lo |
| 141 |
: (rate_hi - 11 + inc_hi); |
| 142 |
if (shift > 0) { |
| 143 |
return 5 - shift; |
| 144 |
} |
| 145 |
} |
| 146 |
return 31; |
| 147 |
}(); |
| 148 |
} |
| 149 |
} |
| 150 |
} |
| 151 |
return result; |
| 152 |
} |
| 153 |
static constexpr auto attack = computeAttackTable(); |
| 154 |
|
| 155 |
|
| 156 |
[[nodiscard]] constexpr auto computeReleaseTable() |
| 157 |
{ |
| 158 |
std::array<std::array<std::array<std::array<uint8_t, 64 >, 4 >, 4 >, 14 > result = {}; |
| 159 |
for (auto timer_shift : xrange(14)) { |
| 160 |
for (auto timer : xrange(4)) { |
| 161 |
for (auto rate : xrange(64)) { |
| 162 |
uint8_t rate_hi = rate >> 2; |
| 163 |
uint8_t rate_lo = rate & 3; |
| 164 |
bool inc_hi = EG_STEP_HI[timer][rate_lo]; |
| 165 |
bool inc_lo = [&]() -> bool { |
| 166 |
if ((rate_hi < 12) && (rate_hi != 0)) { |
| 167 |
switch (timer_shift + rate_hi) { |
| 168 |
case 12: return true; |
| 169 |
case 13: return rate_lo & 2; |
| 170 |
case 14: return rate_lo & 1; |
| 171 |
} |
| 172 |
} |
| 173 |
return false; |
| 174 |
}(); |
| 175 |
for (auto counter_state : xrange(4)) { |
| 176 |
bool i0 = rate_hi == 15 || (rate_hi == 14 && inc_hi); |
| 177 |
bool i1 = (rate_hi == 14 && !inc_hi) || |
| 178 |
(rate_hi == 13 && inc_hi) || |
| 179 |
(rate_hi == 13 && !inc_hi && (counter_state & 1)) || |
| 180 |
(rate_hi == 12 && inc_hi && (counter_state & 1)) || |
| 181 |
(rate_hi == 12 && !inc_hi && (counter_state == 3)) || |
| 182 |
(inc_lo && (counter_state == 3)); |
| 183 |
int i01 = (i0 << 1) | i1; |
| 184 |
result[timer_shift][timer][counter_state][rate] = i01; |
| 185 |
} |
| 186 |
} |
| 187 |
} |
| 188 |
} |
| 189 |
return result; |
| 190 |
} |
| 191 |
|
| 192 |
|
| 193 |
template<typename Table> |
| 194 |
[[nodiscard]] constexpr auto compressTable(const Table& table) |
| 195 |
{ |
| 196 |
struct CompressedTable { |
| 197 |
std::array<std::array<std::array<uint8_t, 4>, 4>, 14> index; |
| 198 |
std::array<std::array<uint8_t, 64>, 64> data; |
| 199 |
}; |
| 200 |
CompressedTable result = {}; |
| 201 |
size_t out_n = 0; |
| 202 |
for (auto i : xrange(14)) { |
| 203 |
for (auto j : xrange(4)) { |
| 204 |
for (auto k : xrange(4)) { |
| 205 |
auto haystack = std::span{result.data.data(), out_n}; |
| 206 |
auto it = std::ranges::find_if(haystack, [&](const auto& candidate) { |
| 207 |
return std::ranges::equal(table[i][j][k], candidate); |
| 208 |
}); |
| 209 |
if (it != haystack.end()) { |
| 210 |
result.index[i][j][k] = narrow<uint8_t>(std::ranges::distance(haystack.begin(), it)); |
| 211 |
} else { |
| 212 |
copy_to_range(table[i][j][k], result.data[out_n]); |
| 213 |
result.index[i][j][k] = out_n; |
| 214 |
++out_n; |
| 215 |
} |
| 216 |
} |
| 217 |
} |
| 218 |
} |
| 219 |
return result; |
| 220 |
} |
| 221 |
static constexpr auto release = compressTable(computeReleaseTable()); |
| 222 |
|
| 223 |
|
| 224 |
YM2413::YM2413() |
| 225 |
: attackPtr (attack[0][0]) |
| 226 |
, releasePtr(release.data[0]) |
| 227 |
{ |
| 228 |
|
| 229 |
copy_to_range(m_patches, subspan(patches, 1)); |
| 230 |
reset(); |
| 231 |
} |
| 232 |
|
| 233 |
void YM2413::reset() |
| 234 |
{ |
| 235 |
for (auto& w : writes) w.port = uint8_t(-1); |
| 236 |
write_fm_cycle = uint8_t(-1); |
| 237 |
write_data = fm_data = write_address = 0; |
| 238 |
fast_fm_rewrite = test_mode_active = false; |
| 239 |
|
| 240 |
eg_counter_state = 3; |
| 241 |
eg_timer = eg_timer_shift = eg_timer_shift_lock = eg_timer_lock = 0; |
| 242 |
attackPtr = attack[eg_timer_shift_lock][eg_timer_lock]; |
| 243 |
auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state]; |
| 244 |
releasePtr = release.data[idx]; |
| 245 |
std::ranges::fill(eg_state, EgState::release); |
| 246 |
std::ranges::fill(eg_level, 0x7f); |
| 247 |
std::ranges::fill(eg_dokon, false); |
| 248 |
eg_rate[0] = eg_rate[1] = 0; |
| 249 |
eg_sl[0] = eg_sl[1] = eg_out[0] = eg_out[1] = 0; |
| 250 |
eg_timer_shift_stop = false; |
| 251 |
eg_kon[0] = eg_kon[1] = eg_off[0] = eg_off[1] = false; |
| 252 |
|
| 253 |
std::ranges::fill(pg_phase, 0); |
| 254 |
std::ranges::fill(op_fb1, 0); |
| 255 |
std::ranges::fill(op_fb2, 0); |
| 256 |
|
| 257 |
op_mod = 0; |
| 258 |
op_phase[0] = op_phase[1] = 0; |
| 259 |
|
| 260 |
lfo_counter = lfo_am_counter = 0; |
| 261 |
lfo_vib_counter = lfo_am_out = 0; |
| 262 |
lfo_vib = VIB_TAB[lfo_vib_counter]; |
| 263 |
lfo_am_step = lfo_am_dir = false; |
| 264 |
|
| 265 |
std::ranges::fill(fnum, 0); |
| 266 |
std::ranges::fill(block, 0); |
| 267 |
std::ranges::fill(vol8, 0); |
| 268 |
std::ranges::fill(inst, 0); |
| 269 |
std::ranges::fill(sk_on, 0); |
| 270 |
for (auto i : xrange(9)) { |
| 271 |
p_inst[i] = &patches[inst[i]]; |
| 272 |
changeFnumBlock(i); |
| 273 |
} |
| 274 |
rhythm = testMode = 0; |
| 275 |
patches[0] = Patch(); |
| 276 |
c_dcm[0] = c_dcm[1] = c_dcm[2] = 0; |
| 277 |
|
| 278 |
rm_noise = 1; |
| 279 |
rm_tc_bits = 0; |
| 280 |
|
| 281 |
delay6 = delay7 = delay10 = delay11 = delay12 = 0; |
| 282 |
|
| 283 |
std::ranges::fill(regs, 0); |
| 284 |
latch = 0; |
| 285 |
} |
| 286 |
|
| 287 |
template<uint32_t CYCLES> ALWAYS_INLINE const YM2413::Patch& YM2413::preparePatch1(bool use_rm_patches) const |
| 288 |
{ |
| 289 |
return (is_rm_cycle(CYCLES) && use_rm_patches) |
| 290 |
? r_patches[rm_for_cycle(CYCLES)] |
| 291 |
: *p_inst[CH_OFFSET[CYCLES]]; |
| 292 |
} |
| 293 |
|
| 294 |
template<uint32_t CYCLES> ALWAYS_INLINE uint32_t YM2413::envelopeKSLTL(const Patch& patch1, bool use_rm_patches) const |
| 295 |
{ |
| 296 |
constexpr uint32_t mcsel = ((CYCLES + 1) / 3) & 1; |
| 297 |
constexpr uint32_t ch = CH_OFFSET[CYCLES]; |
| 298 |
|
| 299 |
auto ksl = uint32_t(p_ksl[ch]) >> patch1.ksl_t[mcsel]; |
| 300 |
auto tl2 = [&] -> uint32_t { |
| 301 |
if ((rm_for_cycle(CYCLES) == one_of(RmNum::hh, RmNum::tom)) && use_rm_patches) { |
| 302 |
return inst[ch] << (2 + 1); |
| 303 |
} else if (mcsel == 1) { |
| 304 |
return vol8[ch]; |
| 305 |
} else { |
| 306 |
return patch1.tl2; |
| 307 |
} |
| 308 |
}(); |
| 309 |
return ksl + tl2; |
| 310 |
} |
| 311 |
|
| 312 |
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::envelopeTimer1() |
| 313 |
{ |
| 314 |
if constexpr (CYCLES == 0) { |
| 315 |
eg_counter_state = (eg_counter_state + 1) & 3; |
| 316 |
auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state]; |
| 317 |
releasePtr = release.data[idx]; |
| 318 |
} |
| 319 |
} |
| 320 |
|
| 321 |
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void YM2413::envelopeTimer2(bool& eg_timer_carry) |
| 322 |
{ |
| 323 |
if constexpr (TEST_MODE) { |
| 324 |
if (CYCLES == 0 && (eg_counter_state & 1) == 0) { |
| 325 |
eg_timer_lock = eg_timer & 3; |
| 326 |
eg_timer_shift_lock = (eg_timer_shift <= 13) ? eg_timer_shift : 0; |
| 327 |
eg_timer_shift = 0; |
| 328 |
|
| 329 |
attackPtr = attack[eg_timer_shift_lock][eg_timer_lock]; |
| 330 |
auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state]; |
| 331 |
releasePtr = release.data[idx]; |
| 332 |
} |
| 333 |
{ |
| 334 |
bool timer_inc = (eg_counter_state != 3) ? false |
| 335 |
: (CYCLES == 0) ? true |
| 336 |
: eg_timer_carry; |
| 337 |
auto timer_bit = (eg_timer & 1) + timer_inc; |
| 338 |
eg_timer_carry = timer_bit & 2; |
| 339 |
eg_timer = ((timer_bit & 1) << 17) | (eg_timer >> 1); |
| 340 |
if (testMode & 8) { |
| 341 |
const auto& write = writes[CYCLES]; |
| 342 |
auto data = (write.port != uint8_t(-1)) ? write.value : write_data; |
| 343 |
eg_timer &= 0x2ffff; |
| 344 |
eg_timer |= (data << (16 - 2)) & 0x10000; |
| 345 |
} |
| 346 |
} |
| 347 |
if constexpr (CYCLES == 0) { |
| 348 |
eg_timer_shift_stop = false; |
| 349 |
} else if (!eg_timer_shift_stop && ((eg_timer >> 16) & 1)) { |
| 350 |
eg_timer_shift = CYCLES; |
| 351 |
eg_timer_shift_stop = true; |
| 352 |
} |
| 353 |
} else { |
| 354 |
if constexpr (CYCLES == 0) { |
| 355 |
if ((eg_counter_state & 1) == 0) { |
| 356 |
eg_timer_lock = eg_timer & 3; |
| 357 |
eg_timer_shift_lock = (eg_timer_shift > 13) ? 0 : eg_timer_shift; |
| 358 |
|
| 359 |
attackPtr = attack[eg_timer_shift_lock][eg_timer_lock]; |
| 360 |
auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state]; |
| 361 |
releasePtr = release.data[idx]; |
| 362 |
} |
| 363 |
if (eg_counter_state == 3) { |
| 364 |
eg_timer = (eg_timer + 1) & 0x3ffff; |
| 365 |
eg_timer_shift = narrow_cast<uint8_t>(Math::findFirstSet(eg_timer)); |
| 366 |
} |
| 367 |
} |
| 368 |
} |
| 369 |
} |
| 370 |
|
| 371 |
template<uint32_t CYCLES> ALWAYS_INLINE bool YM2413::envelopeGenerate1() |
| 372 |
{ |
| 373 |
int32_t level = eg_level[(CYCLES + 16) % 18]; |
| 374 |
bool prev2_eg_off = eg_off[(CYCLES + 16) & 1]; |
| 375 |
bool prev2_eg_kon = eg_kon[(CYCLES + 16) & 1]; |
| 376 |
bool prev2_eg_dokon = eg_dokon[(CYCLES + 16) % 18]; |
| 377 |
|
| 378 |
using enum EgState; |
| 379 |
auto state = eg_state[(CYCLES + 16) % 18]; |
| 380 |
if (prev2_eg_dokon) [[unlikely]] { |
| 381 |
eg_state[(CYCLES + 16) % 18] = attack; |
| 382 |
} else if (!prev2_eg_kon) { |
| 383 |
eg_state[(CYCLES + 16) % 18] = release; |
| 384 |
} else if ((state == attack) && (level == 0)) [[unlikely]] { |
| 385 |
eg_state[(CYCLES + 16) % 18] = decay; |
| 386 |
} else if ((state == decay) && ((level >> 3) == eg_sl[CYCLES & 1])) [[unlikely]] { |
| 387 |
eg_state[(CYCLES + 16) % 18] = sustain; |
| 388 |
} |
| 389 |
|
| 390 |
auto prev2_rate = eg_rate[(CYCLES + 16) & 1]; |
| 391 |
int32_t next_level |
| 392 |
= (state != attack && prev2_eg_off && !prev2_eg_dokon) ? 0x7f |
| 393 |
: ((prev2_rate >= 60) && prev2_eg_dokon) ? 0x00 |
| 394 |
: level; |
| 395 |
auto step = [&] -> int { |
| 396 |
switch (state) { |
| 397 |
case attack: |
| 398 |
if (prev2_eg_kon && (level != 0)) [[likely]] { |
| 399 |
return (level ^ 0xfff) >> attackPtr[prev2_rate]; |
| 400 |
} |
| 401 |
break; |
| 402 |
case decay: |
| 403 |
if ((level >> 3) == eg_sl[CYCLES & 1]) return 0; |
| 404 |
[[fallthrough]]; |
| 405 |
case sustain: |
| 406 |
case release: |
| 407 |
if (!prev2_eg_off && !prev2_eg_dokon) { |
| 408 |
return releasePtr[prev2_rate]; |
| 409 |
} |
| 410 |
break; |
| 411 |
default: |
| 412 |
UNREACHABLE; |
| 413 |
} |
| 414 |
return 0; |
| 415 |
}(); |
| 416 |
eg_level[(CYCLES + 16) % 18] = narrow_cast<uint8_t>(next_level + step); |
| 417 |
|
| 418 |
return level == 0x7f; |
| 419 |
} |
| 420 |
|
| 421 |
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::envelopeGenerate2(const Patch& patch1, bool use_rm_patches) |
| 422 |
{ |
| 423 |
constexpr uint32_t mcsel = ((CYCLES + 1) / 3) & 1; |
| 424 |
constexpr uint32_t ch = CH_OFFSET[CYCLES]; |
| 425 |
|
| 426 |
bool new_eg_off = eg_level[CYCLES] >= 124; |
| 427 |
eg_off[CYCLES & 1] = new_eg_off; |
| 428 |
|
| 429 |
auto sk = sk_on[CH_OFFSET[CYCLES]]; |
| 430 |
bool new_eg_kon = [&] { |
| 431 |
bool result = sk & 1; |
| 432 |
if (is_rm_cycle(CYCLES) && use_rm_patches) { |
| 433 |
switch (rm_for_cycle(CYCLES)) { |
| 434 |
using enum RmNum; |
| 435 |
case bd0: |
| 436 |
case bd1: |
| 437 |
result |= bool(rhythm & 0x10); |
| 438 |
break; |
| 439 |
case sd: |
| 440 |
result |= bool(rhythm & 0x08); |
| 441 |
break; |
| 442 |
case tom: |
| 443 |
result |= bool(rhythm & 0x04); |
| 444 |
break; |
| 445 |
case tc: |
| 446 |
result |= bool(rhythm & 0x02); |
| 447 |
break; |
| 448 |
case hh: |
| 449 |
result |= bool(rhythm & 0x01); |
| 450 |
break; |
| 451 |
default: |
| 452 |
break; |
| 453 |
} |
| 454 |
} |
| 455 |
return result; |
| 456 |
}(); |
| 457 |
eg_kon[CYCLES & 1] = new_eg_kon; |
| 458 |
|
| 459 |
|
| 460 |
using enum EgState; |
| 461 |
auto state_rate = eg_state[CYCLES]; |
| 462 |
if (state_rate == release && new_eg_kon && new_eg_off) [[unlikely]] { |
| 463 |
state_rate = attack; |
| 464 |
eg_dokon[CYCLES] = true; |
| 465 |
} else { |
| 466 |
eg_dokon[CYCLES] = false; |
| 467 |
} |
| 468 |
|
| 469 |
auto rate4 = [&] { |
| 470 |
if (!new_eg_kon && !(sk & 2) && mcsel == 1 && !patch1.et[mcsel]) { |
| 471 |
return 7 * 4; |
| 472 |
} |
| 473 |
if (new_eg_kon && eg_state[CYCLES] == release && !new_eg_off) { |
| 474 |
return 12 * 4; |
| 475 |
} |
| 476 |
bool tom_or_hh = (rm_for_cycle(CYCLES) == one_of(RmNum::tom, RmNum::hh)) && use_rm_patches; |
| 477 |
if (!new_eg_kon && !mcsel && !tom_or_hh) { |
| 478 |
return 0 * 4; |
| 479 |
} |
| 480 |
return (state_rate == attack ) ? patch1.ar4[mcsel] |
| 481 |
: (state_rate == decay ) ? patch1.dr4[mcsel] |
| 482 |
: (state_rate == sustain) ? (patch1.et[mcsel] ? (0 * 4) : patch1.rr4[mcsel]) |
| 483 |
: ((sk & 2) ? (5 * 4) : patch1.rr4[mcsel]); |
| 484 |
}(); |
| 485 |
|
| 486 |
eg_rate[CYCLES & 1] = narrow_cast<uint8_t>([&] { |
| 487 |
if (rate4 == 0) return 0; |
| 488 |
auto tmp = rate4 + (p_ksr_freq[ch] >> patch1.ksr_t[mcsel]); |
| 489 |
return (tmp < 0x40) ? tmp |
| 490 |
: (0x3c | (tmp & 3)); |
| 491 |
}()); |
| 492 |
} |
| 493 |
|
| 494 |
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void YM2413::doLFO(bool& lfo_am_car) |
| 495 |
{ |
| 496 |
if constexpr (TEST_MODE) { |
| 497 |
|
| 498 |
if constexpr (CYCLES == 17) { |
| 499 |
lfo_counter++; |
| 500 |
if (((lfo_counter & 0x3ff) == 0) || (testMode & 8)) { |
| 501 |
lfo_vib_counter = (lfo_vib_counter + 1) & 7; |
| 502 |
lfo_vib = VIB_TAB[lfo_vib_counter]; |
| 503 |
} |
| 504 |
lfo_am_step = (lfo_counter & 0x3f) == 0; |
| 505 |
} |
| 506 |
|
| 507 |
|
| 508 |
auto am_inc = ((lfo_am_step || (testMode & 8)) && CYCLES < 9) |
| 509 |
? (lfo_am_dir | (CYCLES == 0)) |
| 510 |
: 0; |
| 511 |
|
| 512 |
if constexpr (CYCLES == 0) { |
| 513 |
if (lfo_am_dir && (lfo_am_counter & 0x7f) == 0) { |
| 514 |
lfo_am_dir = false; |
| 515 |
} else if (!lfo_am_dir && (lfo_am_counter & 0x69) == 0x69) { |
| 516 |
lfo_am_dir = true; |
| 517 |
} |
| 518 |
} |
| 519 |
|
| 520 |
auto am_bit = (lfo_am_counter & 1) + am_inc + ((CYCLES < 9) ? lfo_am_car : false); |
| 521 |
if constexpr (CYCLES < 8) { |
| 522 |
lfo_am_car = am_bit & 2; |
| 523 |
} |
| 524 |
lfo_am_counter = uint16_t(((am_bit & 1) << 8) | (lfo_am_counter >> 1)); |
| 525 |
|
| 526 |
|
| 527 |
if (testMode & 2) { |
| 528 |
lfo_vib_counter = 0; |
| 529 |
lfo_vib = VIB_TAB[lfo_vib_counter]; |
| 530 |
lfo_counter = 0; |
| 531 |
lfo_am_dir = false; |
| 532 |
lfo_am_counter &= 0xff; |
| 533 |
} |
| 534 |
} else { |
| 535 |
if constexpr (CYCLES == 17) { |
| 536 |
int delta = lfo_am_dir ? -1 : 1; |
| 537 |
|
| 538 |
if (lfo_am_dir && (lfo_am_counter & 0x7f) == 0) { |
| 539 |
lfo_am_dir = false; |
| 540 |
} else if (!lfo_am_dir && (lfo_am_counter & 0x69) == 0x69) { |
| 541 |
lfo_am_dir = true; |
| 542 |
} |
| 543 |
|
| 544 |
if (lfo_am_step) { |
| 545 |
lfo_am_counter = (lfo_am_counter + delta) & 0x1ff; |
| 546 |
} |
| 547 |
|
| 548 |
lfo_counter++; |
| 549 |
if ((lfo_counter & 0x3ff) == 0) { |
| 550 |
lfo_vib_counter = (lfo_vib_counter + 1) & 7; |
| 551 |
lfo_vib = VIB_TAB[lfo_vib_counter]; |
| 552 |
} |
| 553 |
lfo_am_step = (lfo_counter & 0x3f) == 0; |
| 554 |
} |
| 555 |
} |
| 556 |
if constexpr (CYCLES == 17) { |
| 557 |
lfo_am_out = (lfo_am_counter >> 3) & 0x0f; |
| 558 |
} |
| 559 |
} |
| 560 |
|
| 561 |
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void YM2413::doRhythm() |
| 562 |
{ |
| 563 |
if constexpr (TEST_MODE) { |
| 564 |
bool nbit = (rm_noise ^ (rm_noise >> 14)) & 1; |
| 565 |
nbit |= bool(testMode & 2); |
| 566 |
rm_noise = (nbit << 22) | (rm_noise >> 1); |
| 567 |
} else { |
| 568 |
|
| 569 |
|
| 570 |
|
| 571 |
|
| 572 |
|
| 573 |
if constexpr (CYCLES == 17) { |
| 574 |
rm_noise = ((rm_noise & 0x1ff) << 14) |
| 575 |
^ ((rm_noise & 0x3ffff) << 5) |
| 576 |
^ (rm_noise & 0x7fc000) |
| 577 |
^ ((rm_noise >> 9) & 0x3fe0) |
| 578 |
^ (rm_noise >> 18); |
| 579 |
} |
| 580 |
} |
| 581 |
} |
| 582 |
|
| 583 |
template<uint32_t CYCLES> ALWAYS_INLINE uint32_t YM2413::getPhaseMod(uint8_t fb_t) |
| 584 |
{ |
| 585 |
bool ismod2 = ((rhythm & 0x20) && (CYCLES == one_of(12u, 13u))) |
| 586 |
? false |
| 587 |
: (((CYCLES + 4) / 3) & 1); |
| 588 |
bool ismod3 = ((rhythm & 0x20) && (CYCLES == one_of(15u, 16u))) |
| 589 |
? false |
| 590 |
: (((CYCLES + 1) / 3) & 1); |
| 591 |
|
| 592 |
if (ismod3) return op_mod << 1; |
| 593 |
if (ismod2) { |
| 594 |
constexpr uint32_t cycles9 = (CYCLES + 3) % 9; |
| 595 |
uint32_t op_fbsum = (op_fb1[cycles9] + op_fb2[cycles9]) & 0x7fffffff; |
| 596 |
return op_fbsum >> fb_t; |
| 597 |
} |
| 598 |
return 0; |
| 599 |
} |
| 600 |
|
| 601 |
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::doRegWrite() |
| 602 |
{ |
| 603 |
if (write_fm_cycle == CYCLES) [[unlikely]] { |
| 604 |
doRegWrite(CYCLES % 9); |
| 605 |
} |
| 606 |
} |
| 607 |
|
| 608 |
void YM2413::changeFnumBlock(uint32_t ch) |
| 609 |
{ |
| 610 |
static constexpr std::array<uint8_t, 16> KSL_TABLE = { |
| 611 |
0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 |
| 612 |
}; |
| 613 |
p_ksl[ch] = narrow_cast<uint8_t>(std::max(0, KSL_TABLE[fnum[ch] >> 5] - ((8 - block[ch]) << 3)) << 1); |
| 614 |
p_incr[ch] = narrow_cast<uint16_t>(fnum[ch] << block[ch]); |
| 615 |
p_ksr_freq[ch] = uint8_t((block[ch] << 1) | (fnum[ch] >> 8)); |
| 616 |
} |
| 617 |
|
| 618 |
NEVER_INLINE void YM2413::doRegWrite(uint8_t channel) |
| 619 |
{ |
| 620 |
if (write_address < 0x40) { |
| 621 |
write_fm_cycle = uint8_t(-1); |
| 622 |
doRegWrite(write_address & 0xf0, channel, fm_data); |
| 623 |
} else { |
| 624 |
write_address -= 0x40; |
| 625 |
} |
| 626 |
} |
| 627 |
|
| 628 |
void YM2413::doRegWrite(uint8_t regBlock, uint8_t channel, uint8_t data) |
| 629 |
{ |
| 630 |
switch (regBlock) { |
| 631 |
case 0x10: |
| 632 |
fnum[channel] = uint16_t((fnum[channel] & 0x100) | data); |
| 633 |
changeFnumBlock(channel); |
| 634 |
break; |
| 635 |
case 0x20: |
| 636 |
fnum[channel] = uint16_t((fnum[channel] & 0xff) | ((data & 1) << 8)); |
| 637 |
block[channel] = (data >> 1) & 7; |
| 638 |
changeFnumBlock(channel); |
| 639 |
sk_on[channel] = (data >> 4) & 3; |
| 640 |
break; |
| 641 |
case 0x30: |
| 642 |
vol8[channel] = uint8_t((data & 0x0f) << (2 + 1)); |
| 643 |
inst[channel] = (data >> 4) & 0x0f; |
| 644 |
p_inst[channel] = &patches[inst[channel]]; |
| 645 |
break; |
| 646 |
} |
| 647 |
} |
| 648 |
|
| 649 |
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::doIO() |
| 650 |
{ |
| 651 |
if (writes[CYCLES].port != uint8_t(-1)) [[unlikely]] { |
| 652 |
doIO((CYCLES + 1) % 18, writes[CYCLES]); |
| 653 |
} |
| 654 |
} |
| 655 |
|
| 656 |
NEVER_INLINE void YM2413::doIO(uint32_t cycles_plus_1, Write& write) |
| 657 |
{ |
| 658 |
write_data = write.value; |
| 659 |
if (write.port) { |
| 660 |
|
| 661 |
if (write_address < 0x10) { |
| 662 |
doModeWrite(write_address, write.value); |
| 663 |
} else if (write_address < 0x40) { |
| 664 |
write_fm_cycle = write_address & 0xf; |
| 665 |
fm_data = write.value; |
| 666 |
if (!fast_fm_rewrite && (write_fm_cycle == cycles_plus_1)) { |
| 667 |
write_address += 0x40; |
| 668 |
} |
| 669 |
|
| 670 |
|
| 671 |
|
| 672 |
|
| 673 |
fast_fm_rewrite = true; |
| 674 |
} |
| 675 |
} else { |
| 676 |
|
| 677 |
write_address = write.value; |
| 678 |
write_fm_cycle = uint8_t(-1); |
| 679 |
fast_fm_rewrite = false; |
| 680 |
} |
| 681 |
|
| 682 |
write.port = uint8_t(-1); |
| 683 |
} |
| 684 |
|
| 685 |
void YM2413::doModeWrite(uint8_t address, uint8_t value) |
| 686 |
{ |
| 687 |
auto slot = address & 1; |
| 688 |
switch (address) { |
| 689 |
case 0x00: |
| 690 |
case 0x01: |
| 691 |
patches[0].setMulti(slot, value & 0x0f); |
| 692 |
patches[0].setKSR(slot, (value >> 4) & 1); |
| 693 |
patches[0].et[slot] = (value >> 5) & 1; |
| 694 |
patches[0].vib[slot] = (value >> 6) & 1; |
| 695 |
patches[0].setAM(slot, (value >> 7) & 1); |
| 696 |
break; |
| 697 |
|
| 698 |
case 0x02: |
| 699 |
patches[0].setKSL(0, (value >> 6) & 3); |
| 700 |
patches[0].setTL(value & 0x3f); |
| 701 |
break; |
| 702 |
|
| 703 |
case 0x03: |
| 704 |
patches[0].setKSL(1, (value >> 6) & 3); |
| 705 |
patches[0].dcm = (value >> 3) & 3; |
| 706 |
patches[0].setFB(value & 7); |
| 707 |
break; |
| 708 |
|
| 709 |
case 0x04: |
| 710 |
case 0x05: |
| 711 |
patches[0].setDR(slot, value & 0x0f); |
| 712 |
patches[0].setAR(slot, (value >> 4) & 0x0f); |
| 713 |
break; |
| 714 |
|
| 715 |
case 0x06: |
| 716 |
case 0x07: |
| 717 |
patches[0].setRR(slot, value & 0x0f); |
| 718 |
patches[0].sl[slot] = (value >> 4) & 0x0f; |
| 719 |
break; |
| 720 |
|
| 721 |
case 0x0e: |
| 722 |
rhythm = value & 0x3f; |
| 723 |
break; |
| 724 |
|
| 725 |
case 0x0f: |
| 726 |
testMode = value & 0x0f; |
| 727 |
break; |
| 728 |
} |
| 729 |
} |
| 730 |
|
| 731 |
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::doOperator(std::span<float*, 9 + 5> out, bool eg_silent) |
| 732 |
{ |
| 733 |
bool ismod1 = ((rhythm & 0x20) && (CYCLES == one_of(14u, 15u))) |
| 734 |
? false |
| 735 |
: (((CYCLES + 2) / 3) & 1); |
| 736 |
constexpr bool is_next_mod3 = ((CYCLES + 2) / 3) & 1; |
| 737 |
|
| 738 |
auto output = [&] -> int32_t { |
| 739 |
if (eg_silent) return 0; |
| 740 |
auto prev2_phase = op_phase[(CYCLES - 2) & 1]; |
| 741 |
auto quarter = narrow_cast<uint8_t>((prev2_phase & 0x100) ? ~prev2_phase : prev2_phase); |
| 742 |
auto logSin = logSinTab[quarter]; |
| 743 |
auto op_level = std::min(4095, logSin + (eg_out[(CYCLES - 2) & 1] << 4)); |
| 744 |
uint32_t op_exp_m = expTab[op_level & 0xff]; |
| 745 |
auto op_exp_s = op_level >> 8; |
| 746 |
if (prev2_phase & 0x200) { |
| 747 |
return (c_dcm[(CYCLES + 16) % 3] & (ismod1 ? 1 : 2)) |
| 748 |
? ~0 |
| 749 |
: ~(op_exp_m >> op_exp_s); |
| 750 |
} else { |
| 751 |
return narrow<int32_t>(op_exp_m >> op_exp_s); |
| 752 |
} |
| 753 |
}(); |
| 754 |
|
| 755 |
if (ismod1) { |
| 756 |
constexpr uint32_t cycles9 = (CYCLES + 1) % 9; |
| 757 |
op_fb2[cycles9] = op_fb1[cycles9]; |
| 758 |
op_fb1[cycles9] = narrow_cast<int16_t>(output); |
| 759 |
} |
| 760 |
channelOutput<CYCLES>(out, output >> 3); |
| 761 |
|
| 762 |
if (is_next_mod3) { |
| 763 |
op_mod = output & 0x1ff; |
| 764 |
} |
| 765 |
} |
| 766 |
|
| 767 |
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE uint32_t YM2413::getPhase(uint8_t& rm_hh_bits) |
| 768 |
{ |
| 769 |
uint32_t phase = pg_phase[CYCLES]; |
| 770 |
|
| 771 |
|
| 772 |
if constexpr (CYCLES == 12) { |
| 773 |
rm_hh_bits = narrow_cast<uint8_t>(phase >> (2 + 9)); |
| 774 |
} else if (CYCLES == 16 && (rhythm & 0x20)) { |
| 775 |
rm_tc_bits = narrow_cast<uint8_t>(phase >> 8); |
| 776 |
} |
| 777 |
|
| 778 |
if (rhythm & 0x20) { |
| 779 |
auto rm_bit = [&] { |
| 780 |
bool rm_hh_bit2 = (rm_hh_bits >> (2 - 2)) & 1; |
| 781 |
bool rm_hh_bit3 = (rm_hh_bits >> (3 - 2)) & 1; |
| 782 |
bool rm_hh_bit7 = (rm_hh_bits >> (7 - 2)) & 1; |
| 783 |
bool rm_tc_bit3 = (rm_tc_bits >> (3 + 9 - 8)) & 1; |
| 784 |
bool rm_tc_bit5 = (rm_tc_bits >> (5 + 9 - 8)) & 1; |
| 785 |
return (rm_hh_bit2 ^ rm_hh_bit7) |
| 786 |
| (rm_hh_bit3 ^ rm_tc_bit5) |
| 787 |
| (rm_tc_bit3 ^ rm_tc_bit5); |
| 788 |
}; |
| 789 |
auto noise_bit = [&] { |
| 790 |
|
| 791 |
return (rm_noise >> (TEST_MODE ? 0 : (CYCLES + 1))) & 1; |
| 792 |
}; |
| 793 |
switch (CYCLES) { |
| 794 |
case 12: { |
| 795 |
auto b = rm_bit(); |
| 796 |
return (b << 9) | ((b ^ noise_bit()) ? 0xd0 : 0x34); |
| 797 |
} |
| 798 |
case 15: { |
| 799 |
auto rm_hh_bit8 = (rm_hh_bits >> (8 - 2)) & 1; |
| 800 |
return (rm_hh_bit8 << 9) | ((rm_hh_bit8 ^ noise_bit()) << 8); |
| 801 |
} |
| 802 |
case 16: |
| 803 |
return (rm_bit() << 9) | 0x100; |
| 804 |
default: |
| 805 |
break; |
| 806 |
} |
| 807 |
} |
| 808 |
return phase >> 9; |
| 809 |
} |
| 810 |
|
| 811 |
template<uint32_t CYCLES> ALWAYS_INLINE uint32_t YM2413::phaseCalcIncrement(const Patch& patch1) const |
| 812 |
{ |
| 813 |
constexpr uint32_t mcsel = ((CYCLES + 1) / 3) & 1; |
| 814 |
constexpr uint32_t ch = CH_OFFSET[CYCLES]; |
| 815 |
|
| 816 |
uint32_t incr = [&] { |
| 817 |
|
| 818 |
if (patch1.vib[mcsel]) { |
| 819 |
|
| 820 |
|
| 821 |
uint32_t freq = fnum[ch] << 1; |
| 822 |
freq += (int(freq) * lfo_vib) / 256; |
| 823 |
return (freq << block[ch]) >> 1; |
| 824 |
} else { |
| 825 |
return uint32_t(p_incr[ch]); |
| 826 |
} |
| 827 |
}(); |
| 828 |
return (incr * patch1.multi_t[mcsel]) >> 1; |
| 829 |
} |
| 830 |
|
| 831 |
template<uint32_t CYCLES> ALWAYS_INLINE bool YM2413::keyOnEvent() const |
| 832 |
{ |
| 833 |
bool ismod = ((rhythm & 0x20) && (CYCLES == one_of(12u, 13u))) |
| 834 |
? false |
| 835 |
: (((CYCLES + 4) / 3) & 1); |
| 836 |
return ismod ? eg_dokon[(CYCLES + 3) % 18] |
| 837 |
: eg_dokon[CYCLES]; |
| 838 |
} |
| 839 |
|
| 840 |
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE void YM2413::incrementPhase(uint32_t phase_incr, bool key_on_event) |
| 841 |
{ |
| 842 |
uint32_t pg_phase_next = ((TEST_MODE && (testMode & 4)) || key_on_event) |
| 843 |
? 0 |
| 844 |
: pg_phase[CYCLES]; |
| 845 |
pg_phase[CYCLES] = pg_phase_next + phase_incr; |
| 846 |
} |
| 847 |
|
| 848 |
template<uint32_t CYCLES> ALWAYS_INLINE void YM2413::channelOutput(std::span<float*, 9 + 5> out, int32_t ch_out) |
| 849 |
{ |
| 850 |
auto outF = narrow_cast<float>(ch_out); |
| 851 |
switch (CYCLES) { |
| 852 |
case 4: *out[ 0]++ += outF; break; |
| 853 |
case 5: *out[ 1]++ += outF; break; |
| 854 |
case 6: *out[ 2]++ += outF; break; |
| 855 |
case 10: *out[ 3]++ += outF; break; |
| 856 |
case 11: *out[ 4]++ += outF; break; |
| 857 |
case 12: *out[ 5]++ += outF; break; |
| 858 |
case 14: *out[ 9]++ += (rhythm & 0x20) ? 2.0f * outF : 0.0f; break; |
| 859 |
case 15: *out[10]++ += narrow_cast<float>(delay10); |
| 860 |
delay10 = (rhythm & 0x20) ? 2 * ch_out : 0; break; |
| 861 |
case 16: *out[ 6]++ += narrow_cast<float>(delay6); |
| 862 |
*out[11]++ += narrow_cast<float>(delay11); |
| 863 |
delay6 = (rhythm & 0x20) ? 0 : ch_out; |
| 864 |
delay11 = (rhythm & 0x20) ? 2 * ch_out : 0; break; |
| 865 |
case 17: *out[ 7]++ += narrow_cast<float>(delay7); |
| 866 |
*out[12]++ += narrow_cast<float>(delay12); |
| 867 |
delay7 = (rhythm & 0x20) ? 0 : ch_out; |
| 868 |
delay12 = (rhythm & 0x20) ? 2 * ch_out : 0; break; |
| 869 |
case 0: *out[ 8]++ += (rhythm & 0x20) ? 0.0f : outF; |
| 870 |
*out[13]++ += (rhythm & 0x20) ? 2.0f * outF : 0.0f; break; |
| 871 |
default: |
| 872 |
break; |
| 873 |
} |
| 874 |
} |
| 875 |
|
| 876 |
template<uint32_t CYCLES, bool TEST_MODE> ALWAYS_INLINE uint8_t YM2413::envelopeOutput(uint32_t ksltl, int8_t am_t) const |
| 877 |
{ |
| 878 |
if (TEST_MODE && (testMode & 1)) { |
| 879 |
return 0; |
| 880 |
} |
| 881 |
int32_t level = eg_level[CYCLES] + ksltl + (am_t & lfo_am_out); |
| 882 |
return narrow_cast<uint8_t>(std::min(127, level)); |
| 883 |
} |
| 884 |
|
| 885 |
template<uint32_t CYCLES, bool TEST_MODE> |
| 886 |
ALWAYS_INLINE void YM2413::step(Locals& l) |
| 887 |
{ |
| 888 |
if constexpr (CYCLES == 11) { |
| 889 |
|
| 890 |
|
| 891 |
l.use_rm_patches = rhythm & 0x20; |
| 892 |
} |
| 893 |
const Patch& patch1 = preparePatch1<CYCLES>(l.use_rm_patches); |
| 894 |
uint32_t ksltl = envelopeKSLTL<CYCLES>(patch1, l.use_rm_patches); |
| 895 |
envelopeTimer1<CYCLES>(); |
| 896 |
bool eg_silent = envelopeGenerate1<CYCLES>(); |
| 897 |
envelopeTimer2<CYCLES, TEST_MODE>(l.eg_timer_carry); |
| 898 |
envelopeGenerate2<CYCLES>(patch1, l.use_rm_patches); |
| 899 |
bool key_on_event = keyOnEvent<CYCLES>(); |
| 900 |
|
| 901 |
doLFO<CYCLES, TEST_MODE>(l.lfo_am_car); |
| 902 |
doRhythm<CYCLES, TEST_MODE>(); |
| 903 |
uint32_t phaseMod = getPhaseMod<CYCLES>(patch1.fb_t); |
| 904 |
|
| 905 |
constexpr uint32_t mcsel = ((CYCLES + 1) / 3) & 1; |
| 906 |
eg_sl[CYCLES & 1] = patch1.sl[mcsel]; |
| 907 |
auto patch2_am_t = patch1.am_t[mcsel]; |
| 908 |
|
| 909 |
uint32_t phase_incr = phaseCalcIncrement<CYCLES>(patch1); |
| 910 |
c_dcm[CYCLES % 3] = patch1.dcm; |
| 911 |
|
| 912 |
doRegWrite<CYCLES>(); |
| 913 |
doIO<CYCLES>(); |
| 914 |
|
| 915 |
doOperator<CYCLES>(l.out, eg_silent); |
| 916 |
|
| 917 |
uint32_t pg_out = getPhase<CYCLES, TEST_MODE>(l.rm_hh_bits); |
| 918 |
op_phase[CYCLES & 1] = narrow_cast<uint16_t>(phaseMod + pg_out); |
| 919 |
incrementPhase<CYCLES, TEST_MODE>(phase_incr, key_on_event); |
| 920 |
|
| 921 |
eg_out[CYCLES & 1] = envelopeOutput<CYCLES, TEST_MODE>(ksltl, patch2_am_t); |
| 922 |
} |
| 923 |
|
| 924 |
void YM2413::generateChannels(std::span<float*, 9 + 5> out_, uint32_t n) |
| 925 |
{ |
| 926 |
std::array<float*, 9 + 5> out; |
| 927 |
copy_to_range(out_, out); |
| 928 |
|
| 929 |
|
| 930 |
if (test_mode_active) [[unlikely]] { |
| 931 |
repeat(n, [&] { step18<true >(out); }); |
| 932 |
} else { |
| 933 |
repeat(n, [&] { step18<false>(out); }); |
| 934 |
} |
| 935 |
test_mode_active = testMode; |
| 936 |
} |
| 937 |
|
| 938 |
template<bool TEST_MODE> |
| 939 |
NEVER_INLINE void YM2413::step18(std::span<float*, 9 + 5> out) |
| 940 |
{ |
| 941 |
Locals l(out); |
| 942 |
|
| 943 |
step< 0, TEST_MODE>(l); |
| 944 |
step< 1, TEST_MODE>(l); |
| 945 |
step< 2, TEST_MODE>(l); |
| 946 |
step< 3, TEST_MODE>(l); |
| 947 |
step< 4, TEST_MODE>(l); |
| 948 |
step< 5, TEST_MODE>(l); |
| 949 |
step< 6, TEST_MODE>(l); |
| 950 |
step< 7, TEST_MODE>(l); |
| 951 |
step< 8, TEST_MODE>(l); |
| 952 |
step< 9, TEST_MODE>(l); |
| 953 |
step<10, TEST_MODE>(l); |
| 954 |
step<11, TEST_MODE>(l); |
| 955 |
step<12, TEST_MODE>(l); |
| 956 |
step<13, TEST_MODE>(l); |
| 957 |
step<14, TEST_MODE>(l); |
| 958 |
step<15, TEST_MODE>(l); |
| 959 |
step<16, TEST_MODE>(l); |
| 960 |
step<17, TEST_MODE>(l); |
| 961 |
|
| 962 |
allowed_offset = std::max<int>(0, allowed_offset - 18); |
| 963 |
} |
| 964 |
|
| 965 |
void YM2413::writePort(bool port, uint8_t value, int cycle_offset) |
| 966 |
{ |
| 967 |
|
| 968 |
|
| 969 |
|
| 970 |
|
| 971 |
|
| 972 |
|
| 973 |
|
| 974 |
|
| 975 |
|
| 976 |
if (speedUpHack) [[unlikely]] { |
| 977 |
while (cycle_offset < allowed_offset) [[unlikely]] { |
| 978 |
float d = 0.0f; |
| 979 |
std::array<float*, 9 + 5> dummy = { |
| 980 |
&d, &d, &d, &d, &d, &d, &d, &d, &d, |
| 981 |
&d, &d, &d, &d, &d, |
| 982 |
}; |
| 983 |
step18<false>(dummy); |
| 984 |
} |
| 985 |
|
| 986 |
|
| 987 |
|
| 988 |
allowed_offset = ((port ? 84 : 12) / 4) + cycle_offset; |
| 989 |
} |
| 990 |
|
| 991 |
writes[cycle_offset] = {.port = port, .value = value}; |
| 992 |
if (port && (write_address == 0xf)) { |
| 993 |
test_mode_active = true; |
| 994 |
} |
| 995 |
|
| 996 |
|
| 997 |
if (port == 0) { |
| 998 |
latch = value & 63; |
| 999 |
} else { |
| 1000 |
regs[latch] = value; |
| 1001 |
} |
| 1002 |
} |
| 1003 |
|
| 1004 |
void YM2413::pokeReg(uint8_t reg, uint8_t value) |
| 1005 |
{ |
| 1006 |
regs[reg] = value; |
| 1007 |
if (reg < 0x10) { |
| 1008 |
doModeWrite(reg, value); |
| 1009 |
} else { |
| 1010 |
if (uint8_t ch = reg & 0xf; ch < 9) { |
| 1011 |
doRegWrite(reg & 0xf0, ch, value); |
| 1012 |
} |
| 1013 |
} |
| 1014 |
} |
| 1015 |
|
| 1016 |
std::span<const uint8_t, 64> YM2413::peekRegs() const |
| 1017 |
{ |
| 1018 |
return regs; |
| 1019 |
} |
| 1020 |
|
| 1021 |
float YM2413::getAmplificationFactor() const |
| 1022 |
{ |
| 1023 |
return 1.0f / 256.0f; |
| 1024 |
} |
| 1025 |
|
| 1026 |
void YM2413::setSpeed(double speed) |
| 1027 |
{ |
| 1028 |
speedUpHack = speed > 1.0; |
| 1029 |
} |
| 1030 |
|
| 1031 |
} |
| 1032 |
|
| 1033 |
|
| 1034 |
static constexpr auto egStateInfo = std::to_array<enum_string<YM2413NukeYKT::YM2413::EgState>>({ |
| 1035 |
{ "attack", YM2413NukeYKT::YM2413::EgState::attack }, |
| 1036 |
{ "decay", YM2413NukeYKT::YM2413::EgState::decay }, |
| 1037 |
{ "sustain", YM2413NukeYKT::YM2413::EgState::sustain }, |
| 1038 |
{ "release", YM2413NukeYKT::YM2413::EgState::release }, |
| 1039 |
}); |
| 1040 |
SERIALIZE_ENUM(YM2413NukeYKT::YM2413::EgState, egStateInfo); |
| 1041 |
|
| 1042 |
namespace YM2413NukeYKT { |
| 1043 |
|
| 1044 |
template<typename Archive> |
| 1045 |
void YM2413::Write::serialize(Archive& ar, unsigned ) |
| 1046 |
{ |
| 1047 |
ar.serialize("port", port, |
| 1048 |
"value", value); |
| 1049 |
} |
| 1050 |
|
| 1051 |
template<typename Archive> |
| 1052 |
void YM2413::serialize(Archive& ar, unsigned ) |
| 1053 |
{ |
| 1054 |
ar.serialize("writes", writes, |
| 1055 |
"write_data", write_data, |
| 1056 |
"fm_data", fm_data, |
| 1057 |
"write_address", write_address, |
| 1058 |
"write_fm_cycle", write_fm_cycle, |
| 1059 |
"fast_fm_rewrite", fast_fm_rewrite, |
| 1060 |
"test_mode_active", test_mode_active); |
| 1061 |
|
| 1062 |
ar.serialize("eg_timer", eg_timer, |
| 1063 |
"eg_sl", eg_sl, |
| 1064 |
"eg_out", eg_out, |
| 1065 |
"eg_counter_state", eg_counter_state, |
| 1066 |
"eg_timer_shift", eg_timer_shift, |
| 1067 |
"eg_timer_shift_lock", eg_timer_shift_lock, |
| 1068 |
"eg_timer_lock", eg_timer_lock, |
| 1069 |
"eg_state", eg_state, |
| 1070 |
"eg_level", eg_level, |
| 1071 |
"eg_rate", eg_rate, |
| 1072 |
"eg_dokon", eg_dokon, |
| 1073 |
"eg_kon", eg_kon, |
| 1074 |
"eg_off", eg_off, |
| 1075 |
"eg_timer_shift_stop", eg_timer_shift_stop, |
| 1076 |
"pg_phase", pg_phase); |
| 1077 |
|
| 1078 |
ar.serialize("op_fb1", op_fb1, |
| 1079 |
"op_fb2", op_fb2, |
| 1080 |
"op_mod", op_mod, |
| 1081 |
"op_phase", op_phase, |
| 1082 |
"lfo_counter", lfo_counter, |
| 1083 |
"lfo_am_counter", lfo_am_counter, |
| 1084 |
"lfo_vib_counter", lfo_vib_counter, |
| 1085 |
"lfo_am_out", lfo_am_out, |
| 1086 |
"lfo_am_step", lfo_am_step, |
| 1087 |
"lfo_am_dir", lfo_am_dir); |
| 1088 |
|
| 1089 |
ar.serialize("rm_noise", rm_noise, |
| 1090 |
"rm_tc_bits", rm_tc_bits, |
| 1091 |
"c_dcm", c_dcm, |
| 1092 |
"regs", regs, |
| 1093 |
"latch", latch); |
| 1094 |
|
| 1095 |
if constexpr (Archive::IS_LOADER) { |
| 1096 |
|
| 1097 |
attackPtr = attack[eg_timer_shift_lock][eg_timer_lock]; |
| 1098 |
auto idx = release.index[eg_timer_shift_lock][eg_timer_lock][eg_counter_state]; |
| 1099 |
releasePtr = release.data[idx]; |
| 1100 |
|
| 1101 |
lfo_vib = VIB_TAB[lfo_vib_counter]; |
| 1102 |
|
| 1103 |
delay6 = delay7 = delay10 = delay11 = delay12 = 0; |
| 1104 |
|
| 1105 |
|
| 1106 |
|
| 1107 |
|
| 1108 |
for (auto i : xrange(uint8_t(64))) { |
| 1109 |
pokeReg(i, regs[i]); |
| 1110 |
} |
| 1111 |
} |
| 1112 |
} |
| 1113 |
|
| 1114 |
} |
| 1115 |
|
| 1116 |
using YM2413NukeYKT::YM2413; |
| 1117 |
INSTANTIATE_SERIALIZE_METHODS(YM2413); |
| 1118 |
REGISTER_POLYMORPHIC_INITIALIZER(YM2413Core, YM2413, "YM2413-NukeYKT"); |
| 1119 |
|
| 1120 |
} |