49static constexpr int MAX_ATT_INDEX = 0x280;
50static constexpr int MIN_ATT_INDEX = 0;
51static constexpr int TL_SHIFT = 2;
53static constexpr unsigned LFO_SHIFT = 18;
54static constexpr unsigned LFO_PERIOD = 1 << LFO_SHIFT;
57static constexpr int EG_ATT = 4;
58static constexpr int EG_DEC = 3;
59static constexpr int EG_SUS = 2;
60static constexpr int EG_REL = 1;
61static constexpr int EG_OFF = 0;
63static constexpr int EG_REV = 5;
64static constexpr int EG_DMP = 6;
67static constexpr std::array<uint8_t, 16> pan_left = {
68 0, 8, 16, 24, 32, 40, 48, 255, 255, 0, 0, 0, 0, 0, 0, 0
70static constexpr std::array<uint8_t, 16> pan_right = {
71 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 48, 40, 32, 24, 16, 8
76static constexpr int16_t SC(
int dB) {
return int16_t(dB / 3 * 0x20); }
77static constexpr std::array<int16_t, 16> dl_tab = {
78 SC( 0), SC( 3), SC( 6), SC( 9), SC(12), SC(15), SC(18), SC(21),
79 SC(24), SC(27), SC(30), SC(33), SC(36), SC(39), SC(42), SC(93)
82static constexpr uint8_t RATE_STEPS = 8;
83static constexpr std::array<uint8_t, 15 * RATE_STEPS> eg_inc = {
85 0, 1, 0, 1, 0, 1, 0, 1,
86 0, 1, 0, 1, 1, 1, 0, 1,
87 0, 1, 1, 1, 0, 1, 1, 1,
88 0, 1, 1, 1, 1, 1, 1, 1,
90 1, 1, 1, 1, 1, 1, 1, 1,
91 1, 1, 1, 2, 1, 1, 1, 2,
92 1, 2, 1, 2, 1, 2, 1, 2,
93 1, 2, 2, 2, 1, 2, 2, 2,
95 2, 2, 2, 2, 2, 2, 2, 2,
96 2, 2, 2, 4, 2, 2, 2, 4,
97 2, 4, 2, 4, 2, 4, 2, 4,
98 2, 4, 4, 4, 2, 4, 4, 4,
100 4, 4, 4, 4, 4, 4, 4, 4,
101 8, 8, 8, 8, 8, 8, 8, 8,
102 0, 0, 0, 0, 0, 0, 0, 0,
105[[nodiscard]]
static constexpr uint8_t O(
int a) {
return narrow<uint8_t>(a * RATE_STEPS); }
106static constexpr std::array<uint8_t, 64> eg_rate_select = {
107 O(14),O(14),O(14),O(14),
108 O( 0),O( 1),O( 2),O( 3),
109 O( 0),O( 1),O( 2),O( 3),
110 O( 0),O( 1),O( 2),O( 3),
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( 4),O( 5),O( 6),O( 7),
121 O( 8),O( 9),O(10),O(11),
122 O(12),O(12),O(12),O(12),
128static constexpr std::array<uint8_t, 64> eg_rate_shift = {
150[[nodiscard]]
static constexpr int L(
double a) {
return int((LFO_PERIOD * a) / 44100.0 + 0.5); }
151static constexpr std::array<int, 8> lfo_period = {
165static constexpr std::array<int16_t, 8> vib_depth = {
183static constexpr std::array<uint8_t, 8> am_depth = {
202[[nodiscard]]
static constexpr int8_t sign_extend_4(
int x)
204 return narrow<int8_t>((x ^ 8) - 8);
213[[nodiscard]]
static constexpr unsigned calcStep(int8_t oct, uint16_t fn, int16_t vib = 0)
215 if (oct == -8)
return 0;
216 unsigned t = (fn + 1024 + vib) << (8 + oct);
220void YMF278::Slot::reset()
222 wave = FN = TLdest = TL = pan = vib = AM = 0;
225 AR = D1R = D2R = RC = RR = 0;
226 PRVB = keyon = DAMP =
false;
228 step = calcStep(OCT, FN);
231 loopAddr = endAddr = 0;
232 env_vol = MAX_ATT_INDEX;
244uint8_t YMF278::Slot::compute_rate(
int val)
const
248 }
else if (val == 15) {
255 res += (FN & 0x200) ? 1 : 0;
257 return narrow<uint8_t>(
std::clamp(res, 0, 63));
260uint8_t YMF278::Slot::compute_decay_rate(
int val)
const
275 if (env_vol < dl_tab[4]) {
291 if (env_vol >= dl_tab[6]) {
295 return compute_rate(val);
298int16_t YMF278::Slot::compute_vib()
const
307 auto lfo_fm = narrow<int16_t>(lfo_cnt / (LFO_PERIOD / 0x40));
309 if (lfo_fm & 0x10) lfo_fm ^= 0x1F;
310 if (lfo_fm & 0x20) lfo_fm = narrow<int16_t>(-(lfo_fm & 0x0F));
312 return narrow<int16_t>((lfo_fm * vib_depth[vib]) / 12);
315uint16_t YMF278::Slot::compute_am()
const
321 auto lfo_am = narrow<uint16_t>(lfo_cnt / (LFO_PERIOD / 0x100));
323 if (lfo_am >= 0x80) lfo_am ^= 0xFF;
325 return narrow<uint16_t>((lfo_am * am_depth[AM]) >> 7);
329void YMF278::advance()
334 auto tl_int_cnt = eg_cnt % 9;
335 auto tl_int_step = (eg_cnt / 9) % 3;
337 for (
auto& op : slots) {
339 if (tl_int_cnt == 0) {
340 if (tl_int_step == 0) {
342 if (op.TL < op.TLdest) ++op.TL;
345 if (op.TL > op.TLdest) --op.TL;
350 op.lfo_cnt = (op.lfo_cnt + lfo_period[op.lfo]) & (LFO_PERIOD - 1);
356 uint8_t rate = op.compute_rate(op.AR);
363 uint8_t shift = eg_rate_shift[rate];
364 if (!(eg_cnt & ((1 << shift) - 1))) {
365 uint8_t select = eg_rate_select[rate];
367 op.env_vol = narrow<int16_t>(op.env_vol + ((~op.env_vol * eg_inc[select + ((eg_cnt >> shift) & 7)]) >> 4));
368 if (op.env_vol <= MIN_ATT_INDEX) {
369 op.env_vol = MIN_ATT_INDEX;
372 op.state = op.DL ? EG_DEC : EG_SUS;
378 uint8_t rate = op.compute_decay_rate(op.D1R);
379 uint8_t shift = eg_rate_shift[rate];
380 if (!(eg_cnt & ((1 << shift) - 1))) {
381 uint8_t select = eg_rate_select[rate];
382 op.env_vol = narrow<int16_t>(op.env_vol + eg_inc[select + ((eg_cnt >> shift) & 7)]);
383 if (op.env_vol >= op.DL) {
384 op.state = (op.env_vol < MAX_ATT_INDEX) ? EG_SUS : EG_OFF;
390 uint8_t rate = op.compute_decay_rate(op.D2R);
391 uint8_t shift = eg_rate_shift[rate];
392 if (!(eg_cnt & ((1 << shift) - 1))) {
393 uint8_t select = eg_rate_select[rate];
394 op.env_vol = narrow<int16_t>(op.env_vol + eg_inc[select + ((eg_cnt >> shift) & 7)]);
395 if (op.env_vol >= MAX_ATT_INDEX) {
396 op.env_vol = MAX_ATT_INDEX;
403 uint8_t rate = op.compute_decay_rate(op.RR);
404 uint8_t shift = eg_rate_shift[rate];
405 if (!(eg_cnt & ((1 << shift) - 1))) {
406 uint8_t select = eg_rate_select[rate];
407 op.env_vol = narrow<int16_t>(op.env_vol + eg_inc[select + ((eg_cnt >> shift) & 7)]);
408 if (op.env_vol >= MAX_ATT_INDEX) {
409 op.env_vol = MAX_ATT_INDEX;
425int16_t YMF278::getSample(Slot& slot, uint16_t pos)
const
433 return narrow_cast<int16_t>(
readMem(slot.startAddr + pos) << 8);
437 unsigned addr = slot.startAddr + ((pos / 2) * 3);
439 return narrow_cast<int16_t>(
443 return narrow_cast<int16_t>(
445 ((
readMem(addr + 1) << 4) & 0xF0));
450 unsigned addr = slot.startAddr + (pos * 2);
451 return narrow_cast<int16_t>(
461uint16_t YMF278::nextPos(Slot& slot, uint16_t pos, uint16_t increment)
467 if ((uint32_t(pos) + slot.endAddr) >= 0x10000)
468 pos += narrow_cast<uint16_t>(slot.endAddr + slot.loopAddr);
472bool YMF278::anyActive()
474 return ranges::any_of(slots, [](
auto& op) {
return op.state != EG_OFF; });
481static constexpr int vol_factor(
int x,
unsigned envVol)
483 if (envVol >= MAX_ATT_INDEX)
return 0;
484 int vol_mul = 0x80 - narrow<int>(envVol & 0x3F);
485 int vol_shift = 7 + narrow<int>(envVol >> 6);
486 return (x * ((0x8000 * vol_mul) >> vol_shift)) >> 15;
491 static constexpr std::array<float, 8> level = {
504void YMF278::generateChannels(std::span<float*> bufs,
unsigned num)
513 for (
auto j :
xrange(num)) {
514 for (
auto i :
xrange(24)) {
516 if (sl.state == EG_OFF) {
522 auto sample = narrow_cast<int16_t>(
523 (getSample(sl, sl.pos) * (0x10000 - sl.stepPtr) +
524 getSample(sl, nextPos(sl, sl.pos, 1)) * sl.stepPtr) >> 16);
531 auto envVol = narrow_cast<uint16_t>(
532 std::min(sl.env_vol + ((sl.lfo_active && sl.AM) ? sl.compute_am() : 0),
534 int smplOut = vol_factor(vol_factor(sample, envVol), sl.TL << TL_SHIFT);
539 int32_t volLeft = pan_left [sl.pan];
540 int32_t volRight = pan_right[sl.pan];
542 volLeft = (0x20 - (volLeft & 0x0f)) >> (volLeft >> 4);
543 volRight = (0x20 - (volRight & 0x0f)) >> (volRight >> 4);
545 bufs[i][2 * j + 0] += narrow_cast<float>((smplOut * volLeft ) >> 5);
546 bufs[i][2 * j + 1] += narrow_cast<float>((smplOut * volRight) >> 5);
548 unsigned step = (sl.lfo_active && sl.vib)
549 ? calcStep(sl.OCT, sl.FN, sl.compute_vib())
553 if (sl.stepPtr >= 0x10000) {
554 sl.pos = nextPos(sl, sl.pos, narrow<uint16_t>(sl.stepPtr >> 16));
555 sl.stepPtr &= 0xffff;
562void YMF278::keyOnHelper(YMF278::Slot& slot)
565 slot.env_vol = MAX_ATT_INDEX;
566 if (slot.compute_rate(slot.AR) < 63) {
571 slot.env_vol = MIN_ATT_INDEX;
573 slot.state = slot.DL ? EG_DEC : EG_SUS;
582 writeRegDirect(reg, data, time);
585void YMF278::writeRegDirect(uint8_t reg, uint8_t data, EmuTime::param time)
588 if (reg >= 0x08 && reg <= 0xF7) {
589 int sNum = (reg - 8) % 24;
590 auto& slot = slots[sNum];
591 switch ((reg - 8) / 24) {
593 slot.wave = (slot.wave & 0x100) | data;
594 int waveTblHdr = (regs[2] >> 2) & 0x7;
595 int base = (slot.wave < 384 || !waveTblHdr) ?
597 (waveTblHdr * 0x80000 + ((slot.wave - 384) * 12));
598 std::array<uint8_t, 12> buf;
599 for (
auto i :
xrange(12)) {
604 slot.bits = (buf[0] & 0xC0) >> 6;
605 slot.startAddr = buf[2] | (buf[1] << 8) | ((buf[0] & 0x3F) << 16);
606 slot.loopAddr = uint16_t(buf[4] | (buf[3] << 8));
607 slot.endAddr = uint16_t(buf[6] | (buf[5] << 8));
608 for (
auto i :
xrange(7, 12)) {
612 writeRegDirect(narrow<uint8_t>(8 + sNum + (i - 2) * 24), buf[i], time);
623 slot.wave = uint16_t((slot.wave & 0xFF) | ((data & 0x1) << 8));
624 slot.FN = (slot.FN & 0x380) | (data >> 1);
625 slot.step = calcStep(slot.OCT, slot.FN);
629 slot.FN = uint16_t((slot.FN & 0x07F) | ((data & 0x07) << 7));
630 slot.PRVB = (data & 0x08) != 0;
631 slot.OCT = sign_extend_4((data & 0xF0) >> 4);
632 slot.step = calcStep(slot.OCT, slot.FN);
636 uint8_t
t = data >> 1;
637 slot.TLdest = (
t != 0x7f) ?
t : 0xff;
640 slot.TL = slot.TLdest;
653 slot.pan = data & 0x0F;
658 slot.lfo_active =
false;
662 slot.lfo_active =
true;
665 slot.DAMP = (data & 0x40) != 0;
680 slot.lfo = (data >> 3) & 0x7;
681 slot.vib = data & 0x7;
685 slot.D1R = data & 0xF;
688 slot.DL = dl_tab[data >> 4];
689 slot.D2R = data & 0xF;
693 slot.RR = data & 0xF;
696 slot.AM = data & 0x7;
731 memAdr = (regs[3] << 16) | (regs[4] << 8) | data;
773 return (regs[2] & 0x1F) | 0x20;
788static constexpr unsigned INPUT_RATE = 44100;
790static size_t getRamSize(
int ramSizeInKb)
792 if ((ramSizeInKb != 0) &&
793 (ramSizeInKb != 128) &&
794 (ramSizeInKb != 256) &&
795 (ramSizeInKb != 512) &&
796 (ramSizeInKb != 640) &&
797 (ramSizeInKb != 1024) &&
798 (ramSizeInKb != 2048)) {
800 "Wrong sample ram size for MoonSound (YMF278). "
801 "Got ", ramSizeInKb,
", but must be one of "
802 "0, 128, 256, 512, 640, 1024 or 2048.");
804 return size_t(ramSizeInKb) * 1024;
810 24, INPUT_RATE, true)
811 , motherBoard(config.getMotherBoard())
812 , debugRegisters(motherBoard,
getName())
813 , debugMemory (motherBoard,
getName())
814 , rom(
getName() +
" ROM",
"rom", config)
815 , ram(config,
getName() +
" RAM",
"YMF278 sample RAM",
816 getRamSize(ramSizeInKb))
818 if (rom.
size() != 0x200000) {
820 "Wrong ROM for MoonSound (YMF278). The ROM (usually "
821 "called yrw801.rom) should have a size of exactly 2MB.");
847 for (
auto& op : slots) {
851 for (
int i = 0xf7; i >= 0; --i) {
852 writeRegDirect(narrow<uint8_t>(i), 0, time);
904unsigned YMF278::getRamAddress(
unsigned addr)
const
907 if (regs[2] & 2) [[unlikely]] {
910 if ((0x180000 <= addr) && (addr <= 0x1FFFFF)) {
912 switch (addr & 0x060000) {
917 if (ram.
size() == 256 * 1024) {
938 if (ram.
size() == 640 * 1024) {
943 if (addr > 0x080000) {
954 if (address < 0x200000) {
958 unsigned ramAddr = getRamAddress(address);
959 if (ramAddr < ram.
size()) {
971 if (address < 0x200000) {
974 unsigned ramAddr = getRamAddress(address);
975 if (ramAddr < ram.
size()) {
976 ram.
write(ramAddr, value);
1001template<
typename Archive>
1005 ar.serialize(
"startaddr", startAddr,
1006 "loopaddr", loopAddr,
1014 if (ar.versionAtLeast(version, 4)) {
1015 ar.serialize(
"endaddr", endAddr,
1018 unsigned e = 0; ar.serialize(
"endaddr",
e); endAddr = uint16_t((
e ^ 0xffff) + 1);
1021 if (ar.versionAtLeast(version, 2)) {
1022 ar.serialize(
"OCT", O);
1024 ar.serializeChar(
"OCT", O);
1026 OCT = sign_extend_4(O);
1029 if (ar.versionAtLeast(version, 2)) {
1030 ar.serialize(
"PRVB", PRVB,
1042 char PRVB_ = 0; ar.serializeChar(
"PRVB", PRVB_); PRVB = PRVB_;
1043 char TL_ = 0; ar.serializeChar(
"TL", TL_ ); TL = TL_;
1044 char pan_ = 0; ar.serializeChar(
"pan", pan_); pan = pan_;
1045 char vib_ = 0; ar.serializeChar(
"vib", vib_); vib = vib_;
1046 char AM_ = 0; ar.serializeChar(
"AM", AM_ ); AM = AM_;
1047 char AR_ = 0; ar.serializeChar(
"AR", AR_ ); AR = AR_;
1048 char D1R_ = 0; ar.serializeChar(
"D1R", D1R_); D1R = D1R_;
1049 char D2R_ = 0; ar.serializeChar(
"D2R", D2R_); D2R = D2R_;
1050 char RC_ = 0; ar.serializeChar(
"RC", RC_ ); RC = RC_;
1051 char RR_ = 0; ar.serializeChar(
"RR", RR_ ); RR = RR_;
1053 ar.serialize(
"bits", bits,
1054 "lfo_active", lfo_active);
1056 ar.serialize(
"state", state);
1057 if (ar.versionBelow(version, 4)) {
1058 assert(Archive::IS_LOADER);
1059 if (state ==
one_of(EG_REV, EG_DMP)) {
1065 if constexpr (Archive::IS_LOADER) {
1066 step = calcStep(OCT, FN);
1085template<
typename Archive>
1088 ar.serialize(
"slots", slots,
1090 if (ar.versionAtLeast(version, 4)) {
1091 ar.serialize(
"ram", ram);
1095 ar.serialize_blob(
"registers", regs);
1096 if (ar.versionAtLeast(version, 3)) {
1097 ar.serialize(
"memadr", memAdr);
1099 assert(Archive::IS_LOADER);
1104 memAdr = (regs[3] << 16) | (regs[4] << 8) | regs[5];
1108 if constexpr (Archive::IS_LOADER) {
1110 uint8_t
t = regs[0x50 + i] >> 1;
1111 sl.TLdest = (
t != 0x7f) ?
t : 0xff;
1113 sl.keyon = (regs[0x68 + i] & 0x80) != 0;
1114 sl.DAMP = (regs[0x68 + i] & 0x40) != 0;
1115 sl.lfo = (regs[0x80 + i] >> 3) & 7;
1124YMF278::DebugRegisters::DebugRegisters(
MSXMotherBoard& motherBoard_,
1125 const std::string& name_)
1127 "OPL4 registers", 0x100)
1131uint8_t YMF278::DebugRegisters::read(
unsigned address)
1134 return ymf278.peekReg(narrow<uint8_t>(address));
1137void YMF278::DebugRegisters::write(
unsigned address, uint8_t value, EmuTime::param time)
1140 ymf278.writeReg(narrow<uint8_t>(address), value, time);
1146YMF278::DebugMemory::DebugMemory(MSXMotherBoard& motherBoard_,
1147 const std::string& name_)
1148 : SimpleDebuggable(motherBoard_, name_ +
" mem",
1149 "OPL4 memory (includes both ROM and RAM)", 0x400000)
1153uint8_t YMF278::DebugMemory::read(
unsigned address)
1156 return ymf278.readMem(address);
1159void YMF278::DebugMemory::write(
unsigned address, uint8_t value)
1162 ymf278.writeMem(address, value);
EmuTime::param getCurrentTime()
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....
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
std::string getName(KeyCode keyCode)
Translate key code to key name.
This file implemented 3 utility functions:
void serialize(Archive &ar, T &t, unsigned version)
bool any_of(InputRange &&range, UnaryPredicate pred)
constexpr void fill(ForwardRange &&range, const T &value)
#define OUTER(type, member)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)