52static constexpr int MAX_ATT_INDEX = 0x280;
53static constexpr int MIN_ATT_INDEX = 0;
54static constexpr int TL_SHIFT = 2;
56static constexpr unsigned LFO_SHIFT = 18;
57static constexpr unsigned LFO_PERIOD = 1 << LFO_SHIFT;
60static constexpr int EG_ATT = 4;
61static constexpr int EG_DEC = 3;
62static constexpr int EG_SUS = 2;
63static constexpr int EG_REL = 1;
64static constexpr int EG_OFF = 0;
66static constexpr int EG_REV = 5;
67static constexpr int EG_DMP = 6;
70static constexpr std::array<uint8_t, 16> pan_left = {
71 0, 8, 16, 24, 32, 40, 48, 255, 255, 0, 0, 0, 0, 0, 0, 0
73static constexpr std::array<uint8_t, 16> pan_right = {
74 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 48, 40, 32, 24, 16, 8
79static constexpr int16_t SC(
int dB) {
return int16_t(dB / 3 * 0x20); }
80static constexpr std::array<int16_t, 16> dl_tab = {
81 SC( 0), SC( 3), SC( 6), SC( 9), SC(12), SC(15), SC(18), SC(21),
82 SC(24), SC(27), SC(30), SC(33), SC(36), SC(39), SC(42), SC(93)
85static constexpr uint8_t RATE_STEPS = 8;
86static constexpr std::array<uint8_t, 15 * RATE_STEPS> eg_inc = {
88 0, 1, 0, 1, 0, 1, 0, 1,
89 0, 1, 0, 1, 1, 1, 0, 1,
90 0, 1, 1, 1, 0, 1, 1, 1,
91 0, 1, 1, 1, 1, 1, 1, 1,
93 1, 1, 1, 1, 1, 1, 1, 1,
94 1, 1, 1, 2, 1, 1, 1, 2,
95 1, 2, 1, 2, 1, 2, 1, 2,
96 1, 2, 2, 2, 1, 2, 2, 2,
98 2, 2, 2, 2, 2, 2, 2, 2,
99 2, 2, 2, 4, 2, 2, 2, 4,
100 2, 4, 2, 4, 2, 4, 2, 4,
101 2, 4, 4, 4, 2, 4, 4, 4,
103 4, 4, 4, 4, 4, 4, 4, 4,
104 8, 8, 8, 8, 8, 8, 8, 8,
105 0, 0, 0, 0, 0, 0, 0, 0,
108[[nodiscard]]
static constexpr uint8_t O(
int a) {
return narrow<uint8_t>(a * RATE_STEPS); }
109static constexpr std::array<uint8_t, 64> eg_rate_select = {
110 O(14),O(14),O(14),O(14),
111 O( 0),O( 1),O( 2),O( 3),
112 O( 0),O( 1),O( 2),O( 3),
113 O( 0),O( 1),O( 2),O( 3),
114 O( 0),O( 1),O( 2),O( 3),
115 O( 0),O( 1),O( 2),O( 3),
116 O( 0),O( 1),O( 2),O( 3),
117 O( 0),O( 1),O( 2),O( 3),
118 O( 0),O( 1),O( 2),O( 3),
119 O( 0),O( 1),O( 2),O( 3),
120 O( 0),O( 1),O( 2),O( 3),
121 O( 0),O( 1),O( 2),O( 3),
122 O( 0),O( 1),O( 2),O( 3),
123 O( 4),O( 5),O( 6),O( 7),
124 O( 8),O( 9),O(10),O(11),
125 O(12),O(12),O(12),O(12),
131static constexpr std::array<uint8_t, 64> eg_rate_shift = {
153[[nodiscard]]
static constexpr int L(
double a) {
return int((LFO_PERIOD * a) / 44100.0 + 0.5); }
154static constexpr std::array<int, 8> lfo_period = {
168static constexpr std::array<int16_t, 8> vib_depth = {
186static constexpr std::array<uint8_t, 8> am_depth = {
205[[nodiscard]]
static constexpr int8_t sign_extend_4(
int x)
207 return narrow<int8_t>((x ^ 8) - 8);
216[[nodiscard]]
static constexpr unsigned calcStep(int8_t oct, uint16_t fn, int16_t vib = 0)
218 if (oct == -8)
return 0;
219 unsigned t = (fn + 1024 + vib) << (8 + oct);
223void YMF278::Slot::reset()
225 wave = FN = TLdest = TL = pan = vib = AM = 0;
228 AR = D1R = D2R = RC = RR = 0;
229 PRVB = keyon = DAMP =
false;
231 step = calcStep(OCT, FN);
234 loopAddr = endAddr = 0;
235 env_vol = MAX_ATT_INDEX;
247uint8_t YMF278::Slot::compute_rate(
int val)
const
251 }
else if (val == 15) {
257 res += 2 * std::clamp(OCT + RC, 0, 15);
258 res += (FN & 0x200) ? 1 : 0;
260 return narrow<uint8_t>(std::clamp(res, 0, 63));
263uint8_t YMF278::Slot::compute_decay_rate(
int val)
const
278 if (env_vol < dl_tab[4]) {
294 if (env_vol >= dl_tab[6]) {
298 return compute_rate(val);
301int16_t YMF278::Slot::compute_vib()
const
310 auto lfo_fm = narrow<int16_t>(lfo_cnt / (LFO_PERIOD / 0x40));
312 if (lfo_fm & 0x10) lfo_fm ^= 0x1F;
313 if (lfo_fm & 0x20) lfo_fm = narrow<int16_t>(-(lfo_fm & 0x0F));
315 return narrow<int16_t>((lfo_fm * vib_depth[vib]) / 12);
318uint16_t YMF278::Slot::compute_am()
const
324 auto lfo_am = narrow<uint16_t>(lfo_cnt / (LFO_PERIOD / 0x100));
326 if (lfo_am >= 0x80) lfo_am ^= 0xFF;
328 return narrow<uint16_t>((lfo_am * am_depth[AM]) >> 7);
332void YMF278::advance()
337 auto tl_int_cnt = eg_cnt % 9;
338 auto tl_int_step = (eg_cnt / 9) % 3;
340 for (
auto& op : slots) {
342 if (tl_int_cnt == 0) {
343 if (tl_int_step == 0) {
345 if (op.TL < op.TLdest) ++op.TL;
348 if (op.TL > op.TLdest) --op.TL;
353 op.lfo_cnt = (op.lfo_cnt + lfo_period[op.lfo]) & (LFO_PERIOD - 1);
359 uint8_t rate = op.compute_rate(op.AR);
366 uint8_t shift = eg_rate_shift[rate];
367 if (!(eg_cnt & ((1 << shift) - 1))) {
368 uint8_t select = eg_rate_select[rate];
370 op.env_vol = narrow<int16_t>(op.env_vol + ((~op.env_vol * eg_inc[select + ((eg_cnt >> shift) & 7)]) >> 4));
371 if (op.env_vol <= MIN_ATT_INDEX) {
372 op.env_vol = MIN_ATT_INDEX;
375 op.state = op.DL ? EG_DEC : EG_SUS;
381 uint8_t rate = op.compute_decay_rate(op.D1R);
382 uint8_t shift = eg_rate_shift[rate];
383 if (!(eg_cnt & ((1 << shift) - 1))) {
384 uint8_t select = eg_rate_select[rate];
385 op.env_vol = narrow<int16_t>(op.env_vol + eg_inc[select + ((eg_cnt >> shift) & 7)]);
386 if (op.env_vol >= op.DL) {
387 op.state = (op.env_vol < MAX_ATT_INDEX) ? EG_SUS : EG_OFF;
393 uint8_t rate = op.compute_decay_rate(op.D2R);
394 uint8_t shift = eg_rate_shift[rate];
395 if (!(eg_cnt & ((1 << shift) - 1))) {
396 uint8_t select = eg_rate_select[rate];
397 op.env_vol = narrow<int16_t>(op.env_vol + eg_inc[select + ((eg_cnt >> shift) & 7)]);
398 if (op.env_vol >= MAX_ATT_INDEX) {
399 op.env_vol = MAX_ATT_INDEX;
406 uint8_t rate = op.compute_decay_rate(op.RR);
407 uint8_t shift = eg_rate_shift[rate];
408 if (!(eg_cnt & ((1 << shift) - 1))) {
409 uint8_t select = eg_rate_select[rate];
410 op.env_vol = narrow<int16_t>(op.env_vol + eg_inc[select + ((eg_cnt >> shift) & 7)]);
411 if (op.env_vol >= MAX_ATT_INDEX) {
412 op.env_vol = MAX_ATT_INDEX;
428int16_t YMF278::getSample(
const Slot& slot, uint16_t pos)
const
436 return narrow_cast<int16_t>(
readMem(slot.startAddr + pos) << 8);
440 unsigned addr = slot.startAddr + ((pos / 2) * 3);
442 return narrow_cast<int16_t>(
446 return narrow_cast<int16_t>(
448 ((
readMem(addr + 1) << 4) & 0xF0));
453 unsigned addr = slot.startAddr + (pos * 2);
454 return narrow_cast<int16_t>(
464uint16_t YMF278::nextPos(
const Slot& slot, uint16_t pos, uint16_t increment)
470 if ((uint32_t(pos) + slot.endAddr) >= 0x10000)
471 pos += narrow_cast<uint16_t>(slot.endAddr + slot.loopAddr);
475bool YMF278::anyActive()
477 return ranges::any_of(slots, [](
auto& op) {
return op.state != EG_OFF; });
484static constexpr int vol_factor(
int x,
unsigned envVol)
486 if (envVol >= MAX_ATT_INDEX)
return 0;
487 int vol_mul = 0x80 - narrow<int>(envVol & 0x3F);
488 int vol_shift = 7 + narrow<int>(envVol >> 6);
489 return (x * ((0x8000 * vol_mul) >> vol_shift)) >> 15;
494 static constexpr std::array<float, 8> level = {
507void YMF278::generateChannels(std::span<float*> bufs,
unsigned num)
516 for (
auto j :
xrange(num)) {
517 for (
auto i :
xrange(24)) {
519 if (sl.state == EG_OFF) {
525 auto sample = narrow_cast<int16_t>(
526 (getSample(sl, sl.pos) * (0x10000 - sl.stepPtr) +
527 getSample(sl, nextPos(sl, sl.pos, 1)) * sl.stepPtr) >> 16);
534 auto envVol = narrow_cast<uint16_t>(
535 std::min(sl.env_vol + ((sl.lfo_active && sl.AM) ? sl.compute_am() : 0),
537 int smplOut = vol_factor(vol_factor(sample, envVol), sl.TL << TL_SHIFT);
542 int32_t volLeft = pan_left [sl.pan];
543 int32_t volRight = pan_right[sl.pan];
545 volLeft = (0x20 - (volLeft & 0x0f)) >> (volLeft >> 4);
546 volRight = (0x20 - (volRight & 0x0f)) >> (volRight >> 4);
548 bufs[i][2 * j + 0] += narrow_cast<float>((smplOut * volLeft ) >> 5);
549 bufs[i][2 * j + 1] += narrow_cast<float>((smplOut * volRight) >> 5);
551 unsigned step = (sl.lfo_active && sl.vib)
552 ? calcStep(sl.OCT, sl.FN, sl.compute_vib())
556 if (sl.stepPtr >= 0x10000) {
557 sl.pos = nextPos(sl, sl.pos, narrow<uint16_t>(sl.stepPtr >> 16));
558 sl.stepPtr &= 0xffff;
565void YMF278::keyOnHelper(YMF278::Slot& slot)
const
568 slot.env_vol = MAX_ATT_INDEX;
569 if (slot.compute_rate(slot.AR) < 63) {
574 slot.env_vol = MIN_ATT_INDEX;
576 slot.state = slot.DL ? EG_DEC : EG_SUS;
585 writeRegDirect(reg, data, time);
588void YMF278::writeRegDirect(uint8_t reg, uint8_t data, EmuTime::param time)
591 if (reg >= 0x08 && reg <= 0xF7) {
592 int sNum = (reg - 8) % 24;
593 auto& slot = slots[sNum];
594 switch ((reg - 8) / 24) {
596 slot.wave = (slot.wave & 0x100) | data;
597 int waveTblHdr = (regs[2] >> 2) & 0x7;
598 int base = (slot.wave < 384 || !waveTblHdr) ?
600 (waveTblHdr * 0x80000 + ((slot.wave - 384) * 12));
601 std::array<uint8_t, 12> buf;
602 for (
auto i :
xrange(12)) {
607 slot.bits = (buf[0] & 0xC0) >> 6;
608 slot.startAddr = buf[2] | (buf[1] << 8) | ((buf[0] & 0x3F) << 16);
609 slot.loopAddr = uint16_t(buf[4] | (buf[3] << 8));
610 slot.endAddr = uint16_t(buf[6] | (buf[5] << 8));
611 for (
auto i :
xrange(7, 12)) {
615 writeRegDirect(narrow<uint8_t>(8 + sNum + (i - 2) * 24), buf[i], time);
626 slot.wave = uint16_t((slot.wave & 0xFF) | ((data & 0x1) << 8));
627 slot.FN = (slot.FN & 0x380) | (data >> 1);
628 slot.step = calcStep(slot.OCT, slot.FN);
632 slot.FN = uint16_t((slot.FN & 0x07F) | ((data & 0x07) << 7));
633 slot.PRVB = (data & 0x08) != 0;
634 slot.OCT = sign_extend_4((data & 0xF0) >> 4);
635 slot.step = calcStep(slot.OCT, slot.FN);
639 uint8_t
t = data >> 1;
640 slot.TLdest = (
t != 0x7f) ?
t : 0xff;
643 slot.TL = slot.TLdest;
656 slot.pan = data & 0x0F;
661 slot.lfo_active =
false;
665 slot.lfo_active =
true;
668 slot.DAMP = (data & 0x40) != 0;
683 slot.lfo = (data >> 3) & 0x7;
684 slot.vib = data & 0x7;
688 slot.D1R = data & 0xF;
691 slot.DL = dl_tab[data >> 4];
692 slot.D2R = data & 0xF;
696 slot.RR = data & 0xF;
699 slot.AM = data & 0x7;
734 memAdr = (regs[3] << 16) | (regs[4] << 8) | data;
776 return (regs[2] & 0x1F) | 0x20;
791static constexpr unsigned INPUT_RATE = 44100;
793static size_t getRamSize(
int ramSizeInKb)
795 if ((ramSizeInKb != 0) &&
796 (ramSizeInKb != 128) &&
797 (ramSizeInKb != 256) &&
798 (ramSizeInKb != 512) &&
799 (ramSizeInKb != 640) &&
800 (ramSizeInKb != 1024) &&
801 (ramSizeInKb != 2048)) {
803 "Wrong sample ram size for MoonSound (YMF278). "
804 "Got ", ramSizeInKb,
", but must be one of "
805 "0, 128, 256, 512, 640, 1024 or 2048.");
807 return size_t(ramSizeInKb) * 1024;
813 24, INPUT_RATE, true)
814 , motherBoard(config.getMotherBoard())
815 , debugRegisters(motherBoard, getName())
816 , debugMemory (motherBoard, getName())
817 , rom(getName() +
" ROM",
"rom", config)
818 , ram(config, getName() +
" RAM",
"YMF278 sample RAM",
819 getRamSize(ramSizeInKb))
821 if (rom.
size() != 0x200000) {
823 "Wrong ROM for MoonSound (YMF278). The ROM (usually "
824 "called yrw801.rom) should have a size of exactly 2MB.");
850 for (
auto& op : slots) {
854 for (
int i = 0xf7; i >= 0; --i) {
855 writeRegDirect(narrow<uint8_t>(i), 0, time);
909unsigned YMF278::getRamAddress(
unsigned addr)
const
912 if (regs[2] & 2) [[unlikely]] {
915 if ((0x180000 <= addr) && (addr <= 0x1FFFFF)) {
917 switch (addr & 0x060000) {
922 if (ram.
size() == 256 * 1024) {
943 if (ram.
size() == 640 * 1024) {
948 if (addr > 0x080000) {
959 if (address < 0x200000) {
963 unsigned ramAddr = getRamAddress(address);
964 if (ramAddr < ram.
size()) {
976 if (address < 0x200000) {
979 unsigned ramAddr = getRamAddress(address);
980 if (ramAddr < ram.
size()) {
981 ram.
write(ramAddr, value);
1006template<
typename Archive>
1007void YMF278::Slot::serialize(Archive& ar,
unsigned version)
1010 ar.serialize(
"startaddr", startAddr,
1011 "loopaddr", loopAddr,
1019 if (ar.versionAtLeast(version, 4)) {
1020 ar.serialize(
"endaddr", endAddr,
1023 unsigned e = 0; ar.serialize(
"endaddr", e); endAddr = uint16_t((e ^ 0xffff) + 1);
1026 if (ar.versionAtLeast(version, 2)) {
1027 ar.serialize(
"OCT", O);
1029 ar.serializeChar(
"OCT", O);
1031 OCT = sign_extend_4(O);
1034 if (ar.versionAtLeast(version, 2)) {
1035 ar.serialize(
"PRVB", PRVB,
1047 char PRVB_ = 0; ar.serializeChar(
"PRVB", PRVB_); PRVB = PRVB_;
1048 char TL_ = 0; ar.serializeChar(
"TL", TL_ ); TL = TL_;
1049 char pan_ = 0; ar.serializeChar(
"pan", pan_); pan = pan_;
1050 char vib_ = 0; ar.serializeChar(
"vib", vib_); vib = vib_;
1051 char AM_ = 0; ar.serializeChar(
"AM", AM_ ); AM = AM_;
1052 char AR_ = 0; ar.serializeChar(
"AR", AR_ ); AR = AR_;
1053 char D1R_ = 0; ar.serializeChar(
"D1R", D1R_); D1R = D1R_;
1054 char D2R_ = 0; ar.serializeChar(
"D2R", D2R_); D2R = D2R_;
1055 char RC_ = 0; ar.serializeChar(
"RC", RC_ ); RC = RC_;
1056 char RR_ = 0; ar.serializeChar(
"RR", RR_ ); RR = RR_;
1058 ar.serialize(
"bits", bits,
1059 "lfo_active", lfo_active);
1061 ar.serialize(
"state", state);
1062 if (ar.versionBelow(version, 4)) {
1063 assert(Archive::IS_LOADER);
1064 if (state ==
one_of(EG_REV, EG_DMP)) {
1070 if constexpr (Archive::IS_LOADER) {
1071 step = calcStep(OCT, FN);
1090template<
typename Archive>
1093 ar.serialize(
"slots", slots,
1095 if (ar.versionAtLeast(version, 4)) {
1096 ar.serialize(
"ram", ram);
1100 ar.serialize_blob(
"registers", regs);
1101 if (ar.versionAtLeast(version, 3)) {
1102 ar.serialize(
"memadr", memAdr);
1104 assert(Archive::IS_LOADER);
1109 memAdr = (regs[3] << 16) | (regs[4] << 8) | regs[5];
1113 if constexpr (Archive::IS_LOADER) {
1115 uint8_t
t = regs[0x50 + i] >> 1;
1116 sl.TLdest = (
t != 0x7f) ?
t : 0xff;
1118 sl.keyon = (regs[0x68 + i] & 0x80) != 0;
1119 sl.DAMP = (regs[0x68 + i] & 0x40) != 0;
1120 sl.lfo = (regs[0x80 + i] >> 3) & 7;
1129YMF278::DebugRegisters::DebugRegisters(
MSXMotherBoard& motherBoard_,
1130 const std::string& name_)
1132 "OPL4 registers", 0x100)
1136uint8_t YMF278::DebugRegisters::read(
unsigned address)
1138 const auto& ymf278 =
OUTER(YMF278, debugRegisters);
1139 return ymf278.peekReg(narrow<uint8_t>(address));
1142void YMF278::DebugRegisters::write(
unsigned address, uint8_t value, EmuTime::param time)
1144 auto& ymf278 =
OUTER(YMF278, debugRegisters);
1145 ymf278.writeReg(narrow<uint8_t>(address), value, time);
1151YMF278::DebugMemory::DebugMemory(MSXMotherBoard& motherBoard_,
1152 const std::string& name_)
1153 : SimpleDebuggable(motherBoard_, name_ +
" mem",
1154 "OPL4 memory (includes both ROM and RAM)", 0x400000)
1158uint8_t YMF278::DebugMemory::read(
unsigned address)
1160 const auto& ymf278 =
OUTER(YMF278, debugMemory);
1161 return ymf278.readMem(address);
1164void YMF278::DebugMemory::write(
unsigned address, uint8_t value)
1166 auto& ymf278 =
OUTER(YMF278, debugMemory);
1167 ymf278.writeMem(address, value);
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
void updateStream(EmuTime::param time)
void setSoftwareVolume(float volume, EmuTime::param time)
Change the 'software volume' of this sound device.
void unregisterSound()
Unregisters this sound device with the Mixer.
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
void write(size_t addr, byte value)
std::span< byte > getWriteBackdoor()
uint8_t readReg(uint8_t reg)
void writeReg(uint8_t reg, uint8_t data, EmuTime::param time)
void setMixLevel(uint8_t x, EmuTime::param time)
void reset(EmuTime::param time)
void writeMem(unsigned address, uint8_t value)
uint8_t readMem(unsigned address) const
void serialize(Archive &ar, unsigned version)
uint8_t peekReg(uint8_t reg) const
YMF278(const std::string &name, int ramSizeInKb, const DeviceConfig &config)
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
This file implemented 3 utility functions:
constexpr void fill(ForwardRange &&range, const T &value)
constexpr bool any_of(InputRange &&range, UnaryPredicate pred)
#define OUTER(type, member)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)